在科研作图中,读者一边想看特征之间的相关性,另一边又想知道每个特征对分类任务到底有多重要。如果把这两部分内容分成两张图来画,读者往往需要来回切换视线,阅读体验并不理想。因此本文把 PCC 热力图和 IGR 条形图拼接在同一张图里,让“特征之间的关系”和“特征本身的重要性”同时呈现出来。


科研绘图的第一步,不是马上画图,而是先把图形风格统一好。这样后面不管生成几张图,字号、字体、清晰度都会保持一致。脚本里专门写了一个 fs() 函数来统一控制字号缩放,同时通过 plt.rcParams.update() 一次性设置字体、分辨率和坐标轴参数。
FONT_SCALE = 1.3
deffs(size):
return size * FONT_SCALE
plt.rcParams.update({
'font.family': 'serif',
'font.serif': ['Times New Roman'],
'font.size': fs(10),
'axes.labelsize': fs(10),
'xtick.labelsize': fs(8),
'ytick.labelsize': fs(8),
'figure.dpi': 600,
'savefig.dpi': 600,
'axes.unicode_minus': False,
})
读取一个二分类样本 CSV 文件,如果数据里存在 label 列,就把它当作分类标签;其余列作为特征。这个处理方式很实用,因为 PCC 和 IGR 的输入并不完全一样:前者只需要特征矩阵,后者还需要标签列。
df = pd.read_csv(csv_file, encoding='utf-8')
if'label'in df.columns:
labels = df['label']
features = df.drop(['label'], axis=1)
else:
labels = None
features = df
features 负责“特征和特征之间是什么关系”,labels 负责“特征对任务结果有没有帮助”。。
PCC 就是皮尔逊相关系数,它的作用是衡量两个变量之间有没有线性相关关系。值越接近 1,说明正相关越强;越接近 -1,说明负相关越强;接近 0 则说明线性关系比较弱:
correlation_matrix = features.corr(method='pearson')
correlation_matrix.to_csv(
os.path.join(output_dir, 'correlation_matrix_data.csv')
)
虽然代码只有一行,但在做特征筛选时,如果发现两个变量高度相关,就要注意特征信息冗余问题。通过手动用矩形格子去画右上三角区域,这样做的好处是布局更灵活,也方便和右侧的 IGR 柱状图严丝合缝地拼接起来。
如果说 PCC 是看“特征和特征”的关系,那么 IGR 看的是“特征和标签”的关系。先计算信息熵,再计算信息增益比。这里的核心思想可以简单理解为:一个特征如果能很好地区分不同类别,那么它的 IGR 往往就更高。
defcalculate_entropy(data):
_, counts = np.unique(data, return_counts=True)
probabilities = counts / len(data)
return -np.sum(probabilities * np.log2(probabilities))
defcalculate_information_gain_ratio(labels, feature, entropy_labels):
unique_values, value_counts = np.unique(feature, return_counts=True)
information_gain = 0
split_info = 0
for value, count in zip(unique_values, value_counts):
subset_labels = labels[feature == value]
entropy_subset = calculate_entropy(subset_labels)
information_gain += (count / len(feature)) * entropy_subset
split_info -= (count / len(feature)) * np.log2(count / len(feature))
information_gain = entropy_labels - information_gain
return information_gain / split_info if split_info != 0else0
IGR 越大,说明这个特征越能帮助模型区分类别。脚本算完以后,把每个特征的 IGR 值保存成表格,再在图中画成横向柱状图,这样一眼就能看出“谁更重要”。
这不是简单地画两张图,而是用 GridSpec 把两部分内容合并成一个整体。左边是 PCC 热力图,右边是 IGR 条形图,而且右侧柱子的顺序和左侧特征名称完全对齐,这样理解图会非常顺畅。
from matplotlib.gridspec import GridSpec
gs = GridSpec(
nrows=1, ncols=2,
width_ratios=[label_w + heatmap_w, igr_w],
wspace=0.0
)
ax_heat = fig.add_subplot(gs[0])
ax_igr = fig.add_subplot(gs[1])
后面的实现也很细致。热力图部分用 Rectangle 逐格绘制,并根据背景亮度自动切换白字或黑字;柱状图部分则用 barh() 横向绘制,和左侧热力图共享同一组特征顺序。
这次优化的重点在右侧柱状图。原来的做法是直接复用 PCC 的发散色带,但 IGR 本质上是非负值指标,更适合使用“由浅到深”的顺序渐变色。现在脚本专门为 IGR 做了一个独立配色函数,还额外把最大值用珊瑚色高亮出来,让重点更明确。
defget_igr_bar_colors(igr_values):
igr_values = np.asarray(igr_values, dtype=float)
max_igr = max(float(np.max(igr_values)), 1e-9)
igr_norm = Normalize(vmin=0, vmax=max_igr)
igr_cmap = sns.blend_palette(
['#edf6f9', '#cfe8df', '#7cc5b7', '#2a9d8f', '#1d4e89'],
as_cmap=True
)
colors = [igr_cmap(0.25 + 0.70 * igr_norm(v)) for v in igr_values]
colors[int(np.argmax(igr_values))] = '#e76f51'
return colors
对应的柱状图代码也做了同步优化,比如浅色边框、柔和背景、虚线网格和重点数值高亮:
bar_colors = get_igr_bar_colors(igr_values)
bars = ax_igr.barh(
y_pos,
igr_values,
height=0.65,
color=bar_colors,
edgecolor='#f8fafc',
linewidth=0.8,
zorder=3
)
ax_igr.set_facecolor('#f8fbfb')
ax_igr.xaxis.grid(True, linestyle=(0, (3, 3)), linewidth=0.6)
本文传达了一个很实用的观念:配色不只是“好不好看”,更关系到信息表达是否清晰。发散色适合表示正负变化,顺序色适合表示大小递增,把颜色类型和数据性质对应起来,图的质量会明显提升。
上述完成了三件事情:第一,计算 PCC 和 IGR 两类指标;第二,把两种分析结果整合到一张图上;第三,通过配色和布局优化提升图形的可读性。这种“分析 + 可视化 + 美化”的完整思路,非常适合科研工作中的特征分析场景。
现在绘图代码都不支持免费获取了,20/篇文章。同时欢迎加入小编科研绘图VIP群,268/年,所有科研绘图相关文章代码免费获取,涵盖机器学习模型(回归和分类)的shap分析、还有各种如皮尔逊分析等相关的图,以及期刊复现图,源代码直接复制或者打开就能绘图。同时进群赠送SHAP科研分析软件6.0(文本版),且免费更新使用。VX:GISyanjiushengya!!