22

I am trying to achieve differentiation by hatch pattern instead of by (just) colour. How do I do it using pandas?

It's possible in matplotlib, by passing the hatch optional argument as discussed here. I know I can also pass that option to a pandas plot, but I don't know how to tell it to use a different hatch pattern for each DataFrame column.

df = pd.DataFrame(rand(10, 4), columns=['a', 'b', 'c', 'd'])
df.plot(kind='bar', hatch='/');

enter image description here

For colours, there is the colormap option described here. Is there something similar for hatching? Or can I maybe set it manually by modifying the Axes object returned by plot?

Community
  • 1
  • 1
metakermit
  • 21,267
  • 15
  • 86
  • 95

3 Answers3

28

This is kind of hacky but it works:

df = pd.DataFrame(np.random.rand(10, 4), columns=['a', 'b', 'c', 'd'])
ax = plt.figure(figsize=(10, 6)).add_subplot(111)
df.plot(ax=ax, kind='bar', legend=False)

bars = ax.patches
hatches = ''.join(h*len(df) for h in 'x/O.')

for bar, hatch in zip(bars, hatches):
    bar.set_hatch(hatch)

ax.legend(loc='center right', bbox_to_anchor=(1, 1), ncol=4)

bar

metakermit
  • 21,267
  • 15
  • 86
  • 95
behzad.nouri
  • 74,723
  • 18
  • 126
  • 124
  • 1
    Excellent, just the acceptable kind of hacky :) Now we just need to submit a patch like this to pandas.plot as `hatchmap=True` or something... – metakermit Apr 03 '14 at 13:06
  • 3
    Rather than filtering `ax.get_children()`, it might be enough to simply access the bars as `ax.patches`. – saffsd Jun 20 '14 at 06:24
  • 1
    Brilliant. I adapted this approach to a df.plot.barh I have and it works perfectly. I have 8 bar segments and hatched every other bar with `' / / / /'` (there are spaces for no pattern). +1 for the question and the accepted answer. – DaveL17 Jan 13 '17 at 19:31
  • How would this look for stacked area plots? – MERose Jul 19 '19 at 15:17
  • Better to do `hatches = [h for h in patterns for _ in range(len(df))]` because all hatches patterns don't have the same length (e.g. "xxx") – mitsi Oct 15 '20 at 14:44
6

This code allows you a little more freedom when defining the patterns, so you can have '//', etc.

bars = ax.patches
patterns =('-', '+', 'x','/','//','O','o','\\','\\\\')
hatches = [p for p in patterns for i in range(len(df))]
for bar, hatch in zip(bars, hatches):
    bar.set_hatch(hatch)
Leonardo
  • 1,533
  • 17
  • 28
0

Another way to do this is plotting each column individually:

df = pd.DataFrame(np.random.randint(3, 10, size=(5, 4)), columns=['a', 'b', 'c', 'd'])
hatches = list('-*/o')
fig, ax = plt.subplots()
for i, c in enumerate(df.columns):
    ax.bar(x=df.index - 0.3 + 0.2*i, height=df[c], width=0.2, hatch=hatches[i], label=c)
ax.legend()
plt.show()

Result

Alex Bochkarev
  • 2,851
  • 1
  • 18
  • 32