0

I'm searching for an easy way of adding hatches to my plots in matplotlib. I only want the hatches in specific bars and not for all my bars in the plot. I don't want to manually put settings for each bar.

0) This is a part of my df:

target  withCR_RF   withCR_ET   withCR_LG   withCR_Ridge
0   partA   0.102   0.100   -0.3    -0.3
1   partB   0.081   0.085   -0.3    -0.3
2   part    -0.063  -0.050  -0.3    -0.3

enter image description here

1) This is the code I'm using for the plot:

from matplotlib.colors import ListedColormap

cmap = ListedColormap(['#96C3EB', '#299438', '#B8B8B8', '#CCAC93'])
ax = df.plot.bar(x='target', colormap=cmap)

ax.set_xlabel(None)
ax.set_ylabel('prediction performance R²')
ax.set_title('comparing pipelines of prediction of executive function variables')
plt.xticks(rotation=45, ha='right')
plt.show()

2) Now, I'd like to implement the hatches in the same way as the colors. I don't want all of the bars hatched, but just the first one, so I try this, which doesn't work.

cmap = ListedColormap(['#96C3EB', '#299438', '#B8B8B8', '#CCAC93'])
hatches = ['++', '', '', '']
ax = df.plot.bar(x='target', colormap=cmap, hatch=hatches)

ax.set_xlabel(None)
ax.set_ylabel('prediction performance R²')
ax.set_title('comparing pipelines of prediction of executive function variables')
plt.xticks(rotation=45, ha='right')
plt.show()

I either get all bars hatched or none of them hatched. I don't want hatches for all the bars and I don't want to adapt every bar manually.

3) @Tranbi, thanks for your suggestion. This is now working but not for all light blue bars. There are six light blue bars with the condition withCR_RF (because of 6 targets) but only one bar of them has changed. How can I change all six of them?

cmap = ListedColormap(['#96C3EB', '#299438', '#B8B8B8', '#CCAC93'])
#hatches = ['++', '', '', '']
#hatches = ['', '++', '', '', '', '', 'xx', '', '', '']
ax = df.plot.bar(x='target', colormap=cmap, hatch=hatches)

ax.set_xlabel(None)
ax.set_ylabel('prediction performance R²')
ax.set_title('comparing pipelines of prediction of executive function variables')
plt.xticks(rotation=45, ha='right')


for lbl, patch in zip(ax.get_xticklabels() , ax.patches):
        if lbl.get_text() == 'withCR_RF':
            patch.set_hatch('*')
            patch.set_edgecolor(patch.get_facecolor())
            patch.set_facecolor('none')

plt.show()

plot

4) @Mozway, thanks for your answer. Your last option is perfect for my needs, but is it possible to index more than one bar? This doesn't work, unfortunately. I'd like to have all light blue bars hatched.

cmap = ListedColormap(['#96C3EB', '#299438', '#B8B8B8', '#CCAC93'])
hatches = ['++', '', '', '']
hatches = ['', '++', '', '', '', '', 'xx', '', '', '']
ax = df.plot.bar(x='target', colormap=cmap)
ax.patches[0,1,2,3].set_hatch('+') #see here please
ax.set_xlabel(None)
ax.set_ylabel('prediction performance R²')
ax.set_title('comparing pipelines of prediction of executive function variables')
plt.xticks(rotation=45, ha='right')
plt.show()
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
beginner
  • 1
  • 3
  • 1
    please provide a minimal example of `df` – mozway Apr 25 '23 at 08:13
  • 1
    You can loop over your patches and set the hatch for the desired one(s). See https://stackoverflow.com/questions/75756814/adding-hatch-to-seaborn-multi-boxplot/75757174#75757174 – Tranbi Apr 25 '23 at 08:16
  • please [edit] the question with this code – mozway Apr 25 '23 at 08:22
  • @beginner you shouldn't use `ax.get_xticklabels()` in `zip` but `df.columns`. (also `get_text()` isn't necessary in your case) – Tranbi Apr 25 '23 at 09:24
  • Can you clarify the question? you show pictures with 4 bars but your code produces 3x4 bars. Which bars exactly do you want to hatch? – mozway Apr 25 '23 at 09:36
  • Tranbi, thank you! I'm slowly getting there. This works now, but only for one of the light blue bars. I'll edit my Q @mozway – beginner Apr 25 '23 at 09:37

2 Answers2

0

What you want is unclear.

Given your data, you can either use:

hatches = ['++', '', '', '']
ax = df.plot.bar(x='target', colormap=cmap, hatch=hatches)

enter image description here

Or maybe:

hatches = ['++', '', '', '']
df.set_index('target').T.plot.bar(colormap=cmap, hatch=hatches)

enter image description here

Last option, if you want to manually change one patch:

cmap = ListedColormap(['#96C3EB', '#299438', '#B8B8B8', '#CCAC93'])
ax = df.plot.bar(x='target', colormap=cmap)
ax.patches[0].set_hatch('+')

enter image description here

mozway
  • 194,879
  • 13
  • 39
  • 75
  • thanks a lot @mozway! really help a lot and I appreciate the answer a lot! Would the last option also be possible with more than one patch? (all light blue bars?) What would the correct way of indexing be here? `ax.patches[0, 1, 2].set_hatch('+')` - doesn't work... – beginner Apr 25 '23 at 09:22
0

For me, the following code works best as I don't have to define each axes but can easily change and edit every bar I want to change. In my case, I can specify the conditions of the targets perfectly by setting the order. This code is still simple and I can adapt it easily to create a lot of similar plots.

fig, ax = plt.subplots()
ax = df.plot.bar(rot=0,ax=ax)
# get all bars in the plot
bars = ax.patches
patterns = ['/', 'o', '', '',]  # set hatch patterns in the correct order
hatches = []  # list for hatches in the order of the bars
for h in patterns:  # loop over patterns to create bar-ordered hatches
    for i in range(int(len(bars) / len(patterns))):
        hatches.append(h)
for bar, hatch in zip(bars, hatches):  # loop over bars and hatches to set hatches in correct order
    bar.set_hatch(hatch)
# generate legend. this is important to set explicitly, otherwise no hatches will be shown!
ax.legend()
plt.show()
Kyle F Hartzenberg
  • 2,567
  • 3
  • 6
  • 24
beginner
  • 1
  • 3