Good afternoon,
I am trying to plot this sample data in a way that grouping is maintained on the actual graph, but I couldn't find a way/method to get these labels added this way with matplotlib
(the provided example is done using Excel). Please refer to the first graph provided below and pay attention to the nicely nested grouping "London -> Switch 1, Switch3 -> Interface"
>>> data = {
'Location': ['NYC', 'NYC', 'NYC', 'NYC', 'LON', 'LON', 'LON', 'TKY', 'TKY', 'TKY'],
'Device': ['Device1', 'Device2', 'Device3', 'Device1', 'Switch1', 'Switch1', 'Switch3', 'Router1',
'Router2', 'Router2'],
'Interface': ['Et1', 'Et2', 'Et3', 'Et2', 'Et1', 'Et3', 'Et22', 'Gi1', 'Gi1', 'Gi2'],
'Errors': [69, 58, 16, 83, 58, 78, 16, 30, 33, 64]}
>>> df = pd.DataFrame(data)
I would like to keep this structure when plotting with matplotlib
too, but all my attempts were not very successful so far ...
Is there an easy way to achieve this?
And here is what the matplotlib
graph looks like:
SOLUTION
I really wanted to post this solution so it can stay here for completeness. I had to add on to the code I found within the duplicated thread
and here it is.
from matplotlib import pyplot as plt
from collections import Counter
def add_line(ax, xpos, ypos):
line = plt.Line2D([xpos, xpos], [ypos + .1, ypos],
transform=ax.transAxes, color='gray')
line.set_clip_on(False)
ax.add_line(line)
def my_label(my_index, level):
labels = my_index.get_level_values(level)
if level == df.index.nlevels-1:
return [(x, 1) for x in labels]
else:
x = Counter()
x.update(labels)
return x.items()
def label_group_bar_table(ax,df):
ypos = -.1
scale = 1./df.index.size
for level in range(df.index.nlevels)[::-1]:
pos = 0
for label, rpos in my_label(df.index, level):
lxpos = (pos + .5 * rpos) * scale
ax.text(lxpos, ypos, label, ha='center',
transform=ax.transAxes, size=12, color='gray')
add_line(ax, pos*scale, ypos)
pos += rpos
add_line(ax, pos*scale, ypos)
ypos -= .05
def autonotate(df):
for num, entry in enumerate(df.reset_index().iterrows()):
num, row = entry
location, device, interface, errors = row
ax.text(num, errors*1.02, errors, ha='center', size=12, color='black')
>>> df = rdf.groupby(['Location','Device','Interface']).sum()
>>> fig = plt.figure()
>>> ax = fig.add_subplot()
>>> df.plot(kind='bar', stacked=False, ax=fig.gca(), figsize=(20,7), color='orange')
>>> labels = ['' for item in ax.get_xticklabels()]
>>> ax.set_xticklabels(labels)
>>> ax.set_xlabel('')
>>> label_group_bar_table(ax, df)
>>> fig.subplots_adjust(bottom=.1*df.index.nlevels)
>>> autonotate(df)
>>> plt.tight_layout()
>>> plt.show()
This way I was able to produce the following diagram, which is pretty close to what I needed: