Python 对齐多个叠置多面Seborn CAT图

import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np

################### generate dummy data set ###################
np.random.seed(20240224)
numPoints = 300 # should be divisible by 3 and 2
df = pd.DataFrame({"CategoryX": np.random.randint(1, 4, numPoints),
"CategoryY": np.random.rand(numPoints),
# the imbalance here seems to be the problem trigger
"CategoryColor": np.random.choice([0,1,2,3], size=numPoints, p=[0.33, 0.33, 0.33, 0.01]),
"CategoryColumn": np.array(["ColA", "ColB", "ColC"] * (numPoints // 3)),
"CategoryRow": np.array(["RowA"] * (numPoints // 2) + ["RowB"] * (numPoints // 2)),
})

################### actual plot ###################

commonParams = dict(
x="CategoryX",
y="CategoryY",
hue="CategoryColor",
)

g = sns.catplot(
data=df,
**commonParams,
col="CategoryColumn",
row="CategoryRow",
kind="strip",
dodge=True,
)

# map by hand bc I couldn't figure out how to properly use map() or map_dataframe()
for i, s in enumerate(df['CategoryColumn'].unique()):
for j, f in enumerate(df['CategoryRow'].unique()):
sns.boxplot(
data=df[(df['CategoryColumn'] == s) & (df['CategoryRow'] == f)],
**commonParams,
ax=g.axes[j, i],        # draw on the existing axes
legend=False,
)


推荐答案

(我也测试了hue_order，但这只有在您还设置了相同 colored颜色 数量的调色板时才起作用，不幸的是，这会弄乱图形图例.使用Seborn 0.13.2和Pandas 2.2.1进行测试)


# change the column from numeric to pd.Categorical
df["CategoryColor"] = pd.Categorical(df["CategoryColor"])

commonParams = dict(
x="CategoryX",
y="CategoryY",
hue="CategoryColor",
palette='flare',
)

g = sns.catplot(
data=df,
**commonParams,
col="CategoryColumn",
row="CategoryRow",
kind="strip",
dodge=True,
)

for (row, col), ax in g.axes_dict.items():
sns.boxplot(
data=df[(df['CategoryColumn'] == col) & (df['CategoryRow'] == row)],
**commonParams,
ax=ax,  # draw on the existing axes
legend=False,
boxprops={'alpha': 0.7} # transparency to see stripplot
)
plt.show()