如何规范seaborn distplot?

Posted

技术标签:

【中文标题】如何规范seaborn distplot?【英文标题】:How to normalize seaborn distplot? 【发布时间】:2019-08-03 08:06:04 【问题描述】:

出于再现性原因、数据集和再现性原因,我在 [此处][1] 共享它。

这是我正在做的 - 从第 2 列开始,我正在读取当前行并将其与前一行的值进行比较。如果它更大,我会继续比较。如果当前值小于前一行的值,我想将当前值(较小)除以前一个值(较大)。因此,以下代码:

这给出了以下图。

sns.distplot(quotient, hist=False, label=protname)

从图中我们可以看出

quotient_times 小于 3 时,Data-V 的商为 0.8,如果 quotient_times 为 大于 3。

我想规范化这些值,以便我们将第二个绘图值的y-axis 置于 0 和 1 之间。我们如何在 Python 中做到这一点?

【问题讨论】:

norm_hist=True 不,这没有帮助。 【参考方案1】:

前言

据我了解,seaborn distplot 默认情况下会进行 kde 估计。 如果您想要一个标准化的 distplot 图,可能是因为您假设该图的 Ys 应该在 [0;1] 之间。如果是这样,堆栈溢出问题引发了kde estimators showing values above 1的问题。

引用one answer:

连续的 pdf (pdf=概率密度函数) 永远不会说值小于 1,对于连续随机变量的 pdf,f函数 p(x) 不是概率。您可以参考连续随机变量及其分布

引用importanceofbeingernest的第一条评论:

对 pdf 的积分是 1。这里没有矛盾。

据我所知,CDF (Cumulative Density Function) 的值应该在 [0; 1]。

注意:所有可能的连续拟合函数都是on SciPy site and available in the package scipy.stats

也许也看看probability mass functions?


如果你真的想对同一张图进行归一化,那么你应该收集绘制函数(选项1)或函数定义(选项2)的实际数据点,然后自己进行归一化并重新绘制。

选项 1

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

print('System versions          : '.format(sys.version))
print('System versions          : '.format(sys.version_info))
print('Numpy versqion           : '.format(np.__version__))
print('matplotlib.pyplot version: '.format(matplotlib.__version__))
print('seaborn version          : '.format(sns.__version__))

protocols = 

types = "data_v": "data_v.csv"

for protname, fname in types.items():
    col_time,col_window = np.loadtxt(fname,delimiter=',').T
    trailing_window = col_window[:-1] # "past" values at a given index
    leading_window  = col_window[1:]  # "current values at a given index
    decreasing_inds = np.where(leading_window < trailing_window)[0]
    quotient = leading_window[decreasing_inds]/trailing_window[decreasing_inds]
    quotient_times = col_time[decreasing_inds]

    protocols[protname] = 
        "col_time": col_time,
        "col_window": col_window,
        "quotient_times": quotient_times,
        "quotient": quotient,
    

    fig, (ax1, ax2) = plt.subplots(1,2, sharey=False, sharex=False)
    g = sns.distplot(quotient, hist=True, label=protname, ax=ax1, rug=True)
    ax1.set_title('basic distplot (kde=True)')
    # get distplot line points
    line = g.get_lines()[0]
    xd = line.get_xdata()
    yd = line.get_ydata()
    # https://***.com/questions/29661574/normalize-numpy-array-columns-in-python
    def normalize(x):
        return (x - x.min(0)) / x.ptp(0)
    #normalize points
    yd2 = normalize(yd)
    # plot them in another graph
    ax2.plot(xd, yd2)
    ax2.set_title('basic distplot (kde=True)\nwith normalized y plot values')

    plt.show()

选项 2

