如何确定中线的小提琴图边缘的 x 值

Posted

技术标签:

【中文标题】如何确定中线的小提琴图边缘的 x 值【英文标题】:How to determine the x value on the edge of the violinplot for a mean line 【发布时间】:2021-11-13 16:25:01 【问题描述】:

我试图在小提琴图上画一条平均线,因为我无法找到一种方法让 sns 替换来自“四分位数”的“中位数”线,所以我决定编写代码,以便在每种情况下它绘制在顶部。我计划在我拥有的三个图表的平均值(y 值)上使用 plt.plot 绘制水平线。

我有确切的 y(高度)值,我希望在其中绘制水平线,但是,我很难找出每个小提琴图在该特定 y 值上的界限。我知道,因为它是对称的,所以域是(-x,x),所以我需要一种方法来找到“x”值,以便我能够添加 3 条水平线,每条水平线都以我拥有的小提琴图为界。

这是我的代码,plt.plot 的 x 值为 -0.37,这是我通过反复试验找到的,我希望 python 为给定的 y 值找到它。强>

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

data = [2.57e-05, 4.17e-06, -5.4e-06, -5.05e-06, 1.15e-05, -6.7e-06, 1.01e-05, 5.53e-06, 8.13e-06, 1.27e-05, 1.11e-06, -2.87e-06, -1.38e-06, -1.07e-05, -8.04e-06, 4.77e-06, 3.22e-07, 9.86e-06, 1.38e-05, 1.32e-05, -3.48e-06, -4.69e-06, 8.15e-06, 4.21e-07, 2.71e-06, 7.52e-08, 1.04e-06, -1.92e-06, -4.08e-06, 4.76e-06]

vg = sns.violinplot(data=data, inner="quartile", scale="width")
    
a = sns.pointplot(data=data, zlinestyles='-', join=False, ci=None, color='red')
        
for p in vg.lines:
    p.set_linestyle('-')
    p.set_linewidth(0.8)  # Sets the thickness of the quartile lines 
    p.set_color('white')  # Sets the color of the quartile lines 
    p.set_alpha(0.8)

for p in vg.lines[1::3]:  # these are the median lines; not means
    p.set_linestyle('-')
    p.set_linewidth(0)  # Sets the thickness of the median lines 
    p.set_color('black')  # Sets the color of the median lines 
    p.set_alpha(0.8)

# add a mean line from the edge of the violin plot
plt.plot([-0.37, 0], [np.mean(data), np.mean(data)], 'k-', lw=1)
plt.show()

请参阅我删除了中点但留下四分位线的图片,我想在可见蓝点的地方绘制平均线

这是我用我通过反复试验找到的 x 值绘制 plt.plot 后的图片:仅适用于我的情况

【问题讨论】:

希望答案是有帮助的。彻底回答问题很费时间。如果您的问题已解决,请接受解决方案 位于答案左上角的 ▲/▼ 箭头下方。如果出现更好的解决方案,则可以接受新的解决方案。如果您的声望超过 15,您还可以使用 ▲/▼ 箭头对答案的有用性进行投票。 如果解决方案无法回答问题,请发表评论。 What should I do when someone answers my question?。谢谢。 【参考方案1】:

你可以画一条太长的线,然后用多边形剪成小提琴。

请注意,inner='quartile' 显示 25%、50% 和 75% 线。 50% 线也称为中位数。这类似于boxplots 通常的绘制方式。以过于相似的方式显示平均值是相当令人困惑的。这就是为什么 seaborn(和许多其他库)更喜欢将平均值显示为一个点。

这是一些示例代码(请注意,sns.violinplot 的返回值是 ax,并且命名非常不同,因此很难找到进入 matplotlib 和 seaborn 文档和示例的方法)。

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

tips = sns.load_dataset('tips')
tips['day'] = pd.Categorical(tips['day'])

ax = sns.violinplot(data=tips, x='day', y='total_bill', hue='day', inner='quartile', scale='width', dodge=False)
sns.pointplot(data=tips, x='day', y='total_bill', join=False, ci=None, color='yellow', ax=ax)
ax.legend_.remove()

for p in ax.lines:
    p.set_linestyle('-')
    p.set_linewidth(0.8)  # Sets the thickness of the quartile lines
    p.set_color('white')  # Sets the color of the quartile lines
    p.set_alpha(0.8)
for x, (day, violin) in enumerate(zip(tips['day'].cat.categories, ax.collections)):
    line = ax.hlines(tips[tips['day'] == day]['total_bill'].mean(), x - 0.5, x + 0.5, color='black', ls=':', lw=2)
    patch = PathPatch(violin.get_paths()[0], transform=ax.transData)
    line.set_clip_path(patch)  # clip the line by the form of the violin
plt.show()

更新为使用数据列表列表:

data = [np.random.randn(10, 7).cumsum(axis=0).ravel() for _ in range(3)]

ax = sns.violinplot(data=data, inner='quartile', scale='width', palette='Set2')
# sns.pointplot(data=data, join=False, ci=None, color='red', ax=ax) # shows the means
ax.set_xticks(range(len(data)))
ax.set_xticklabels(['I' * (k + 1) for k in range(len(data))])

for p in ax.lines:
    p.set_linestyle('-')
    p.set_linewidth(0.8)  # Sets the thickness of the quartile lines
    p.set_color('white')  # Sets the color of the quartile lines
    p.set_alpha(0.8)
for x, (data_x, violin) in enumerate(zip(data, ax.collections)):
    line = ax.hlines(np.mean(data_x), x - 0.5, x + 0.5, color='black', ls=':', lw=2)
    patch = PathPatch(violin.get_paths()[0], transform=ax.transData)
    line.set_clip_path(patch)
plt.show()

PS:关于enumerate(zip(...))的一些进一步解释

for data_x in data: 将遍历列表data 的条目,首先将data[0] 分配给data_x 等。 for x, data_x in enumerate(data): 将遍历列表data 的条目,同时将变量x0 增加到1,最后增加到2for data_x, violin in zip(data, ax.collections):data_x 循环遍历列表 data 的条目,同时通过存储在 ax.collections 的列表中的变量 violin(这是 matplotlib 存储小提琴形状的地方) for x, (data_x, violin) in enumerate(zip(data, ax.collections)): combines the enumeration with zip`

【讨论】:

我很困惑如何在我的代码中实现这个方法:我正在使用数组,所以我不太明白 for 循环是什么:for x, (day, violin) in enumerate(zip (tips['day'].cat.categories, ax.collections)): 正在迭代。 x, (day,violin) 真正代表什么?而不是 enumerate(zip(..)) 真正在做什么?顺便谢谢你的帮助,我一直在到处寻找答案,再多一点帮助就太好了! 另外,当我调整一个类似的公式时,我的 python 不明白什么是“小提琴”,看着我尖叫。 @TrentonMcKinney 最初不是,然后我尝试了 Jupiter,它做到了 @TrentonMcKinney 非常感谢您提供的附加代码。我更新了它以在循环中省略[:4],并且更类似于OP。 如果这回答了您的问题,您可能会将marking 的答案视为已接受。

以上是关于如何确定中线的小提琴图边缘的 x 值的主要内容,如果未能解决你的问题,请参考以下文章

如何求联合分布函数和边缘分布函数?

确定Qt中线的边界矩形

已知联合分布函数怎么求边缘分布函数呢?

plot.lm() 如何确定残差与拟合图的异常值?

如何使用 MATLAB 绘制小提琴图

如何使用 MATLAB 绘制小提琴图