Put a bar as background of bars in histogram plot

Question:

I have a histogram plot and different bars has different color. I want to show a background (the same width as the bar width) and also change the bar from rectangular to oval. I attached an that show the way I want.

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
rating = [9, 5, 7, 6]
objects = ('h', 'b', 'c', 'a')
y_pos = np.arange(len(objects))
cmap = plt.get_cmap('cool')
norm = plt.Normalize(vmin=min(rating), vmax=max(rating))
plt.barh(y_pos, rating, align='center', color=cmap(norm(np.array(rating))))
plt.show()

The maximum length of the bars is 10. In the following image, we can see a bar with two colors (blue and black background). Can you please help me with that?

enter image description here

Asked By: Sadcow

||

Answers:

You can use FancyBboxPatch and seaborn:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from matplotlib.patches import FancyBboxPatch


mydict = {
    'Index': ['3', '2', '1', '0'],
    'rating': [9, 5, 7, 6],
    'max_rating': [10, 10, 10, 10]}

df = pd.DataFrame(mydict).set_index('Index')
cmap = plt.get_cmap('cool')
norm = plt.Normalize(vmin=min(df.rating), vmax=max(df.rating))




plt.subplots(figsize=(5, 2))
ax = sns.barplot(x=df.max_rating, y=df.index, color='black')
ax2 = sns.barplot(x=df.rating, y=df.index, palette="pastel")
ax2.set_xlim(-1, 11)

new_patches_1 = []
new_patches_2 = []
for idx in range(len(df.index)-1, -1, -1):
    patch_1 = ax.patches[idx]
    bb_1 = patch_1.get_bbox()
    color = patch_1.get_facecolor()
    p_bbox_1 = FancyBboxPatch((bb_1.xmin, bb_1.ymin),
                            abs(bb_1.width), abs(bb_1.height),
                            boxstyle="round,pad=0.08,rounding_size=0.8",
                            ec="none", fc=color,
                            mutation_aspect=0.5
                            )
    patch_1.remove()
    new_patches_1.append(p_bbox_1)
    
    patch_2 = ax2.patches[idx]
    bb_2 = patch_2.get_bbox()
    color = patch_2.get_facecolor()
    p_bbox_2 = FancyBboxPatch((bb_2.xmin, bb_2.ymin),
                            df.rating[idx], abs(bb_2.height),
                            boxstyle="round,pad=0.08,rounding_size=0.8",
                            ec="none", fc=color,
                            mutation_aspect=0.5
                            
                            )
    patch_2.remove()
    new_patches_2.append(p_bbox_2)

for patch_1 in new_patches_1:
    ax.add_patch(patch_1)
    
for patch_2 in new_patches_2:
    ax2.add_patch(patch_2)

sns.despine(left=True, bottom=True)

Then you get the figure:

enter image description here

Update :

Since you want to change the space between each bar, I add a variable to adjust the height :

The code in the first loop change to :

new_patches_1 = []
new_patches_2 = []
height = 0.6
for idx in range(len(df.index)-1, -1, -1):
    patch_1 = ax.patches[idx]
    bb_1 = patch_1.get_bbox()
    color = patch_1.get_facecolor()
    p_bbox_1 = FancyBboxPatch((bb_1.xmin, bb_1.ymin),
                            abs(bb_1.width), height,
                            boxstyle="round,pad=0.08,rounding_size=0.8",
                            ec="none", fc=color,
                            mutation_aspect=height*0.625
                            )
    patch_1.remove()
    new_patches_1.append(p_bbox_1)
    
    patch_2 = ax2.patches[idx]
    bb_2 = patch_2.get_bbox()
    color = patch_2.get_facecolor()
    p_bbox_2 = FancyBboxPatch((bb_2.xmin, bb_2.ymin),
                            df.rating[idx], height,
                            boxstyle="round,pad=0.08,rounding_size=0.8",
                            ec="none", fc=color,
                            mutation_aspect=height*0.625
                            
                            )
    patch_2.remove()
    new_patches_2.append(p_bbox_2)

You can change the variable height, to adjust the distance.

Attention: the height should bigger than 0.

Update 2:

If you need custom color, I add a variable color, just after the definition of the variable norm

cmap = plt.get_cmap('cool')
norm = plt.Normalize(vmin=min(df.rating), vmax=max(df.rating))
color = cmap(norm(df.rating))

Then change the plot of the ax2 to this :

ax2 = sns.barplot(x=df.rating, y=df.index, palette=color)
Answered By: HMH1013
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.