下面,我尝试执行 kde 并标准化获得的估计。我不是统计专家,所以 kde 的使用在某些方面可能是错误的(它与 seaborn 的不同,正如屏幕截图中所见,这是因为 seaborn 的工作方式比我好得多。它只是试图模仿kde 与 scipy 的拟合。我猜结果还不错

截图:

代码:

import numpy as np
from scipy import stats
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import sys

print('System versions          : '.format(sys.version))
print('System versions          : '.format(sys.version_info))
print('Numpy versqion           : '.format(np.__version__))
print('matplotlib.pyplot version: '.format(matplotlib.__version__))
print('seaborn version          : '.format(sns.__version__))

protocols = 

types = "data_v": "data_v.csv"

for protname, fname in types.items():
    col_time,col_window = np.loadtxt(fname,delimiter=',').T
    trailing_window = col_window[:-1] # "past" values at a given index
    leading_window  = col_window[1:]  # "current values at a given index
    decreasing_inds = np.where(leading_window < trailing_window)[0]
    quotient = leading_window[decreasing_inds]/trailing_window[decreasing_inds]
    quotient_times = col_time[decreasing_inds]

    protocols[protname] = 
        "col_time": col_time,
        "col_window": col_window,
        "quotient_times": quotient_times,
        "quotient": quotient,
    

    fig, (ax1, ax2, ax3, ax4) = plt.subplots(1,4, sharey=False, sharex=False)
    diff=quotient_times
    ax1.plot(diff, quotient, ".", label=protname, color="blue")
    ax1.set_ylim(0, 1.0001)
    ax1.set_title(protname)
    ax1.set_xlabel("quotient_times")
    ax1.set_ylabel("quotient")
    ax1.legend()

    sns.distplot(quotient, hist=True, label=protname, ax=ax2, rug=True)
    ax2.set_title('basic distplot (kde=True)')

    # taken from seaborn's source code (utils.py and distributions.py)
    def seaborn_kde_support(data, bw, gridsize, cut, clip):
        if clip is None:
            clip = (-np.inf, np.inf)
        support_min = max(data.min() - bw * cut, clip[0])
        support_max = min(data.max() + bw * cut, clip[1])
        return np.linspace(support_min, support_max, gridsize)

    kde_estim = stats.gaussian_kde(quotient, bw_method='scott')

    # manual linearization of data
    #linearized = np.linspace(quotient.min(), quotient.max(), num=500)

    # or better: mimic seaborn's internal stuff
    bw = kde_estim.scotts_factor() * np.std(quotient)
    linearized = seaborn_kde_support(quotient, bw, 100, 3, None)

    # computes values of the estimated function on the estimated linearized inputs
    Z = kde_estim.evaluate(linearized)

    # https://***.com/questions/29661574/normalize-numpy-array-columns-in-python
    def normalize(x):
        return (x - x.min(0)) / x.ptp(0)

    # normalize so it is between 0;1
    Z2 = normalize(Z)
    for name, func in 'min': np.min, 'max': np.max.items():
        print(': source=, normalized='.format(name, func(Z), func(Z2)))

    # plot is different from seaborns because not exact same method applied
    ax3.plot(linearized, Z, ".", label=protname, color="orange")
    ax3.set_title('Non linearized gaussian kde values')

    # manual kde result with Y axis avalues normalized (between 0;1)
    ax4.plot(linearized, Z2, ".", label=protname, color="green")
    ax4.set_title('Normalized gaussian kde values')

    plt.show()

输出:

System versions          : 3.7.2 (default, Feb 21 2019, 17:35:59) [MSC v.1915 64 bit (AMD64)]
System versions          : sys.version_info(major=3, minor=7, micro=2, releaselevel='final', serial=0)
Numpy versqion           : 1.16.2
matplotlib.pyplot version: 3.0.2
seaborn version          : 0.9.0
min: source=0.0021601491646143518, normalized=0.0
max: source=9.67319154426489, normalized=1.0

与评论相反,绘图:

[(x-min(quotient))/(max(quotient)-min(quotient)) for x in quotient]

不改变行为!它仅更改内核密度估计的源数据。曲线形状将保持不变。

Quoting seaborn's distplot doc:

该函数结合了matplotlib hist函数(带自动 使用 seaborn kdeplot() 和 rugplot() 函数。它还可以适应 scipy.stats 分布和 在数据上绘制估计的 PDF。

默认情况下:

kde : bool,可选设置为 True 是否绘制高斯核密度估计。

默认使用kde。引用 seaborn 的 kde 文档:

拟合并绘制单变量或双变量核密度估计值。

引用SCiPy gaussian kde method doc:

使用高斯核表示核密度估计。

核密度估计是一种估计概率密度的方法 以非参数方式的随机变量的函数 (PDF)。 gaussian_kde 适用于单变量和多变量数据。它 包括自动带宽确定。估计效果最好 对于单峰分布;双峰或多峰分布倾向于 被过度平滑。

请注意,正如您自己提到的那样,我确实相信您的数据是双峰的。它们看起来也很离散。据我所知,离散分布函数的分析方式可能与连续分布函数不同,而且拟合可能会很棘手。

这是一个包含各种法律的示例:

import numpy as np
from scipy.stats import uniform, powerlaw, logistic
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import sys

print('System versions          : '.format(sys.version))
print('System versions          : '.format(sys.version_info))
print('Numpy versqion           : '.format(np.__version__))
print('matplotlib.pyplot version: '.format(matplotlib.__version__))
print('seaborn version          : '.format(sns.__version__))

protocols = 

types = "data_v": "data_v.csv"

for protname, fname in types.items():
    col_time,col_window = np.loadtxt(fname,delimiter=',').T
    trailing_window = col_window[:-1] # "past" values at a given index
    leading_window  = col_window[1:]  # "current values at a given index
    decreasing_inds = np.where(leading_window < trailing_window)[0]
    quotient = leading_window[decreasing_inds]/trailing_window[decreasing_inds]
    quotient_times = col_time[decreasing_inds]

    protocols[protname] = 
        "col_time": col_time,
        "col_window": col_window,
        "quotient_times": quotient_times,
        "quotient": quotient,
    
    fig, [(ax1, ax2, ax3), (ax4, ax5, ax6)] = plt.subplots(2,3, sharey=False, sharex=False)
    diff=quotient_times
    ax1.plot(diff, quotient, ".", label=protname, color="blue")
    ax1.set_ylim(0, 1.0001)
    ax1.set_title(protname)
    ax1.set_xlabel("quotient_times")
    ax1.set_ylabel("quotient")
    ax1.legend()
    quotient2 = [(x-min(quotient))/(max(quotient)-min(quotient)) for x in quotient]
    print(quotient2)
    sns.distplot(quotient, hist=True, label=protname, ax=ax2, rug=True)
    ax2.set_title('basic distplot (kde=True)')
    sns.distplot(quotient2, hist=True, label=protname, ax=ax3, rug=True)
    ax3.set_title('logistic distplot')

    sns.distplot(quotient, hist=True, label=protname, ax=ax4, rug=True, kde=False, fit=uniform)
    ax4.set_title('uniform distplot')
    sns.distplot(quotient, hist=True, label=protname, ax=ax5, rug=True, kde=False, fit=powerlaw)
    ax5.set_title('powerlaw distplot')
    sns.distplot(quotient, hist=True, label=protname, ax=ax6, rug=True, kde=False, fit=logistic)
    ax6.set_title('logistic distplot')
    plt.show()

输出:

System versions          : 3.7.2 (default, Feb 21 2019, 17:35:59) [MSC v.1915 64 bit (AMD64)]
System versions          : sys.version_info(major=3, minor=7, micro=2, releaselevel='final', serial=0)
Numpy versqion           : 1.16.2
matplotlib.pyplot version: 3.0.2
seaborn version          : 0.9.0
[1.0, 0.05230125523012544, 0.0433775382360589, 0.024590765616971128, 0.05230125523012544, 0.05230125523012544, 0.05230125523012544, 0.02836946874603772, 0.05230125523012544, 0.05230125523012544, 0.05230125523012544, 0.05230125523012544, 0.03393500048652319, 0.05230125523012544, 0.05230125523012544, 0.05230125523012544, 0.0037013196009011043, 0.0, 0.05230125523012544]

截图:

【讨论】:

看看我提供的 scipy 链接。 docs.scipy.org/doc/scipy-0.14.0/reference/generated/…【参考方案2】:

在最新更新中,sns.distplot 已被弃用,而必须使用 sns.histplot。因此,要获得归一化的直方图/密度,必须使用以下语法:

sns.histplot(x, kind='hist', stat='density');

sns.plot(x, stat='density');

而不是

sns.distplot(x, kde=False, norm_hist=True);

PS:要获取密度而不是直方图,必须将种类值更改为“kde”。

【讨论】:

以上是关于如何规范seaborn distplot?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Seaborn distplot 中绘制 Pandas 日期时间序列?

在 seaborn displot/histplot 函数中绘制高斯拟合直方图(不是 distplot)

Seaborn:具有相对频率的 distplot()

seaborn distplot / displot 具有多个分布

无法在 seaborn distplot 中显示图例

Python Seaborn:在 Facetgrid 中绘制多个 distplot