【问题标题】:How to plot a mean line on a distplot between 0 and the y value of the mean?如何在 0 和平均值的 y 值之间的 distplot 上绘制平均线?
【发布时间】:2020-11-28 02:59:12
【问题描述】:

我有一个分布图,我想绘制一条从 0 到平均频率的 y 值的平均线。 I want to do this,但在 distplot 出现时让线路停止。为什么没有一个简单的参数可以做到这一点?这将非常有用。

我有一些代码可以让我快到那里:

plt.plot([x.mean(),x.mean()], [0, *what here?*])

除了我想要的 y 值之外,此代码按照我的意愿绘制一条线。正确的数学是什么让 y max 停止在 distplot 中的平均值频率?下面是我的一个分布图的示例,使用 0.6 作为 y-max。如果有一些数学让它停在平均值的 y 值上,那就太棒了。我试过将平均值除以计数等。

【问题讨论】:

  • "为什么没有一个简单的参数可以做到这一点?"因为如果库试图构建一个 API 来处理每个域和用户的特定用例,那么 API 将会非常臃肿且难以使用,并且是难以维护的负担。
  • 你能解释一下标题 “y 轴极限之间” 和帖子 “一条从 0 到平均值的 y 值的线之间的区别吗?”频率”? (顺便说一下,曲线显示的是density,而不是频率。)

标签: python matplotlib seaborn distribution


【解决方案1】:

更新最新版本的 matplotlib (3.3.4) 和 seaborn (0.11.1):带有shade=True 的 kdeplot 现在不再创建线对象。要获得与以前相同的结果,设置 shade=False 仍将创建线对象。然后可以用ax.fill_between() 填充曲线。下面的代码进行了相应的更改。 (使用revision history 查看旧版本。)

ax.lines[0]获取kde的曲线,可以提取其中的x和y数据。 np.interp 然后可以找到给定 x 值的曲线高度:

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

x = np.random.normal(np.tile(np.random.uniform(10, 30, 5), 50), 3)
ax = sns.kdeplot(x, shade=False, color='crimson')
kdeline = ax.lines[0]
mean = x.mean()
xs = kdeline.get_xdata()
ys = kdeline.get_ydata()
height = np.interp(mean, xs, ys)
ax.vlines(mean, 0, height, color='crimson', ls=':')
ax.fill_between(xs, 0, ys, facecolor='crimson', alpha=0.2)
plt.show()

同样的方法可以扩展到显示平均值和标准差,或中位数和四分位数:

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

x = np.random.normal(np.tile(np.random.uniform(10, 30, 5), 50), 3)
fig, axes = plt.subplots(ncols=2, figsize=(12, 4))
for ax in axes:
    sns.kdeplot(x, shade=False, color='crimson', ax=ax)
    kdeline = ax.lines[0]
    xs = kdeline.get_xdata()
    ys = kdeline.get_ydata()
    if ax == axes[0]:
        middle = x.mean()
        sdev = x.std()
        left = middle - sdev
        right = middle + sdev
        ax.set_title('Showing mean and sdev')
    else:
        left, middle, right = np.percentile(x, [25, 50, 75])
        ax.set_title('Showing median and quartiles')
    ax.vlines(middle, 0, np.interp(middle, xs, ys), color='crimson', ls=':')
    ax.fill_between(xs, 0, ys, facecolor='crimson', alpha=0.2)
    ax.fill_between(xs, 0, ys, where=(left <= xs) & (xs <= right), interpolate=True, facecolor='crimson', alpha=0.2)
    # ax.set_ylim(ymin=0)
plt.show()

PS:对于kde的模式:

    mode_idx = np.argmax(ys)
    ax.vlines(xs[mode_idx], 0, ys[mode_idx], color='lime', ls='--')

【讨论】:

  • ylim 上的小弃用警告,并且必须从 ax.vlines 中删除参数 ls,但总体而言答案很好
  • 感谢您的反馈。我无法用ylimls=ax.vlines 重现您的问题。我现在使用 seaborn 0.11.1 和 matplotlib 3.3.4 进行了测试,并注意到 shade=True 不再创建线对象的问题。更新后的代码现在应该适用于这些版本。我还注意到 ax.set_ylim() 不再需要了,所以我把它省略了。
  • 很好用,这应该是seaborn中的一个选项。
【解决方案2】:

使用plt.get_ylim(),您可以获得当前绘图的限制:[bottom, top]。
因此,在您的情况下,您可以提取实际限制并将其保存在ylim 中,然后画线:

fig, ax = plt.subplots()

ylim = ax.get_ylim()
ax.plot([x.mean(),x.mean()], ax.get_ylim())
ax.set_ylim(ylim)

由于 ax.plot 之后会更改 ylim,您必须使用 ax.set_ylim 重新设置它们。

【讨论】:

  • 我更新了我的答案,以考虑到你提到的缺点,谢谢
猜你喜欢
  • 2021-01-17
  • 2015-08-29
  • 2018-12-28
  • 2018-09-20
  • 2021-11-15
  • 2018-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多