Code which I found
Using the answer cited in the OP
for i, (gr, color) in enumerate(zip(subx, colors)):
allows for assigning a color from the list to each group.
def grouped_barplot(df, cat,subcat, val , err):
u = df[cat].unique()
x = np.arange(len(u))
subx = df[subcat].unique()
offsets = (np.arange(len(subx))-np.arange(len(subx)).mean())/(len(subx) 1.)
width= np.diff(offsets).mean()
colors = ['black', 'red', 'green', 'blue', 'cyan','brown','grey','goldenrod','lime','violet','indigo','coral','olive']
# add the colors to the loop
for i, (gr, color) in enumerate(zip(subx, colors)):
dfg = df[df[subcat].eq(gr)]
plt.bar(x offsets[i], dfg[val], width=width, yerr=dfg[err], color=color, label=gr)
plt.legend(title='Sample_Set', bbox_to_anchor=(1, 1.02), loc='upper left')
plt.yscale('log')
plt.xlabel(cat)
plt.ylabel(val)
plt.xticks(x, u)
plt.show()
cat = "Candidate"
subcat = "Sample_Set"
val = "Values"
err = "Error"
grouped_barplot(df, cat, subcat, val, err )
Original Answer
- The request can be implemented, but should not be:
- This is not a good visualization practice. The bars within a given group should all have the same color. The point of a visualization is to convey information. All this will do is make the plot confusing to read, thereby defeating the purpose.
- Because the the bars are plotted in groups:
'X'
, 'Y'
, 'Z'
of 'Sample_Set 1'
, 'X'
, 'Y'
, 'Z'
of 'Sample_Set 2'
and 'X'
, 'Y'
, 'Z'
of 'Sample_Set 3'
, only 3 labels will be created in the legend, which means a custom patch legend with appropriate handles and labels will need to be created.
Plot and Customize
- The order of
rects
is not the same as df
, so df
is sorted differently in order to zip
the correct color to the correct rect
# add a colors column to the dataframe
df['color'] = colors[:len(df)]
# plot vals with yerr
ax = vals.plot(kind='bar', yerr=yerr, logy=True, rot=0, figsize=(6, 5), legend=False, ylabel='Value', title='Comparison')
# extract the Rectangle bar objects
rects = [c for c in ax.get_children() if isinstance(c, mpl.patches.Rectangle)]
# change the face color of the bar
for rect, color in zip(rects, df.sort_values(['Sample_Set', 'Candidate'])['color']):
rect.set_fc(color)
# create a custom handle for the legend
handles = list()
for idx, v in df.iterrows():
patch = Patch(color=v.color, label=f'{v.Candidate} {v.Sample_Set}')
handles.append(patch)
# add the legend
ax.legend(title='Candidate Sample_Set', handles=handles, bbox_to_anchor=(1, 1.02), loc='upper left')
plt.show()
Using the