I want to draw different random hatches (from a predefined set) for each broken bar.
Like this:
Below is the full code example:
import matplotlib.pyplot as plt
import pandas as pd
import random
result = pd.DataFrame([['Bill', 1972, 1974],
['Bill', 1976, 1978],
['Bill', 1967, 1971],
['Danny', 1969, 1975],
['Danny', 1976, 1977],
['James', 1971, 1972],
['Marshall', 1967, 1975]],
columns=['Person', 'Year_start', 'Year_left']).\
sort_values(['Year_start', 'Year_left'], ascending=[True, True]).\
reset_index(drop=True)
fig, ax = plt.subplots()
height = 0.5
names = []
colors = ['tab:blue', 'tab:green', 'tab:red', 'yellow', 'brown', 'black', 'tab:orange', 'aquamarine']
hatches = ['/', 'o', '+', '-', '*', 'O', 'x', 'X', '|']
bc = result.groupby('Person')['Person'].count().max()
# this is broken barh's max count in this dataframe that will be used further in loop (in order to avoid using constants)
print(bc)
for y, (name, g) in enumerate(result.groupby('Person', sort=False)):
ax.broken_barh(list(zip(g['Year_start'],
g['Year_left'] - g['Year_start'])),
(y - height / 2, height),
facecolors=random.sample(colors, k=bc),
hatch=random.sample(hatches, k=bc)
)
names.append(name)
ax.set_ylim(0 - height, len(names) - 1 + height)
ax.set_xlim(result['Year_start'].min() - 1, result['Year_left'].max() + 1)
ax.set_yticks(range(len(names)), names)
ax.set_yticklabels(names)
print(result)
ax.grid(True)
plt.show()
While it perfectly works with the colors:
facecolors=random.sample(colors, k=bc)
it does not work with the hatches in the same loop:
hatch=random.sample(hatches, k=bc)
hatches are not displayed (image below), which is very strange. Is this something that is not supported for hatches in particular, or did I made a mistake? Many thanks!
While it perfectly works with the
colors
, it does not work with thehatches
in the same loop.. Is this something that is not supported for hatches in particular ?
I think the answer is in the docstring of set_color
:
Set the facecolor(s) of the collection. c can be a color (all patches have same color), or a sequence of colors; if it is a sequence the patches will cycle through the sequence.
Source : [code]
It means that Matplotlib can handle different face colors for each individual bar by cycling through the provided sequence of colors but it is not the same for the hatches. So, for the hatches, you need to use random.choice(hatches)
instead of random.sample(hatches, k=bc)
because the former returns a single value while the latter returns a sequence/list.
Output :
That being said, if you wanna assign a different fcolor/hatch of every piece of bar, you can sample both fcolors/hatches in //
this way (well, one of the ways):
names = []
for y, (name, g) in enumerate(result.groupby('Person', sort=False)):
for (start, width), facecolor, hatch in zip(
zip(g['Year_start'], g['Year_left'] - g['Year_start']),
random.sample(colors, k=bc),
random.sample(hatches, k=bc)
):
ax.broken_barh(
[(start, width)], (y - height / 2, height),
facecolors=facecolor, hatch=hatch)
names.append(name)
Output :