如何对两个KDE图做差值运算并展示差异热图?
问题
如何对两个不同的kdeplot做差值运算并展示差异?如图右上角所示,绿色气泡与红色气泡存在重叠区域,希望将两个数据集相减后展示差异——比如当某位置绿色数据集的密度数值大于红色时,该位置显示绿色。
用户原代码如下:
import matplotlib.pyplot as plt from PIL import Image import seaborn as sns from functions.getMinimapExtent import getMinimapExtent from functions.getMinimapPath import getMinimapPath def getHeatmap(x, y, map:str, title:str, x1, y1): #x ~= [2054, 4225, 4234, 2134, 3552, 5632, ...] #y ~= [1452, 2354, 6423, 3256, 2341, 5532, ...] #x1 ~= [5323, 2535, 1535, 6433, 2356, 2353, ...] #y1 ~= [6435, 2212, 3555, 2366, 7312, 1643, ...] img = Image.open(getMinimapPath(map)) minimapExtent = getMinimapExtent(map) plt.imshow(img, extent=minimapExtent) # set seaborn style sns.set_style("white") # Custom the color, add shade and bandwidth sns.kdeplot(x=x, y=y, cmap="Reds", shade=True, bw_adjust=.2, alpha=.6, thresh=0.25, zorder=-1) sns.kdeplot(x=x1, y=y1, cmap="Greens", shade=True, bw_adjust=.2, alpha=.6, thresh=0.25, zorder=-1) plt.title(title) plt.axis("off") plt.savefig("bot/data/img/heatmap.png", dpi=500, bbox_inches="tight") plt.close()

解决方案
要实现两个KDE密度的差值可视化,需要手动计算两个数据集的核密度估计值,再对网格上的密度值做差值运算,最后根据差值结果绘制差异区域。步骤如下:
- 补充依赖工具:新增
scipy.stats.gaussian_kde用于计算核密度,numpy生成对齐地图的网格点。 - 生成匹配底图的网格:根据地图的范围参数,生成均匀分布的x、y网格,确保坐标和底图完全对齐。
- 分别计算两个数据集的KDE密度:用和原代码一致的平滑度参数,计算每个网格点的密度值。
- 计算密度差值:用绿色数据集的密度减去红色数据集的密度,得到差值矩阵。
- 绘制目标差异区域:筛选出绿色密度大于红色的区域(差值>0),用绿色填充展示。
修改后的代码如下:
import matplotlib.pyplot as plt from PIL import Image import seaborn as sns import numpy as np from scipy.stats import gaussian_kde from functions.getMinimapExtent import getMinimapExtent from functions.getMinimapPath import getMinimapPath def getHeatmap(x, y, map:str, title:str, x1, y1): # 加载底图和范围 img = Image.open(getMinimapPath(map)) minimapExtent = getMinimapExtent(map) x_min, x_max, y_min, y_max = minimapExtent plt.imshow(img, extent=minimapExtent) sns.set_style("white") # -------------------------- # 计算两个数据集的KDE密度差值 # -------------------------- # 生成网格点(bins值越大精度越高,计算速度越慢,可按需调整) bins = 200 xx, yy = np.meshgrid(np.linspace(x_min, x_max, bins), np.linspace(y_min, y_max, bins)) grid_points = np.vstack([xx.ravel(), yy.ravel()]) # 计算红色数据集的KDE密度,bw_method对应原代码的bw_adjust kde_red = gaussian_kde(np.vstack([x, y]), bw_method=0.2) density_red = kde_red(grid_points).reshape(xx.shape) # 计算绿色数据集的KDE密度 kde_green = gaussian_kde(np.vstack([x1, y1]), bw_method=0.2) density_green = kde_green(grid_points).reshape(xx.shape) # 计算密度差值:绿色 - 红色 density_diff = density_green - density_red # -------------------------- # 绘制差异区域 # -------------------------- # 只显示绿色密度大于红色的区域,用绿色填充 plt.contourf(xx, yy, np.where(density_diff > 0, density_diff, np.nan), cmap="Greens", alpha=0.6, zorder=-1) # 可选:若需要同时展示红色密度更高的区域,取消下面注释 # plt.contourf(xx, yy, np.where(density_diff < 0, -density_diff, np.nan), # cmap="Reds", alpha=0.6, zorder=-1) plt.title(title) plt.axis("off") plt.savefig("bot/data/img/heatmap.png", dpi=500, bbox_inches="tight") plt.close()
关键细节说明
bw_method=0.2和原代码的bw_adjust=.2效果一致,保证KDE的平滑程度不变。bins参数控制网格精度,数值越大细节越丰富,但会增加计算耗时,可根据地图尺寸调整。np.where用于筛选出绿色密度更高的区域,只保留有效差值部分,避免无意义的重叠显示。zorder=-1确保差异图层在底图下方,不会遮挡地图内容。
内容的提问来源于stack exchange,提问作者monkaCode
相关产品推荐
相关产品推荐

