如何归一化由2D直方图生成的热力图?
当然可以搞定这个问题!你想让热力图反映分布而非样本量带来的频率差异,核心就是把绝对计数转换成相对密度或者做统一的归一化处理。下面给你几种实用的方法,一步步来:
方法1:直接用内置的密度归一化(最省心)
如果你用的是Matplotlib的hist2d函数,它自带了density=True参数——这个参数会自动把每个bin的绝对计数转换成概率密度,也就是计数除以(总样本数×每个bin的面积)。这样一来,不管两组数据的样本量差多少,热力图的颜色都只反映数据的分布密度,完全消除了样本量的影响。
举个实现代码的例子:
import matplotlib.pyplot as plt import numpy as np # 生成模拟数据(模拟你的x1-y1和x2-y2样本量差异) np.random.seed(42) x1, y1 = np.random.multivariate_normal([0,0], [[1,0.5],[0.5,1]], 10000).T x2, y2 = np.random.multivariate_normal([0,0], [[1,0.5],[0.5,1]], 1000).T # 绘制归一化后的热力图 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) # 第一组数据(10k样本) hist1, xedges, yedges, im1 = ax1.hist2d(x1, y1, bins=30, density=True, cmap='viridis') ax1.set_title('x1-y1 (10k samples, density normalized)') plt.colorbar(im1, ax=ax1, label='Probability Density') # 第二组数据(1k样本) hist2, _, _, im2 = ax2.hist2d(x2, y2, bins=30, density=True, cmap='viridis') ax2.set_title('x2-y2 (1k samples, density normalized)') plt.colorbar(im2, ax=ax2, label='Probability Density') plt.tight_layout() plt.show()
这样生成的两张图,颜色刻度完全对应相同的密度水平,你能直接对比两组数据的分布模式,不会被样本量干扰。
方法2:统一全局归一化到0-1范围(跨图对比更直观)
如果你希望所有热力图都用同一个0-1的颜色刻度(0代表最低密度,1代表所有数据集中的最大密度),可以先计算所有数据集的最大密度值,再统一设置vmin和vmax:
# 先提前计算所有数据的密度值 hist1, _, _ = np.histogram2d(x1, y1, bins=30, density=True) hist2, _, _ = np.histogram2d(x2, y2, bins=30, density=True) # 找出所有数据中的最大密度,作为全局参考 global_max_density = max(hist1.max(), hist2.max()) # 绘图时统一使用这个全局最大值作为vmax fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) im1 = ax1.hist2d(x1, y1, bins=30, density=True, cmap='viridis', vmin=0, vmax=global_max_density) ax1.set_title('x1-y1 (normalized to global max density)') plt.colorbar(im1[3], ax=ax1, label='Normalized Density (0-1)') im2 = ax2.hist2d(x2, y2, bins=30, density=True, cmap='viridis', vmin=0, vmax=global_max_density) ax2.set_title('x2-y2 (normalized to global max density)') plt.colorbar(im2[3], ax=ax2, label='Normalized Density (0-1)') plt.tight_layout() plt.show()
这种方法的好处是,所有图的颜色标尺完全一致,能一眼看出哪组数据的某些区域密度相对更高。
方法3:单图内部归一化(聚焦自身分布)
如果你的需求是让每个热力图自己的最大密度对应1,最小对应0(只看单组数据内部的相对分布),可以手动计算每个bin的计数,然后除以该组数据的最大计数:
# 先计算每组数据的绝对计数直方图 counts1, xedges, yedges = np.histogram2d(x1, y1, bins=30) counts2, _, _ = np.histogram2d(x2, y2, bins=30) # 内部归一化到0-1范围 norm_counts1 = counts1 / counts1.max() norm_counts2 = counts2 / counts2.max() # 用pcolormesh绘制归一化后的矩阵 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) im1 = ax1.pcolormesh(xedges, yedges, norm_counts1.T, cmap='viridis', vmin=0, vmax=1) ax1.set_title('x1-y1 (self-normalized to 0-1)') plt.colorbar(im1, ax=ax1, label='Relative Frequency') im2 = ax2.pcolormesh(xedges, yedges, norm_counts2.T, cmap='viridis', vmin=0, vmax=1) ax2.set_title('x2-y2 (self-normalized to 0-1)') plt.colorbar(im2, ax=ax2, label='Relative Frequency') plt.tight_layout() plt.show()
这种方式适合单独分析每组数据的内部分布强度,但跨图对比时要注意,因为各自的“1”代表的绝对计数不一样。
总结一下:
- 优先用
density=True,它是最直接消除样本量影响的方式,让热力图反映真实分布密度; - 要跨图统一对比,就用全局最大密度作为参考设置
vmin/vmax; - 只看单组内部相对分布,就手动做内部归一化。
内容的提问来源于stack exchange,提问作者user9639519
相关产品推荐
相关产品推荐

