使用scale_fill_stepsn分箱时如何避免数据重新缩放?
问题描述
我有一组连续数据,想用分箱颜色刻度做可视化。由于数据分布不均,我在数值区间的低端设置了更多断点,目的是突出低数值之间的差异。但scale_fill_stepsn在分箱过程中会自动重新缩放数据,导致颜色映射仅反映断点的相对位置,低数值间的差异难以清晰区分。我希望颜色能均匀分配到自定义的断点上,同时仍使用scale_fill_stepsn()函数,有没有可行方法?
我知道可以手动对数据分箱后以离散形式传入ggplot,但不想这么操作;同时也不想对数据进行任何变换(比如取对数)。
示例代码:
library(ggplot2) # 生成带异常值的样本数据 df <- expand.grid(x = 0:5, y = 0:5) df$z <- abs(rnorm(36)) df$z[[4]] <- 12 df$z[[8]] <- 17 df$z[[30]] <- 7 breaks=c(0, 0.25, 0.5, 1, 2, 5, 10, 20) ggplot(df) + geom_tile(aes(x=x, y=y, fill=z)) + scale_fill_stepsn(colors=terrain.colors(7), breaks=breaks)
当前生成的图表中,大部分色块为相近的绿色,低数值间的差异无法清晰分辨:
低数值差异模糊的ggplot热力图
解决方案
核心是通过自定义rescaler函数,让自定义断点在颜色映射时被当作等间距处理,规避scale_fill_stepsn默认的线性缩放逻辑。
只需在scale_fill_stepsn中添加rescaler = ~ as.numeric(cut(., breaks = breaks))参数,这样每个分箱会被赋予连续的整数序号,颜色就能均匀分配到每个自定义断点区间:
library(ggplot2) df <- expand.grid(x = 0:5, y = 0:5) df$z <- abs(rnorm(36)) df$z[[4]] <- 12 df$z[[8]] <- 17 df$z[[30]] <- 7 breaks=c(0, 0.25, 0.5, 1, 2, 5, 10, 20) ggplot(df) + geom_tile(aes(x=x, y=y, fill=z)) + scale_fill_stepsn( colors=terrain.colors(7), breaks=breaks, rescaler = ~ as.numeric(cut(., breaks = breaks)) # 关键自定义缩放逻辑 )
原理说明
- 默认
rescaler会将数据线性缩放到[0,1]区间,稀疏的高端断点会压缩低端颜色的差异空间; - 自定义的
rescaler通过cut()把每个数据点映射到对应的分箱序号(1到分箱总数),让每个分箱在颜色映射时拥有相同权重,从而实现颜色在自定义断点间的均匀分配; - 该方法无需修改原始数据,也不需要对数据做任何变换,完全满足你的需求。
内容的提问来源于stack exchange,提问作者Jaken
相关产品推荐
相关产品推荐

