Home > front end >  Custom multi-colored horizontal bar chart matplotlib
Custom multi-colored horizontal bar chart matplotlib

Time:09-17

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.

enter image description here

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:

striped picture

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()

multi-colored hatched bars

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('//')
  • Related