2

I have a code example using matplotlib.pyplot.plot() that works and that that I want to replicate to make hatched bar segments on a stacked bar chart. However, I have been making all of my other graphics using pandas.DataFrame.plot() instead of matplotlib.pyplot.plot() and would like to continue this here as well. The example code returns a tuple of BarContainer objects and pandas.DataFrame.plot() returns an AxisSubplot obect, and I don't know how to navigate between the two.

Any suggestions for how to get BarContainer objects out of pandas.DataFrame.plot() so that I can use those to replicate the example?

If not, any suggestions for how to achieve my aim of adding hatching per colored bar segment on a stacked bar chart using pandas.DataFrame.plot()?

With my data, the hatching will be useful to help differentiate between similar colors since I have both a lot of categories and items and the results otherwise can look visually similar. (I know I could also find a way to plot simpler things, but doing this helpful for my exploratory data analysis.) Thank you!


Example working code to hatch each colored bar segment of a stacked bar chart (from here: https://matplotlib.org/examples/pylab_examples/hatch_demo.html ):

import pandas as pd
import numpy as np  
import matplotlib.pyplot as plt

fig = plt.figure()
ax2 = fig.add_subplot(111)
bars = ax2.bar(range(1, 5), range(1, 5), color='yellow', ecolor='black') + \
    ax2.bar(range(1, 5), [6] * 4, bottom=range(1, 5), color='green', ecolor='black')

patterns = ('-', '+', 'x', '\\', '*', 'o', 'O', '.')
for bar, pattern in zip(bars, patterns):
    bar.set_hatch(pattern)

enter image description here


My code (with simplified data) that I wish had hatches per colored bar segments:

df = pd.DataFrame(np.random.uniform(0,10, size=(5,26)))
df.columns=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
ax = df.plot.barh(stacked=True, width=0.98, figsize=(10,5), cmap='gist_ncar')

enter image description here

  • Like 26 unique patches for each color? (Are there even that many?) – ALollz Jan 03 '20 at 22:30
  • @ ALollz - No, even just a couple patches would work fine. The purpose is to better distinguish categories with similar colors, e.g., to differently hatch light green vs. middle green vs. dark green, etc. For that, it would be fine to reuse the same patterns for different colors, just not for neighboring colors. – thanksfolks22 Jan 03 '20 at 23:35

1 Answers1

6

how to get BarContainer objects out of pandas.DataFrame.plot()

Filter them out of the Axes' containers attribute

>>> import matplotlib as mpl
>>> ax = df.plot.barh(stacked=True, width=0.98, figsize=(10,5), cmap='gist_ncar')
>>> ax.containers[:4]
[<BarContainer object of 5 artists>, <BarContainer object of 5 artists>, <BarContainer object of 5 artists>, <BarContainer object of 5 artists>]
>>> bars = [thing for thing in ax.containers if isinstance(thing,mpl.container.BarContainer)]

Set the hatches on the Rectangles in each BarContainer.

import itertools
patterns = itertools.cycle(('-', '+', 'x', '\\', '*', 'o', 'O', '.'))
for bar in bars:
    for patch in bar:
        patch.set_hatch(next(patterns))
L = ax.legend()    # so hatches will show up in the legend

Doing it this way will ensure you don't grab a Patch that isn't part of a bar.


how to get the same patches to show up on the same colors across different items

Maybe while iterating over the patches in each bar, inspect its facecolor (patch.get_facecolor) and maintain a dictionary of {facecolor:hatchsymbol} - if the facecolor is in the dictionary set that hatch otherwise get a new hatch symbol, add it and the color to the dictionary the set the hatch.

d = {}
for bar in bars:
    for patch in bar:
        pat = d.setdefault(patch.get_facecolor(), next(patterns))
        patch.set_hatch(pat)
L = ax.legend()    

Matplotlib Tutorials are worth the effort.

wwii
  • 23,232
  • 7
  • 37
  • 77