QZQ的小世界!

  • 首页
你好!
这里是QZQ的博客站!
  1. 首页
  2. 未分类
  3. 正文

Python 实现二维图的高斯核密度估计,从点到热力图

2025年1月26日 317点热度 1人点赞 0条评论

前言

之前去深圳慢病院搞研究,搞了一年多还欠着一篇论文…估计是很难还上了,因为寒假过后我也就半年的时间了,即将毕业成为社畜…

但是师兄还是给了一个 idea ,简单来说是将 vr 眼动的轨迹转化成热力图的形式,这样数据就从序列成为了图数据,提取特征理论上也确实更好提取一点,大概效果是这样的:

然后改成这样的形式:

这样确实可以清晰地描绘出眼动数据的注意区域~

分析这个研究不是本文要讨论的内容,本文主主要介绍如何实现出上两幅图片的转变。

核密度估计

理论

本人不太钻研理论,因此自己讲容易误人子弟,这里就引用的多一点:

https://www.bilibili.com/video/BV1ir4y1h7Pc/

如果你有一个柱状图,想用线来描绘出其趋势,那么首先需要解决的是:如何从点(离散的柱)转化成连续的线。

一个方法是,对于每一个点,我们都将其转化成一个正态分布的中心,再将这些正态分布联合起来

当然,也可以使用下述的别的分布,看诸位的需求。

这就是核密度估计的大概了,点转成线,理论上更高维也依然可以生效,二维的点可以转化成热力图,就如前言介绍的那样。

实现

我们使用的包是scipy.stats.gaussian_kde,这玩意可以实现二维的高斯核密度估计,而且用法相当简单,直接上代码:

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


# 定义测量模型函数
def measure(n):
    """返回两个相关联的测量值."""
    m1 = np.random.normal(size=n)  # 生成标准正态分布数据
    m2 = np.random.normal(scale=0.5, size=n)  # 生成标准差为0.5的正态分布数据
    return m1 + m2, m1 - m2  # 返回两个线性组合后的测量值


# 生成示例数据
m1, m2 = measure(2000)

# 计算数据的范围
xmin, xmax = m1.min(), m1.max()
ymin, ymax = m2.min(), m2.max()

# 执行核密度估计
X, Y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]  # 创建网格
positions = np.vstack([X.ravel(), Y.ravel()])  # 将网格位置展平并堆叠
values = np.vstack([m1, m2])
kernel = stats.gaussian_kde(values)  # 创建KDE对象
Z = np.reshape(kernel(positions).T, X.shape)  # 计算密度并重塑形状

# 可视化结果
fig, ax = plt.subplots()
ax.imshow(np.rot90(Z), cmap=plt.cm.gist_earth_r, extent=[xmin, xmax, ymin, ymax])  # 显示密度图
ax.plot(m1, m2, 'k.', markersize=2)  # 显示原始数据点
ax.set_xlim([xmin, xmax])  # 设置x轴范围
ax.set_ylim([ymin, ymax])  # 设置y轴范围
plt.show()  # 显示图形

 

详细介绍一下上述的代码,首先measure函数会为我们返回由两个正态分布产生的坐标点,对应二维图上的 x,y。这些点会被规范后直接进入我们KDE 类中进行运算:

values = np.vstack([m1, m2])
kernel = stats.gaussian_kde(values)  # 创建KDE对象

 

这两部实际上就完成了高斯核密度估计的操作,但是我们还需要绘制热力图,这就需要我们绘制一个网格。

网格是什么?其实是一系列密集的空间上的点,举个例子:

import numpy as np
import matplotlib.pyplot as plt

# 使用 numpy.mgrid 生成二维网格
x, y = np.mgrid[-2:2:1, -2:2:1]
print(x)
print(y)

 

x 长这样

[[-2 -2 -2 -2]
 [-1 -1 -1 -1]
 [ 0  0  0  0]
 [ 1  1  1  1]]

 

y 长这样

[[-2 -1  0  1]
 [-2 -1  0  1]
 [-2 -1  0  1]
 [-2 -1  0  1]]

 

对应的就是这片矩形区域中所有整数的坐标点。

网格可以进行很多二维运算,产生密度图等效果:

import numpy as np
import matplotlib.pyplot as plt

# 使用 numpy.mgrid 生成二维网格
x, y = np.mgrid[-2:2:1, -2:2:1]

# 计算网格上的某个函数值,例如 z = x^2 + y^2
z = x**2 + y**2

# 使用 Matplotlib 进行可视化
plt.figure(figsize=(8, 6))
plt.contourf(x, y, z, levels=50, cmap='viridis')  # 使用 contourf 填充等高线图
plt.colorbar(label='z value')  # 添加颜色条
plt.title('2D Grid Visualization with numpy.mgrid')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

 

效果如下:

话说回来,上述示例代码中的这些代码为我们提供了网格:

X, Y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]  # 创建网格,100j的意思是采样100个点
positions = np.vstack([X.ravel(), Y.ravel()])  # 将网格位置展平并堆叠

 

网格会进入运算完毕的 KDE 类中,为我们提供热力图:

Z = np.reshape(kernel(positions).T, X.shape)  # 计算密度并重塑形状

 

这个 Z 即为拥有密度信息的向量,将会被用于制图。

上述代码运行完成后效果如下:

参考

scipy.stats.gaussian_kde的官方文档,有现成的参考代码可以直接用:

https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.gaussian_kde.html

绘制热力图:

https://www.zhihu.com/question/362018090

这篇文章适合入门核密度估计,但是使用的包是sklearn.neighbors.KernelDensity,只适宜一维数组的处理:

https://zhuanlan.zhihu.com/p/671141343

 

本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2025年3月12日

QZQ

一只涉猎广泛的技术爱好者,绝赞养猫中~

点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

归档

  • 2025 年 4 月
  • 2025 年 3 月
  • 2025 年 2 月
  • 2025 年 1 月
  • 2024 年 12 月
  • 2024 年 11 月

分类

  • 技术
  • 未分类

COPYRIGHT © 2024 QZQ的小世界!. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang