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?
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:
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)
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?
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:
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)