I want to change the color of a bar in matplotlib's grouped barplot if it meets a certain condition. I'm plotting two bars for each species
- one for today
and one for avg
, where avg
contains yerr
errorbars that show the 10th and 90th percentile values.
Now I want the avg
bar to be green if today
's length
value > 10th percentile, and red if today
's length
value < 10th percentile.
I tried the solutions in these posts
- how to change the color of a single bar if condition is True matplotlib
- Update Single Bar in Matplotlib
but the bars are always green.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
z = pd.DataFrame(data={'length': [40,35,34,40,36,39,38,44,40,39,35,46],
'species': ['A','A','A','A','B','B','B','B','C','C','C','C'],
'type': ['today','avg','10pc','90pc','today','avg','10pc','90pc','today','avg','10pc','90pc']
},
)
z['Date'] = pd.to_datetime('2021-09-20')
z.set_index('Date',inplace=True)
z0 = z.loc[(z.type=='today') | (z.type=='avg')] # average length and today's length
z1 = z.loc[(z.type=='10pc') | (z.type=='90pc')] # 10th and 90th percentile
z2 = []
for n in z.species.unique().tolist():
dz = z.loc[(z.species==n) & (z.type=='today'),'length'].values[0] - z.loc[(z.species==n) & (z.type=='10pc'),'length'].values[0]
if dw>0:
z2.append(1)
else:
z2.append(0)
errors = z1.pivot_table(columns=[z1.index,'species'],index='type',values=['length']).values
avgs = z0.length[z0.type=='avg'].values
bars = np.stack((np.absolute(errors-avgs), np.zeros([2,z1.species.unique().size])), axis=0)
col = ['pink']
for k in z2:
if k==1:
col.append('g') # length within 10% bounds = green
else:
col.append('r') # length outside 10% bounds = red
fig, ax = plt.subplots()
z0.pivot(index='species', columns='type', values='length').plot(kind='bar', yerr=bars, ax=ax, color=col, capsize=0)
ax.set_title(z0.index[0].strftime('%d %b %Y'), fontsize=16)
ax.set_xlabel('species', fontsize=14)
ax.set_ylabel('length (cm)', fontsize=14)
plt.show()