如何创建一个密度图

161
在R中,我可以通过以下方式创建所需的输出:
data = c(rep(1.5, 7), rep(2.5, 2), rep(3.5, 8),
         rep(4.5, 3), rep(5.5, 1), rep(6.5, 8))
plot(density(data, bw=0.5))

Density plot in R

在Python中(使用matplotlib),我最接近的方法是使用一个简单的直方图:
import matplotlib.pyplot as plt
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
plt.hist(data, bins=6)
plt.show()

Histogram in matplotlib

我也尝试了使用normed=True参数,但除了试图将高斯分布拟合到直方图上之外,什么都没得到。

我的最新尝试是围绕scipy.statsgaussian_kde进行的,参考了网上的例子,但到目前为止还没有成功。

6个回答

199

五年后,当我在谷歌上搜索“如何使用Python创建核密度图”时,这个帖子仍然是排名最高的!

今天,更加简单的方法是使用seaborn,它提供了许多方便的绘图函数和良好的样式管理。

import numpy as np
import seaborn as sns
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
sns.set_style('whitegrid')
sns.kdeplot(np.array(data), bw=0.5)

输入图像描述


非常感谢。我已经搜索了数天,寻找像这样的东西。您能否解释一下为什么会给出bw=0.5 - Sitz Blogz
5
“bw”参数代表带宽。我正在尝试与OP的设置匹配(请参阅他最初的第一个代码示例)。关于“bw”的详细解释,请参见https://en.wikipedia.org/wiki/Kernel_density_estimation#Bandwidth_selection。基本上,它控制您希望密度图平滑的程度。带宽越大,曲线就越平滑。 - Xin
我还有一个问题要问,我的数据是离散的,我正在尝试绘制概率密度函数(PDF),在阅读了Scipy文档后,我明白了PMF = PDF这一点。对此有什么建议如何绘制它? - Sitz Blogz
1
当我尝试这个时,我得到了TypeError: slice indices must be integers or None or have an __index__ method的错误。 - endolith
1
只想补充一下,bw参数已经被弃用,可以作为起点进行删除。 - Raisin

144

Sven展示了如何使用Scipy中的gaussian_kde类,但是您会注意到它看起来并不像您用R生成的样子。这是因为gaussian_kde试图自动推断带宽。您可以通过改变gaussian_kde类的covariance_factor函数来调整带宽。首先,这是在不更改该函数的情况下得到的结果:

alt text

然而,如果我使用以下代码:

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
density = gaussian_kde(data)
xs = np.linspace(0,8,200)
density.covariance_factor = lambda : .25
density._compute_covariance()
plt.plot(xs,density(xs))
plt.show()

我得到了:

alt text

这跟你从R得到的结果非常接近。我做了什么?gaussian_kde使用一个可变函数covariance_factor来计算其带宽。在更改该函数之前,此数据返回的值约为0.5。降低这个值会降低带宽。在更改该函数后,我必须调用_compute_covariance,以便正确计算所有因素。虽然它不是与R中的bw参数完全对应,但希望能帮助您走向正确方向。


14
在Scipy 0.11.0版本中,根据问题1619,在高斯核密度估计器gaussian_kde中添加了一个set_bandwidth方法和一个bw_method构造函数参数。 - eddygeek
为了与其他回答链接,在seaborn或pandas实现的kde中,默认的kde是gaussian_kde - Ger

71

选项1:

使用 pandas 数据帧绘图(基于 matplotlib):


import pandas as pd
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
pd.DataFrame(data).plot(kind='density') # or pd.Series()

enter image description here

选项2:

使用 seaborndistplot

import seaborn as sns
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
sns.distplot(data, hist=False)

输入图片描述


4
添加带宽参数:df.plot.density(bw_method=0.5) - Anake
4
@Aziz 不需要使用 pandas.DataFrame,可以使用 pandas.Series(data).plot(kind='density')。 @Anake,不需要将 df.plot.density 设置为一个单独的步骤;可以直接将 bw_method 参数传递到 pd.Series(data).plot(kind='density', bw_method=0.5) 中。 - Nate Anderson

52

或许可以尝试这样做:

import matplotlib.pyplot as plt
import numpy
from scipy import stats
data = [1.5]*7 + [2.5]*2 + [3.5]*8 + [4.5]*3 + [5.5]*1 + [6.5]*8
density = stats.kde.gaussian_kde(data)
x = numpy.arange(0., 8, .1)
plt.plot(x, density(x))
plt.show()

你可以轻松地用其他核密度估计方法替换gaussian_kde()


0
你可以这样做:
s = np.random.normal(2, 3, 1000)
import matplotlib.pyplot as plt
count, bins, ignored = plt.hist(s, 30, density=True)
plt.plot(bins, 1/(3 * np.sqrt(2 * np.pi)) * np.exp( - (bins - 2)**2 / (2 * 3**2) ), 
linewidth=2, color='r')
plt.show()

0
密度图也可以使用matplotlib创建: 函数plt.hist(data)返回密度图所需的y和x值(请参阅文档https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.hist.html)。 因此,以下代码使用matplotlib库创建密度图:
import matplotlib.pyplot as plt
dat=[-1,2,1,4,-5,3,6,1,2,1,2,5,6,5,6,2,2,2]
a=plt.hist(dat,density=True)
plt.close()
plt.figure()
plt.plot(a[1][1:],a[0])      

这段代码返回以下密度图

enter image description here


7
这个回答应该被点踩。然而我不会这样做,因为点踩是邪恶的。相反,我会解释一下问题所在:从一个样本(一组数据点)中进行密度估计通常需要进行平滑处理。这就是R语言中density()函数或SciPy中的gaussian_kde()函数所执行的操作。结果是对数据点所代表的连续密度的近似,这正是提问者所寻求的内容。 - András Aszódi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接