Matplotlib 高效计算不规则点密度的方法
在数据可视化中,点密度图是最常用的图形之一。然而,当数据点分布不均匀时,传统的点密度估计方法就会变得非常耗时,影响图表生成的效率,甚至无法正常生成图表。本文将介绍一种 Matplotlib 高效计算不规则点密度的方法,帮助我们在处理大量数据时,快速生成高效的密度图。
阅读更多:Matplotlib 教程
初识点密度图
什么是点密度图?
点密度图是一种通过在二维空间里绘制点来响应密度的图表,它将大量的点汇聚成一个图形来展示随机变量的走向和分布情况。点密度图能够很好的解决数据点重叠、数据分布不均、数据量较大等问题。
传统的点密度图生成方法
在 Matplotlib 中,我们可以使用其 hexbin() 方法或 scatter() 方法结合 matplotlib.colors 库中的 ListedColormap() 对象来生成点密度图。
hexbin() 方法能够自动划分 bins 区间,并将每个 bin 区间内的点的数量转化为颜色的深浅,并将其绘制到平面坐标中。
import matplotlib.pyplot as plt
import numpy as np
x, y = np.random.normal(size=(2, 20000))
plt.hexbin(x, y, gridsize=40, cmap='Blues')
plt.show()
scatter() 方法结合 ListedColormap() 对象,将点的颜色与点所在区间内的点的数量所对应的颜色关联起来,从而生成点密度图。
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import ListedColormap
x, y = np.random.normal(size=(2, 20000))
hb = plt.hexbin(x, y, gridsize=40, cmap='Blues')
cmap = ListedColormap(['white', 'cyan', 'lime', 'yellow', 'red'])
plt.scatter(x, y, c=hb.get_array(), cmap=cmap)
plt.colorbar(hb)
plt.show()
这两种方法都能够很好的进行点密度估计,但是当数据点数量较大、越来越不规则时就会变得非常慢,并且在高维数据的情况下就会面临维度灾难。
Matplotlib高效计算不规则点密度的方法
Matplotlib 小知识
在介绍本文重点内容之前,先做一个小小的提醒。在进行数据可视化的实践当中,Matplotlib 的一些小技巧能够帮助我们大幅度提高绘图效率,例如:
- 避免重复创建子图,使用 subplot2grid() 方法统一管理子图。
- 使用类似 set_xscale() 之类的方法,来快速设置轴刻度。
- 保留坐标轴、删除顶、右边框等。
- 使用 set_yticklabels() 隐藏 Y 轴坐标标签 & 使用 text() 将坐标轴名称垂直显示。
具体实践可以参考代码:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import ListedColormap
x, y = np.random.normal(size=(2, 20000))
hb = plt.hexbin(x, y, grid### 高效计算不规则点密度
在传统的点密度估计方法中,通常是将二维空间划分为一个一个的小方格,然后统计每个方格中数据点的数量。但是当数据点分布不均匀、不规则时,就会出现方格大小不一、方格间重叠等问题,从而导致点密度的计算变得极为繁琐且效率低下。
在解决这个问题的过程中,我们可以利用 KDE 方法。KDE(Kernel Density Estimation)又称核密度估计,是一种用来估计密度函数的非参数方法。其基本思想是在数据点的每个位置上摆上一个核函数,并对所有核函数进行平滑处理,最后得到一条连续的曲线,表示数据点的密度分布情况。
在 Matplotlib 中,我们可以使用 scipy.stats.gaussian_kde() 函数进行核密度估计,并利用 evaluate() 方法计算出每个位置的核密度值,从而得到点密度图。
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
x, y = np.random.normal(size=(2, 20000))
xy = np.vstack([x, y])
z = gaussian_kde(xy)(xy) # 通过gaussian_kde函数计算核密度估计值
plt.scatter(x, y, c=z, s=50, edgecolor='')
plt.show()
如上图所示,我们使用 gaussian_kde() 函数估计数据点的密度,并将其赋值给变量 z,最后使用 scatter() 方法结合 z 变量来绘制点密度图,并使用 s 参数来控制点的大小。
加速核密度估计
虽然我们已经成功地解决了密度计算不规则点密度图的问题,但是在实际使用中,当数据点数量非常大时,计算点密度图的效率还是会变得比较低。此时,我们可以使用几种方法来加速核密度估计。
1. Numba 加速
Numba 是一个基于 LLVM 的开源 JIT 编译器,可将 Python/NumPy 代码加速数十倍。下面是使用 Numba 加速核密度估计的示例代码:
import matplotlib.pyplot as plt
import numpy as np
from numba import jit
from scipy.stats import gaussian_kde
@jit(nopython=True) # 使用 Numba,加速计算效率
def kde_scipy(x, y, nbins=200j, r=0.25):
X, Y = np.mgrid[1.1 * x.min():1.1 * x.max():nbins,
1.1 * y.min():1.1 * y.max():nbins]
positions = np.vstack([X.ravel(), Y.ravel()])
values = np.vstack([x, y])
kernel = gaussian_kde(values, bw_method='silverman')
Z = np.reshape(kernel(positions).T, X.shape)
idxs = np.where(Z > Z.max() * r)
return X[idxs], Y[idxs], Z[idxs]
x, y = np.random.normal(size=(2, 20000))
x, y, z = kde_scipy(x, y)
plt.scatter(x, y, c=z, s=50, edgecolor='')
plt.show()
经过 Numba 加速后,程序的速度得到了显著的提升。
2. 直接利用灰度图
除了使用 Numba 加速,我们还可以直接计算出灰度图,并利用 Matplotlib 的 imshow() 和 interpolation='nearest' 参数来直接绘制点密度图。
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
x, y = np.random.normal(size=(2, 20000))
xy = np.vstack([x, y])
z = gaussian_kde(xy)(xy)
_, _,z_bins = np.histogram2d(x, y, bins=100)[0]
plt.imshow(
z_bins,
cmap='Blues',
interpolation='nearest',
origin='lower',
extent=[x.min(), x.max(), y.min(), y.max()],
)
plt.show()
在以上代码中,我们使用 histogram2d() 方法来计算出灰度图,并且将 imshow() 方法的 interpolation='nearest' 参数设置为最邻近插值,从而得到了清晰的点密度图。
3. Pandas 加速
如果我们正在处理的数据是一个 Pandas 数据框,那么我们可以利用 Pandas 对数据框的高效计算能力来进一步加速核密度估计。下面是一个 Pandas 加速的核密度估计代码示例:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np
df = pd.DataFrame(np.random.normal(size=(20000, 2)), columns=['x', 'y'])
xmin, ymin = df.min(axis=0)
xmax, ymax = df.max(axis=0)
nbins = 300
f, ax = plt.subplots()
hb = ax.hexbin(
df['x'],
df['y'],
gridsize=nbins,
cmap='Blues',
norm=colors.LogNorm(),
alpha=0.5,
linewidths=0.2,
)
plt.colorbar(hb)
plt.show()
使用 Pandas 来加速核密度估计的效果也是非常明显的。
总结
本文主要介绍了 Matplotlib 高效计算不规则点密度的方法。首先我们对点密度图进行了简单的介绍,并且讲解了传统方法的不足之处。接着我们介绍了核密度估计的基本原理,并通过代码示例演示了如何使用 gaussian_kde() 函数来进行核密度估计。最后,我们还介绍了加速计算的几种方法,包括使用 Numba 加速、直接利用灰度图和 Pandas 加速。
极客教程