如何使用 seaborn 在同一张图上绘制两个小提琴图系列?
How to plot two violin plot series on the same graph using seaborn?
查看有关 violon plots 与 seaborn 的文档,我想知道如何在同一轴上绘制两个系列的小提琴图(第 1 点)并且它们具有可比性(第 2 点)。
关于第 1 点,我想为每个性别重现该情节:
fig, ax = plt.subplots()
sns.violinplot(x="day", y="total_bill", hue="smoker",
data=tips, split=True, ax=ax)
我可以在两个子图中完成:
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(211)
sns.violinplot(x="day", y="total_bill", hue="smoker",
data=tips[tips.sex == "Female"], split=True, ax=ax)
ax = fig.add_subplot(212)
sns.violinplot(x="day", y="total_bill", hue="smoker",
data=tips[tips.sex == "Male"], split=True, ax=ax)
我想在同一个 matplotlib 轴上绘制两个小提琴情节系列。
还有一点是关于小提琴情节的宽度。我不清楚小提琴是否标准化以及如何标准化?我假设为每个图计算宽度。在上面的示例中,第一个子图的 Female 和第二个子图的 Male 计算了宽度。因此我可以直接比较密度吗?我想我可以比较形状,但是,例如,我无法比较星期一男性吸烟者和女性吸烟者的数量?有没有办法管理小提琴的正常化?
对于你的第一点,在 Seaborn 中没有办法做到这一点。查看我的评论以了解可能的解决方法,但简而言之,我认为花费的时间不值得。
对于你的第二个问题,violinplot
的 scale
和 scale_hue
参数控制小提琴音色 normalized/scaled:
scale : {“area”, “count”, “width”}, optional
The method used to scale the width of each violin. If area, each violin will have the same area. If count, the width of the violins will be scaled by the number of observations in that bin. If width, each violin will have the same width.
scale_hue : bool, optional
When nesting violins using a hue variable, this parameter determines whether the scaling is computed within each level of the major grouping variable (scale_hue=True) or across all the violins on the plot (scale_hue=False).
默认值为 'area'
和 False
。您可以在下面看到这些参数的变化如何影响小提琴。例如,如果你想比较不同的图并真实地表示绝对计数,你可以设置 scale='count'
和 scale_hue=False
。请注意,小提琴仍将按比例缩放到图中(而不是数据集中)的最大数量,因此在我们的例子中,女性最大的小提琴将代表约 40 个观察结果,而男性最大的小提琴将代表约 25 个观察结果。
fig, axes = plt.subplots(4, 2, figsize=(10, 16), sharey='row')
axes_cols = (axes.flatten()[::2], axes.flatten()[1::2])
for (sex_name, sex), axes_col in zip(tips.groupby('sex'), axes_cols):
sns.countplot(x="day", hue="smoker", data=sex, ax=axes_col[0])
for scale, ax in zip(['area', 'count', 'width'], axes_col[1:]):
sns.violinplot(x="day", y="total_bill", hue="smoker",
data=sex, split=True, ax=ax, scale=scale)
ax.set_title('scale = {}'.format(scale), y=0.95)
sns.despine()
fig.tight_layout()
添加scale_hue=False
:
正如 Joel Ostblom 在 ist answer 中所说,没有办法在 seaborn 中本地绘制多个小提琴图。我在这里根据他的提示建议一个解决方案。我还在顶部添加了一个计数图,就像他在回答中所做的那样。
我不太了解tips数据集,所以下面的图肯定没有意义。
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="whitegrid", font_scale=1.5)
tips = sns.load_dataset("tips")
# here you add a new column with the two categorical data you want
tips["sex_time"] = tips[["sex", "time"]].apply(lambda x: "_".join(x), axis=1)
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(8, 10),
sharex=True, gridspec_kw=dict(height_ratios=(1, 3), hspace=0))
# select the order you want:
order=["Female_Lunch", "Male_Lunch", "Female_Dinner", "Male_Dinner"]
sns.countplot(
data=tips, x="sex_time", hue="smoker",
order=order,
ax=axes[0]
)
sns.violinplot(
x="sex_time", y="total_bill", hue="smoker", data=tips,
split=True, scale="count", scale_hue=False, inner="stick",
order=order,
ax=axes[1]
)
axes[1].set_xticklabels(["Lunch (Female)", "Lunch (Male)", "Dinner (Female)", "Dinner (Male)"])
axes[1].set_xlabel("Time (Sex)")
axes[1].legend("")
为了补充@joelostblom 的回答,我想分享一个 DIY 解决方案,用于在同一轴上绘制两个系列的小提琴图。
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
# Load the dataset
tips = sns.load_dataset("tips")
# Configure the coloring
color = {"Male": {"Yes": "orange", "No": "blue"}, "Female": {"Yes": "red", "No": "green"}}
# create figure and axes
fig, ax = plt.subplots()
# draw violins for each sex
sex_types = set(tips.sex)
for sex in sex_types:
sns.violinplot(
x="day",
y="total_bill",
hue="smoker",
data=tips[tips.sex == sex],
palette=color[sex],
split=True,
ax=ax,
scale="count",
scale_hue=False,
saturation=0.75,
inner=None
)
# Set transparancy for all violins
for violin in ax.collections:
violin.set_alpha(0.25)
# Compose a custom legend
custom_lines = [
Line2D([0], [0], color=color[sex][smoker], lw=4, alpha=0.25)
for smoker in ["Yes", "No"]
for sex in sex_types
]
ax.legend(
custom_lines,
[f"{sex} : {smoker}" for smoker in ["Yes", "No"] for sex in sex_types],
title="Gender : Smoker"
)
查看有关 violon plots 与 seaborn 的文档,我想知道如何在同一轴上绘制两个系列的小提琴图(第 1 点)并且它们具有可比性(第 2 点)。
关于第 1 点,我想为每个性别重现该情节:
fig, ax = plt.subplots()
sns.violinplot(x="day", y="total_bill", hue="smoker",
data=tips, split=True, ax=ax)
我可以在两个子图中完成:
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(211)
sns.violinplot(x="day", y="total_bill", hue="smoker",
data=tips[tips.sex == "Female"], split=True, ax=ax)
ax = fig.add_subplot(212)
sns.violinplot(x="day", y="total_bill", hue="smoker",
data=tips[tips.sex == "Male"], split=True, ax=ax)
我想在同一个 matplotlib 轴上绘制两个小提琴情节系列。
还有一点是关于小提琴情节的宽度。我不清楚小提琴是否标准化以及如何标准化?我假设为每个图计算宽度。在上面的示例中,第一个子图的 Female 和第二个子图的 Male 计算了宽度。因此我可以直接比较密度吗?我想我可以比较形状,但是,例如,我无法比较星期一男性吸烟者和女性吸烟者的数量?有没有办法管理小提琴的正常化?
对于你的第一点,在 Seaborn 中没有办法做到这一点。查看我的评论以了解可能的解决方法,但简而言之,我认为花费的时间不值得。
对于你的第二个问题,violinplot
的 scale
和 scale_hue
参数控制小提琴音色 normalized/scaled:
scale : {“area”, “count”, “width”}, optional
The method used to scale the width of each violin. If area, each violin will have the same area. If count, the width of the violins will be scaled by the number of observations in that bin. If width, each violin will have the same width.
scale_hue : bool, optional
When nesting violins using a hue variable, this parameter determines whether the scaling is computed within each level of the major grouping variable (scale_hue=True) or across all the violins on the plot (scale_hue=False).
默认值为 'area'
和 False
。您可以在下面看到这些参数的变化如何影响小提琴。例如,如果你想比较不同的图并真实地表示绝对计数,你可以设置 scale='count'
和 scale_hue=False
。请注意,小提琴仍将按比例缩放到图中(而不是数据集中)的最大数量,因此在我们的例子中,女性最大的小提琴将代表约 40 个观察结果,而男性最大的小提琴将代表约 25 个观察结果。
fig, axes = plt.subplots(4, 2, figsize=(10, 16), sharey='row')
axes_cols = (axes.flatten()[::2], axes.flatten()[1::2])
for (sex_name, sex), axes_col in zip(tips.groupby('sex'), axes_cols):
sns.countplot(x="day", hue="smoker", data=sex, ax=axes_col[0])
for scale, ax in zip(['area', 'count', 'width'], axes_col[1:]):
sns.violinplot(x="day", y="total_bill", hue="smoker",
data=sex, split=True, ax=ax, scale=scale)
ax.set_title('scale = {}'.format(scale), y=0.95)
sns.despine()
fig.tight_layout()
添加scale_hue=False
:
正如 Joel Ostblom 在 ist answer 中所说,没有办法在 seaborn 中本地绘制多个小提琴图。我在这里根据他的提示建议一个解决方案。我还在顶部添加了一个计数图,就像他在回答中所做的那样。
我不太了解tips数据集,所以下面的图肯定没有意义。
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="whitegrid", font_scale=1.5)
tips = sns.load_dataset("tips")
# here you add a new column with the two categorical data you want
tips["sex_time"] = tips[["sex", "time"]].apply(lambda x: "_".join(x), axis=1)
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(8, 10),
sharex=True, gridspec_kw=dict(height_ratios=(1, 3), hspace=0))
# select the order you want:
order=["Female_Lunch", "Male_Lunch", "Female_Dinner", "Male_Dinner"]
sns.countplot(
data=tips, x="sex_time", hue="smoker",
order=order,
ax=axes[0]
)
sns.violinplot(
x="sex_time", y="total_bill", hue="smoker", data=tips,
split=True, scale="count", scale_hue=False, inner="stick",
order=order,
ax=axes[1]
)
axes[1].set_xticklabels(["Lunch (Female)", "Lunch (Male)", "Dinner (Female)", "Dinner (Male)"])
axes[1].set_xlabel("Time (Sex)")
axes[1].legend("")
为了补充@joelostblom 的回答,我想分享一个 DIY 解决方案,用于在同一轴上绘制两个系列的小提琴图。
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
# Load the dataset
tips = sns.load_dataset("tips")
# Configure the coloring
color = {"Male": {"Yes": "orange", "No": "blue"}, "Female": {"Yes": "red", "No": "green"}}
# create figure and axes
fig, ax = plt.subplots()
# draw violins for each sex
sex_types = set(tips.sex)
for sex in sex_types:
sns.violinplot(
x="day",
y="total_bill",
hue="smoker",
data=tips[tips.sex == sex],
palette=color[sex],
split=True,
ax=ax,
scale="count",
scale_hue=False,
saturation=0.75,
inner=None
)
# Set transparancy for all violins
for violin in ax.collections:
violin.set_alpha(0.25)
# Compose a custom legend
custom_lines = [
Line2D([0], [0], color=color[sex][smoker], lw=4, alpha=0.25)
for smoker in ["Yes", "No"]
for sex in sex_types
]
ax.legend(
custom_lines,
[f"{sex} : {smoker}" for smoker in ["Yes", "No"] for sex in sex_types],
title="Gender : Smoker"
)