如何在Pandas中使用Seaborn实现数据着色的归一化
问题原因
你当前使用的是默认线性颜色映射规则,程序会自动取数据的最小值(0)和最大值作为色阶的两端,由于其余数值和0的差距极大,导致大部分数值都对应色阶的浅红色区间,颜色区分度极低,就是你看到的图1效果。
解决方案
自定义颜色归一化规则即可,推荐两种适配不同需求的实现方式:
方案1:截断极小值,适配0值无业务意义的场景
将0值排除在有效着色区间外,低于有效阈值的数值统一显示为最浅色,其余数值做线性映射,修改my_gradient函数即可:
import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from matplotlib import colors index = pd.MultiIndex.from_product([[2019, 2020], [1, 2]], names=['year', 'visit']) columns = pd.MultiIndex.from_product([['Group1', 'Group2', 'Group3'], ['value1', 'value2']], names=['subject', 'type']) data = np.round(np.random.randn(4, 6), 1) data[:, ::2] *= 20 data += 50 # 模拟你提到的0值场景 data[0,0] = 0 rdata = pd.DataFrame(data, index=index, columns=columns) cc = sns.light_palette("red", as_cmap=True) cc.set_bad('white') def my_gradient(s, cmap): s_clean = s.replace(np.inf, np.nan) valid_vals = s_clean.dropna() # 排除0值设置最小色阶阈值,也可以手动固定为你需要的数值比如10 vmin = valid_vals[valid_vals > 0].min() # 因为你的数据范围固定是0~100,直接固定vmax为100更稳定 vmax = 100 norm = colors.Normalize(vmin=vmin, vmax=vmax) return [f'background-color: {colors.rgb2hex(x)}' for x in cmap(norm(s_clean))] styler = rdata.style red = styler.apply( my_gradient, cmap=cc, subset=rdata.columns.get_loc_level('value1', level=1)[0], axis=0) styler
方案2:非线性归一化,适配需要保留0值着色差异的场景
使用幂律归一化PowerNorm,调整gamma参数压缩高值区间、拉高低值区间的区分度,0值也会和其他低值有明显的颜色差异:
只需要修改my_gradient函数内的归一化逻辑即可:
def my_gradient(s, cmap): s_clean = s.replace(np.inf, np.nan) # gamma越小,低数值的颜色区分度越高,可根据实际效果调整 norm = colors.PowerNorm(gamma=0.5, vmin=0, vmax=100) return [f'background-color: {colors.rgb2hex(x)}' for x in cmap(norm(s_clean))]
效果参考
| 原图效果(图1) | 优化后效果(图2) |
|---|---|
![]() | ![]() |
内容的提问来源于stack exchange,提问作者project.py
相关产品推荐
相关产品推荐



