I want to create a horizontal bar chart where every bar has a custom striped color scheme (similar to the figure below). I am familiar with creating a normal barhplot, but I don't know how a two-color scheme would work.
Tried code:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.DataFrame({"P1_P2": [0.20],
"P1_P3": [0.16],
"P2_P5": [0.12],
"P3_P5": [0.06],
"P3_P6": [0.06],
"P5_P6": [0.06]})
df=df.T
fig, ax = plt.subplots(dpi=600, figsize=(4, 4), nrows=1, ncols=1, facecolor=None, edgecolor='black')
df.plot.barh(ax=ax, position=0.50, width=0.3, color=(245/255, 153/255, 145/255, 1.0))
ax.get_legend().remove()
plt.show()
CodePudding user response:
This can be achieved by using the facecolor
and edgecolor
arguments within the barh
. I see that you have used the following arguments in the subplots
. The plot is recreated and produced with the solution below:
%matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.DataFrame({"P1_P2": [0.20],
"P1_P3": [0.16],
"P2_P5": [0.12],
"P3_P5": [0.06],
"P3_P6": [0.06],
"P5_P6": [0.06]})
df=df.T
fig, ax = plt.subplots(dpi=600, figsize=(4, 4), nrows=1, ncols=1)
df.plot.barh(ax=ax, position=0.50, width=0.3, facecolor="red",
edgecolor="blue")
ax.get_legend().remove()
plt.show()
To have the stripes in between the colors, the argument hatch
can be used as mentioned in the following code:
%matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.DataFrame({"P1_P2": [0.20],
"P1_P3": [0.16],
"P2_P5": [0.12],
"P3_P5": [0.06],
"P3_P6": [0.06],
"P5_P6": [0.06]})
df=df.T
fig, ax = plt.subplots(dpi=600, figsize=(4, 4), nrows=1, ncols=1)
df.plot.barh(ax=ax, position=0.50, width=0.3, facecolor="red",
edgecolor="blue", hatch=r'//')
ax.get_legend().remove()
plt.show()
The plot would look like this:
In case you want to increase or decrease the line width, this can be altered using plt.rcParams["hatch.linewidth"]
plt.rcParams["hatch.linewidth"]= 4
CodePudding user response:
You can hatch each of the bars with different colors. The width of the lines needs to be adapted depending on the particular plot.
As the same color is used for hatching as for the outlines, a copy of the bar can be created which only shows an outline.
import matplotlib.pyplot as plt
from matplotlib import rcParams
import pandas as pd
from copy import copy
df = pd.DataFrame([0.2 , 0.16, 0.12, 0.06, 0.06, 0.06], index=['P1_P2', 'P1_P3', 'P2_P5', 'P3_P5', 'P3_P6', 'P5_P6'])
first_colors = ['dodgerblue', 'orange', 'green','silver', 'silver', 'dodgerblue']
second_colors = ['crimson', 'crimson', 'dodgerblue', 'dodgerblue', 'green', 'gold']
fig, ax = plt.subplots(dpi=600, figsize=(4, 4), nrows=1, ncols=1, facecolor=None, edgecolor='black')
df.plot.barh(ax=ax, position=0.50, width=0.3, legend=False, linewidth=0)
rcParams['hatch.linewidth'] = 4
for bar, main_color, hatch_color in zip(ax.containers[0], first_colors, second_colors):
rect = copy(bar)
rect.set_facecolor('none')
rect.set_edgecolor('black')
rect.set_linewidth(2)
ax.add_patch(rect)
bar.set_facecolor(main_color)
bar.set_edgecolor(hatch_color)
bar.set_hatch('//')
ax.invert_yaxis()
plt.tight_layout()
plt.show()
PS: A simpler version to get the outlines could be to draw the outline-version of the bars twice, and afterwards update the color and hatching of the bars that were drawn first (the first bars are stored in ax.containers[0]
).
for _ in range(2):
df.plot.barh(ax=ax, position=0.50, width=0.3, legend=False, fc='none', ec='black', lw=2)
rcParams['hatch.linewidth'] = 4
for bar, main_color, hatch_color in zip(ax.containers[0], first_colors, second_colors):
bar.set_linewidth(0)
bar.set_facecolor(main_color)
bar.set_edgecolor(hatch_color)
bar.set_hatch('//')