如何将平均线添加到 seaborn stripplot 或 swarmplot
How to add a mean line to a seaborn stripplot or swarmplot
我有一个带有垂直数据的相当简单的带状图。
planets = sns.load_dataset("planets")
sns.stripplot(x="method", y="distance", data=planets, size=4, color=".7")
plt.xticks(rotation=45, ha="right")
plt.show()
我想将每个 x 元素 (method
) 的平均值绘制为一个小的水平条,类似于您得到的结果:
sns.boxplot(
x="method",
y="distance",
data=planets,
whis=[50, 50],
showfliers=False,
showbox=False,
showcaps=False
)
但是没有第一/第三四分位数的垂直线(whis=[50,50]
只是点)并且显示平均值而不是中位数。也许有一个不涉及箱线图的更优雅的解决方案。
提前致谢。
- Boxplot 对象在
matplotlib.pyplot.boxplot
中定义
showmeans=True
meanline=True
画一条线而不是标记
meanprops={'color': 'k', 'ls': '-', 'lw': 2}
设置线条的颜色、样式和宽度。
- 有关其他线路属性,请参阅
matplotlib.lines.Line2D
。
medianprops={'visible': False}
使中线不可见
whiskerprops={'visible': False}
使胡须线不可见
zorder=10
把线放在顶层
- 在
matplotlib v3.4.2
和 seaborn v0.11.1
中测试
import seaborn as sns
import matplotlib.pyplot as plt
# load the dataset
planets = sns.load_dataset("planets")
p = sns.stripplot(x="method", y="distance", data=planets, size=4, color=".7")
plt.xticks(rotation=45, ha="right")
p.set(yscale='log')
# plot the mean line
sns.boxplot(showmeans=True,
meanline=True,
meanprops={'color': 'k', 'ls': '-', 'lw': 2},
medianprops={'visible': False},
whiskerprops={'visible': False},
zorder=10,
x="method",
y="distance",
data=planets,
showfliers=False,
showbox=False,
showcaps=False,
ax=p)
plt.show()
- 与
seaborn.swarmplot
的工作方式类似
这是一个使用 ax.hlines
并使用 groupby
求均值和列表理解的解决方案:
import seaborn as sns
import matplotlib.pyplot as plt
# load the dataset
planets = sns.load_dataset("planets")
p = sns.stripplot(x="method", y="distance", data=planets, size=4, color=".7", zorder=1)
plt.xticks(rotation=45, ha="right")
p.set(yscale='log');
df_mean = planets.groupby('method', sort=False)['distance'].mean()
_ = [p.hlines(y, i-.25, i+.25, zorder=2) for i, y in df_mean.reset_index()['distance'].items()]
输出:
这是另一个类似于箱线图想法但需要较少覆盖的 hack:绘制一个 pointplot
但置信区间宽度为 0,并激活错误栏“上限”以获得一条水平线参数化宽度:
planets = sns.load_dataset("planets")
spec = dict(x="method", y="distance", data=planets)
sns.stripplot(**spec, size=4, color=".7")
sns.pointplot(**spec, join=False, ci=0, capsize=.7, scale=0)
plt.xticks(rotation=45, ha="right")
这里明显的一个缺点是,对于具有单一观察的组,引导会被跳过,所以你不会在那里得到平均线。这在实际应用中可能是也可能不是问题。
另一个技巧是自己分组,然后用非常宽的垂直线标记绘制散点图:
planets = sns.load_dataset("planets")
variables = dict(x="method", y="distance")
sns.stripplot(data=planets, **variables, size=4, color=".7")
sns.scatterplot(
data=planets.groupby("method")["distance"].mean().reset_index(),
**variables, marker="|", s=2, linewidth=25
)
plt.xticks(rotation=45, ha="right")
我有一个带有垂直数据的相当简单的带状图。
planets = sns.load_dataset("planets")
sns.stripplot(x="method", y="distance", data=planets, size=4, color=".7")
plt.xticks(rotation=45, ha="right")
plt.show()
我想将每个 x 元素 (method
) 的平均值绘制为一个小的水平条,类似于您得到的结果:
sns.boxplot(
x="method",
y="distance",
data=planets,
whis=[50, 50],
showfliers=False,
showbox=False,
showcaps=False
)
但是没有第一/第三四分位数的垂直线(whis=[50,50]
只是点)并且显示平均值而不是中位数。也许有一个不涉及箱线图的更优雅的解决方案。
提前致谢。
- Boxplot 对象在
matplotlib.pyplot.boxplot
中定义showmeans=True
meanline=True
画一条线而不是标记meanprops={'color': 'k', 'ls': '-', 'lw': 2}
设置线条的颜色、样式和宽度。- 有关其他线路属性,请参阅
matplotlib.lines.Line2D
。
- 有关其他线路属性,请参阅
medianprops={'visible': False}
使中线不可见whiskerprops={'visible': False}
使胡须线不可见zorder=10
把线放在顶层
- 在
matplotlib v3.4.2
和seaborn v0.11.1
中测试
import seaborn as sns
import matplotlib.pyplot as plt
# load the dataset
planets = sns.load_dataset("planets")
p = sns.stripplot(x="method", y="distance", data=planets, size=4, color=".7")
plt.xticks(rotation=45, ha="right")
p.set(yscale='log')
# plot the mean line
sns.boxplot(showmeans=True,
meanline=True,
meanprops={'color': 'k', 'ls': '-', 'lw': 2},
medianprops={'visible': False},
whiskerprops={'visible': False},
zorder=10,
x="method",
y="distance",
data=planets,
showfliers=False,
showbox=False,
showcaps=False,
ax=p)
plt.show()
- 与
seaborn.swarmplot
的工作方式类似
这是一个使用 ax.hlines
并使用 groupby
求均值和列表理解的解决方案:
import seaborn as sns
import matplotlib.pyplot as plt
# load the dataset
planets = sns.load_dataset("planets")
p = sns.stripplot(x="method", y="distance", data=planets, size=4, color=".7", zorder=1)
plt.xticks(rotation=45, ha="right")
p.set(yscale='log');
df_mean = planets.groupby('method', sort=False)['distance'].mean()
_ = [p.hlines(y, i-.25, i+.25, zorder=2) for i, y in df_mean.reset_index()['distance'].items()]
输出:
这是另一个类似于箱线图想法但需要较少覆盖的 hack:绘制一个 pointplot
但置信区间宽度为 0,并激活错误栏“上限”以获得一条水平线参数化宽度:
planets = sns.load_dataset("planets")
spec = dict(x="method", y="distance", data=planets)
sns.stripplot(**spec, size=4, color=".7")
sns.pointplot(**spec, join=False, ci=0, capsize=.7, scale=0)
plt.xticks(rotation=45, ha="right")
这里明显的一个缺点是,对于具有单一观察的组,引导会被跳过,所以你不会在那里得到平均线。这在实际应用中可能是也可能不是问题。
另一个技巧是自己分组,然后用非常宽的垂直线标记绘制散点图:
planets = sns.load_dataset("planets")
variables = dict(x="method", y="distance")
sns.stripplot(data=planets, **variables, size=4, color=".7")
sns.scatterplot(
data=planets.groupby("method")["distance"].mean().reset_index(),
**variables, marker="|", s=2, linewidth=25
)
plt.xticks(rotation=45, ha="right")