更改 matplotlib 条形图中特定条形的颜色
Change color of specific bar in matplotlib barplot
如果满足特定条件,我想更改 matplotlib 分组条形图中条形的颜色。我正在为每个 species
绘制两个条 - 一个用于 today
,一个用于 avg
,其中 avg
包含显示第 10 个和第 90 个百分位数的 yerr
个误差条值。
现在,如果 today
的 length
值 > 第 10 个百分位数,我希望 avg
条为绿色,如果 today
的 length
条为红色] 值 < 第 10 个百分位数。
我尝试了这些帖子中的解决方案
- how to change the color of a single bar if condition is True matplotlib
但条形总是绿色的。
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()
一种方法是在创建绘图后覆盖颜色。首先,您需要将初始化 col
的行更改为
col = ['pink']*z['species'].nunique()
获取平均柱数,然后使用相同的 for 循环根据您的情况添加 g 或 r。最后,改变这个
fig, ax = plt.subplots()
z0.pivot(index='species', columns='type', values='length')\
.plot(kind='bar', yerr=bars, ax=ax,
color=['pink','g'], capsize=0) # here use only pink and g
# here overwrite the colors
for p, c in zip(ax.patches, col):
p.set_color(c)
ax.set_title...
请注意,即使您有红色条,今天的图例也是绿色的,这可能会造成混淆。
这是完整的工作示例,感谢 this answer
在图例中添加了红色条目
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches # import this extra
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 dz>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']*z['species'].nunique()
for k in z2:
if k==1:
col.append('g') # length within 10% bounds = green
else:
col.append('r') # length outside 10% bounds = red
print(col)
# ['pink', 'pink', 'pink', 'g', 'r', 'g']
fig, ax = plt.subplots()
z0.pivot(index='species', columns='type', values='length').plot(kind='bar', yerr=bars, ax=ax, color=['pink','g'], capsize=0)
for p, c in zip(ax.patches, col):
p.set_color(c)
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)
handles = None
labels = None
if 0 in z2: ## add the entry on the legend only when there is red bar
# where some data has already been plotted to ax
handles, labels = ax.get_legend_handles_labels()
# manually define a new patch
patch = mpatches.Patch(color='r', label='today')
# handles is a list, so append manual patch
handles.append(patch)
# plot the legend
plt.legend(handles=handles)
else:
# plot the legend when there isn't red bar
plt.legend(handles=handles)
plt.show()
我得到了红条
如果满足特定条件,我想更改 matplotlib 分组条形图中条形的颜色。我正在为每个 species
绘制两个条 - 一个用于 today
,一个用于 avg
,其中 avg
包含显示第 10 个和第 90 个百分位数的 yerr
个误差条值。
现在,如果 today
的 length
值 > 第 10 个百分位数,我希望 avg
条为绿色,如果 today
的 length
条为红色] 值 < 第 10 个百分位数。
我尝试了这些帖子中的解决方案
- how to change the color of a single bar if condition is True matplotlib
但条形总是绿色的。
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()
一种方法是在创建绘图后覆盖颜色。首先,您需要将初始化 col
的行更改为
col = ['pink']*z['species'].nunique()
获取平均柱数,然后使用相同的 for 循环根据您的情况添加 g 或 r。最后,改变这个
fig, ax = plt.subplots()
z0.pivot(index='species', columns='type', values='length')\
.plot(kind='bar', yerr=bars, ax=ax,
color=['pink','g'], capsize=0) # here use only pink and g
# here overwrite the colors
for p, c in zip(ax.patches, col):
p.set_color(c)
ax.set_title...
请注意,即使您有红色条,今天的图例也是绿色的,这可能会造成混淆。
这是完整的工作示例,感谢 this answer
在图例中添加了红色条目import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches # import this extra
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 dz>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']*z['species'].nunique()
for k in z2:
if k==1:
col.append('g') # length within 10% bounds = green
else:
col.append('r') # length outside 10% bounds = red
print(col)
# ['pink', 'pink', 'pink', 'g', 'r', 'g']
fig, ax = plt.subplots()
z0.pivot(index='species', columns='type', values='length').plot(kind='bar', yerr=bars, ax=ax, color=['pink','g'], capsize=0)
for p, c in zip(ax.patches, col):
p.set_color(c)
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)
handles = None
labels = None
if 0 in z2: ## add the entry on the legend only when there is red bar
# where some data has already been plotted to ax
handles, labels = ax.get_legend_handles_labels()
# manually define a new patch
patch = mpatches.Patch(color='r', label='today')
# handles is a list, so append manual patch
handles.append(patch)
# plot the legend
plt.legend(handles=handles)
else:
# plot the legend when there isn't red bar
plt.legend(handles=handles)
plt.show()
我得到了红条