I have a seaborn barplot. There are two groups (two x-axis ticks). The first group has 2 hues/sub-groups/conditions. The second group has 3 hues.
When constructing the barplot, seaborn assumes that there are 3 hues for each group. Thus, it leaves an empty gap where the "third bar" of the first group should be, even though it does not exist.
How do I change the barplot so that:
By the way, in the left subplot, group2
should have a third bar but is left as "n/a" for unrelated reasons.
chatGPT gave me various manual plotting solutions, but ultimately they didn't work. I'm sure if I tried hard enough the manual plotting could work.
Here's one way to do it that requires manipulating the Rectangle
patch positions made by the barplot
command:
import seaborn as sns
import pandas as pd
from matplotlib.patches import Rectangle
# some data
data = {
"group": [1, 1, 2, 2, 2],
"measure": [0.7, 0.75, 0.78, 0.8, 1.3],
"hue": [1, 2, 1, 2, 3]
}
df = pd.DataFrame(data)
fig = sns.barplot(data=df, x="group", y="measure", hue="hue")
# get xy positions of rectangles in bar
rects = {}
for p in fig.get_children():
if isinstance(p, Rectangle):
if p.get_height() in data["measure"]:
# rectangle is one of the bars
xy = p.get_xy()
rects[xy[0]] = p
# number of values in each group
num_in_groups = df.groupby("group").count()["measure"].values
x0 = min(rects.keys()) # x-position of first bar
pad = 0.02 # some padding between groups
# sort bars on x-position
rects = sorted(rects.items())
width = rects[0][1].get_width() # bar widths
tickpos = [
x0 + (width * num_in_groups[0]) / 2,
x0 + pad + (width * num_in_groups[0]) + (width * num_in_groups[1]) / 2
]
i = 0
for _, rect in rects:
rect.set(x=x0)
x0 += width
i += 1
if i == num_in_groups[0]:
# add padding between groups
x0 += pad
# reset the group labels
fig.set_xticks(tickpos, fig.get_xticklabels())
# reset the x-axis limits
edgepad = 0.05
fig.set_xlim([rects[0][0] - edgepad, rects[0][0] + width * sum(num_in_groups) + pad + edgepad])