Maybe a bit odd and random question but still.
A little while back I have seen the below visual in one webinar, which visualizes the org strcture of an organization (who reports to whom etc):
As you see it basically shows that CEO is at the top of organization, Manufacturing director, Finance Director and Operations director are reporting to CEO etc. Each box length shows how many total children each node has (so for CEO it is all people in org, for Manufacturing director it is all people who report to him directly or indirectly etc). The boxes are color coded based on location where the position is located (so like red - USA, Blue - APAC etc)
Now, I love the visual itself, it is very nicely communicates in condensed way the whole org chart.
I want to replicate this chart using my own dataset, but I am not sure even where to start since I have no idea how such chart type is even called. I tried to google it or look through different chart libraries online, but haven't found anything similar.
Hence I have two questions:
To illustrate, my dummy dataset is as follows:
Any advice will be much appreciated.
Given the x, y position, width and height of a manager rectangle, it is pretty straightforward to compute the positions and shapes of the people directly managed by that person. Since the the "Reporting to" relation defines a directed acyclic graph, we can start at the root and recursively traverse the graph to compute the position and shapes of all rectangles. This ensures that we always compute the rectangle for a manager before computing the rectangle for any of the people managed by that person.
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import networkx as nx
def get_layout_recursively(G, predecessor, layout, base_width, base_height):
x, y, _, _ = layout[predecessor]
y -= base_height
for node in G.successors(predecessor):
width = get_total_children(G, node) * base_width
layout[node] = x, y, width, base_height
x += width
layout = get_layout_recursively(G, node, layout, base_width, base_height)
return layout
def get_total_children(G, node):
return len(nx.nodes(nx.dfs_tree(G, node)))
if __name__ == '__main__':
data = dict(
ID = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
predecessor = [pd.NA, 1, 1, 2, 2, 4, 4, 4, 3, 3],
label = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
color = ['tab:blue', 'tab:orange', 'tab:green', 'tab:orange', 'tab:orange', 'tab:pink', 'tab:pink', 'tab:pink', 'tab:green', 'tab:green'],
)
data = pd.DataFrame(data)
data.set_index('ID', inplace=True)
# create graph
G = nx.DiGraph([(predecessor, successor) for (successor, predecessor) in data['predecessor'].iteritems() if not predecessor is pd.NA])
# compute layout recursively starting with the root
base_width = 2
base_height = 10
root = next(nx.topological_sort(G))
layout = {root : (0, 0, get_total_children(G, root) * base_width, base_height)}
layout = get_layout_recursively(G, root, layout, base_width, base_height)
# plot
fig, ax = plt.subplots()
for node in data.index:
x, y, width, height = layout[node]
color = data['color'].loc[node]
label = data['label'].loc[node]
ax.add_patch(plt.Rectangle((x, y), width, height, facecolor=color, edgecolor='white'))
ax.text(x + 0.5*width, y + 0.5 * height, label, ha='center', va='center', color='white')
# rescale the axis such that it encompasses all rectangles
ax.autoscale_view()
# remove axes spines, ticks, labels
ax.axis('off')
plt.show()