Python中数据子集与全集高斯分布密度不一致问题求助
问题描述
我尝试为数据绘制密度分布图,现有数据框:
df = pd.read_csv('des_y3.csv')
通过以下代码计算全集密度:
from scipy.stats import gaussian_kde # Extract x and y x = df['column_1'].to_numpy() y = df['column_2'].to_numpy() xx, yy = np.mgrid[min(x):max(x):100j, min(y):max(y):100j] positions = np.vstack([xx.ravel(), yy.ravel()]) values = np.vstack([x, y]) kernel = gaussian_kde(values) f_1 = np.reshape(kernel(positions).T, xx.shape)
这部分运行正常,但筛选column_3等于'E'的子集计算密度时:
x = df['column_1'][df['column_3'] == 'E'].to_numpy() y = df['column_2'][df['column_3'] == 'E'].to_numpy() xx, yy = np.mgrid[min(x):max(x):100j, min(y):max(y):100j] positions = np.vstack([xx.ravel(), yy.ravel()]) values = np.vstack([x, y]) kernel = gaussian_kde(values) f_2 = np.reshape(kernel(positions).T, xx.shape)
发现子集数据量更少,但f_2的数值反而比f_1大。我需要让子集的密度与原始数据保持一致,同时绘图时子集与全集使用相同的配色方案和等高线水平,当前绘图代码如下:
# Create subplots for the first two components fig, ax = plt.subplots(1, 2) cfset = ax[0].contourf(xx, yy, f, cmap='coolwarm') ax[0].imshow(np.rot90(f), cmap='coolwarm', extent=[xmin, xmax, ymin, ymax]) cset0 = ax[0].contour(xx, yy, f, colors='k') ax[0].set_xlabel('X') ax[0].set_ylabel('Y') ax[0].set_title('All data') cfset = ax[1].contourf(xx, yy, f0, cmap='coolwarm') ax[1].imshow(np.rot90(f0), cmap='coolwarm', extent=[xmin, xmax, ymin, ymax]) cset = ax[1].contour(xx, yy, f0, cset0.levels, colors='k') ax[1].set_xlim(xmin, xmax) ax[1].set_ylim(ymin, ymax) ax[1].set_xlabel('X') ax[1].set_ylabel('Y') ax[1].set_title('Subset') plt.show()
请问该如何解决?
解决方案
1. 修正密度数值不一致的问题
gaussian_kde默认会将密度归一化到当前数据集的分布,即子集和全集的密度积分都为1。子集数据更集中,归一化后峰值自然更高。要让子集密度与全集保持同一量级,需按子集在全集中的占比缩放:
- 计算子集样本量占比:
subset_ratio = len(df[df['column_3'] == 'E']) / len(df) - 用该比例缩放子集密度:
f_2_scaled = f_2 * subset_ratio
另外,计算网格xx, yy时必须使用全集的x、y范围,否则坐标区间不一致会影响视觉对比:
# 改用全集的x、y范围生成网格 x_full = df['column_1'].to_numpy() y_full = df['column_2'].to_numpy() xx, yy = np.mgrid[min(x_full):max(x_full):100j, min(y_full):max(y_full):100j]
2. 统一配色与等高线水平
- 等高线部分已通过
cset0.levels复用全集水平,只需确保缩放后的子集密度能匹配这些层级。 contourf默认会根据当前数据极值自动调整色标范围,需手动指定vmin和vmax为全集密度的极值,实现配色统一:
# 获取全集密度的极值 f_min = f_1.min() f_max = f_1.max() # 绘图时指定统一的色标范围 cfset = ax[0].contourf(xx, yy, f_1, cmap='coolwarm', vmin=f_min, vmax=f_max) cfset = ax[1].contourf(xx, yy, f_2_scaled, cmap='coolwarm', vmin=f_min, vmax=f_max)
注意:contourf和imshow都是绘制填充密度图,重复使用会导致图层重叠,建议只保留其中一个。
完整修正代码示例
import pandas as pd import numpy as np from scipy.stats import gaussian_kde import matplotlib.pyplot as plt # 加载数据 df = pd.read_csv('des_y3.csv') # 计算全集密度 x_full = df['column_1'].to_numpy() y_full = df['column_2'].to_numpy() xx, yy = np.mgrid[min(x_full):max(x_full):100j, min(y_full):max(y_full):100j] positions = np.vstack([xx.ravel(), yy.ravel()]) values_full = np.vstack([x_full, y_full]) kernel_full = gaussian_kde(values_full) f_1 = np.reshape(kernel_full(positions).T, xx.shape) # 计算子集密度并缩放 subset_df = df[df['column_3'] == 'E'] x_sub = subset_df['column_1'].to_numpy() y_sub = subset_df['column_2'].to_numpy() values_sub = np.vstack([x_sub, y_sub]) kernel_sub = gaussian_kde(values_sub) f_2 = np.reshape(kernel_sub(positions).T, xx.shape) # 按子集占比缩放密度 subset_ratio = len(subset_df) / len(df) f_2_scaled = f_2 * subset_ratio # 绘图 fig, ax = plt.subplots(1, 2, figsize=(12, 5)) # 全集绘图 f_min = f_1.min() f_max = f_1.max() cfset0 = ax[0].contourf(xx, yy, f_1, cmap='coolwarm', vmin=f_min, vmax=f_max) cset0 = ax[0].contour(xx, yy, f_1, colors='k') ax[0].set_xlabel('X') ax[0].set_ylabel('Y') ax[0].set_title('All data') # 子集绘图 cfset1 = ax[1].contourf(xx, yy, f_2_scaled, cmap='coolwarm', vmin=f_min, vmax=f_max) cset1 = ax[1].contour(xx, yy, f_2_scaled, cset0.levels, colors='k') ax[1].set_xlim(min(x_full), max(x_full)) ax[1].set_ylim(min(y_full), max(y_full)) ax[1].set_xlabel('X') ax[1].set_ylabel('Y') ax[1].set_title('Subset (scaled)') # 添加统一色条 fig.colorbar(cfset0, ax=ax) plt.tight_layout() plt.show()
内容的提问来源于stack exchange,提问作者SGT
相关产品推荐
相关产品推荐

