二维变量数量分布图:高斯核函数计算核密度估计Gaussian Kernel Density Estimation(KDE) & 六边形分箱图

最近需要可视化统计一个二维变量的数量分布图,网上搜了一下大概有一下两种方法:

1. 核密度估计图,通过核函数来进行密度估计

2. 六边形分箱图(Hexagonal Binning),直接计算不同bin中的点的数量

首先来说一下核密度估计图

具体什么是核密度估计建议大家去B站搜索一下,讲的很清楚,大概就是需要一个核函数,对每一个样本点进行叠加,最后再归一化的一个过程,这个过程和带宽有很大的关系。

我主要用Python的scipy.stats.gaussian_kde函数和matplotlib进行了可视化

具体代码网上很多,这里我又加了一些注释,算是给入门的看的吧:

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import gaussian_kde

x=data1#第一维数据
y=data2#第二维数据
xy = np.vstack([x,y])#将两个维度的数据进行叠加
kenal = gaussian_kde(xy)#这一步根据xy这个样本数据,在全定义域上建立了概率密度分布,所以kenal其实就是一个概率密度函数,输入对应的(x,y)坐标,就给出相应的概率密度
#kenal.set_bandwidth(bw_method=kenal1.factor)#这一步可以直接设置bandwith,通常情况下默认即可
z = kenal.evaluate(xy)#得到我们每个样本点的概率密度
z = gaussian_kde(xy)(xy)#这行代码和上面两行是相同的意思,这行是一行的写法
idx = z.argsort() #对z值进行从小到大排序并返回索引
x, y, z = x[idx], y[idx], z[idx]#对x,y按照z的升序进行排列
#上面两行代码是为了使得z值越高的点,画在上面,不被那些z值低的点挡住,从美观的角度来说还是十分必要的
fig, ax = plt.subplots(figsize=(7,5),dpi=100)
scatter = ax.scatter(x,y,marker='o',c=z,edgecolors='none',s=25,label='label'
                     ,cmap='Spectral_r')
cbar_ax = plt.gcf().add_axes([0.93, 0.15, 0.02, 0.7])#[left,bottom,width,height] position
cbar = fig.colorbar(scatter, cax=cbar_ax, label='Probability density')
ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')
ax.set_title('title')

下面还有两点需要注意的地方:

1. 关于带宽,函数默认采用Scott's Rule:n**(-1./(d+4)),其中n是样本量,d是纬度,也就是样本量越大,维度越低,带宽越小,毕竟样本量多了,对应的高斯函数的方差应该是需要减小的,否则就会变得非常平滑,反之,如果样本量很小,再不用较大的方差,那么也就无法进行差值了。(感觉这里和地理统计中的差值非常像,样本量小,同时方差还很低,就会出现‘牛眼’现象)。

这里我其实并不明白带宽和高斯函数标准差之间的关系,所以我自己带入高斯函数公式推导了一下:

f_h(x)=\frac{1}{n}\times \sum_{i=1}^{n}\phi _h(x-x_i)=\frac{1}{nh}\times\sum_{i=1}^{n}\phi (\frac{x-x_i}{h})=\frac{1}{n}\times \sum_{i=1}^{n}(\frac{1}{\sqrt{2\pi }\sigma h}e^\frac{-(x-x_i)^2}{2\sigma ^2h^2})

可以看出来其实真实的高斯函数标准差是根据样本标准差(\sigma)和带宽(h)相乘得到的,所以带宽越宽,得到的核密度分布越平滑。

2. 函数的返回值是概率密度,这个概率密度对定义域进行积分其实就是1,从上面的公式也可以看出来。

至于六边形分箱图我还没有发现有多好看,好处可能是对数据统计更加准确,毕竟是一个客观统计值,没有概率这一说了,但是还是觉得KDE比较好,大家想了解的就去matplotlib官网看看吧:

Hexbin演示 — Matplotlib 3.3.3 文档

https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hexbin.html

#------------210531-------------------

在做一维核密度分布画图的时候,可能我们的样本权重并不是完全相同,如果想加入权重项并画图,就不能使用sns.kdeplot,虽然这个函数提供了一个选项weights,但是我把权重放进去就会告诉我:AttributeError: 'Line2D' object has no property 'weights';我理解的是只有画二维核密度分布才可以使用,那么如果是一维核密度分布加权重该怎么办呢,想了一下还是得自己写函数来画了...下面是代码:

import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import gaussian_kde

#----画-1 to 1 之前标准正态分布的核密度图
xx = np.zeros(1000)
xx[500:] = np.random.uniform(low=0.0, high=1.0, size=500)
kenal1 = gaussian_kde(np.sort(x),weights=xx)#加权重
kenal2 = gaussian_kde(x)#不加权重
x0 = np.arange(-1,1,0.01)

#-------------加权重
fig = plt.figure(figsize=(15,6)) ##width, height default(8,6)
ax = fig.add_subplot(1,3,1)
z = kenal1.evaluate(x0)#
ax.plot(x0,z)
ax.set_ylim(0,0.5)
ax.set_xlim(-1,1)

ax = fig.add_subplot(1,3,2)
z = kenal2.evaluate(x0)#
ax.plot(x0,z)
ax.set_ylim(0,0.5)
ax.set_xlim(-1,1)

ax = fig.add_subplot(1,3,3)
sns.kdeplot(x)
ax.set_xlim(-1,1)
ax.set_ylim(0, 0.5)

可以看出来加了权重之后,核密度向右边移动了,主要是因为我们把前100个小值的权重设置为了0,而后两个则没有区别(有细微区别,可能是函数default的参数不一样,比如带宽啥的)

  • 14
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个基于高斯核函数的时空核密度估计Python 代码示例: ```python import numpy as np from sklearn.neighbors import KernelDensity # 生成一些样本数据,包括时间、经度和纬度 # 在这个例子中,我们生成了100个样本点 # 时间范围是0到23小时,经度范围是-180到180度,纬度范围是-90到90度 np.random.seed(0) sample_size = 100 times = np.random.randint(0, 24, size=sample_size) longitudes = np.random.uniform(-180, 180, size=sample_size) latitudes = np.random.uniform(-90, 90, size=sample_size) data = np.vstack([times, longitudes, latitudes]).T # 设定核函数的带宽和核函数类型 bandwidth = 0.1 kernel = 'gaussian' # 使用 KernelDensity 进行时空核密度估计 kde = KernelDensity(bandwidth=bandwidth, kernel=kernel) kde.fit(data) # 生成一些测试数据,用于根据时空核密度估计模型计算密度值 # 在这个例子中,我们生成了100个测试点,时间、经度和纬度的范围与样本数据相同 test_times = np.random.randint(0, 24, size=sample_size) test_longitudes = np.random.uniform(-180, 180, size=sample_size) test_latitudes = np.random.uniform(-90, 90, size=sample_size) test_data = np.vstack([test_times, test_longitudes, test_latitudes]).T # 根据时空核密度估计模型计算密度值 log_densities = kde.score_samples(test_data) # 输出结果 print(log_densities) ``` 注意,这里使用的是 sklearn 中的 KernelDensity 类,它支持多种核函数类型,包括高斯核函数。当然,你也可以自己编写高斯核函数的时空核密度估计代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值