如何在tidyterra中对连续值分箱时控制重缩放?
问题描述
我尝试用tidyterra的scale_fill_whitebox_b()函数,通过非均匀分布的自定义断点对连续栅格值分箱,目的是突出低值区间的差异。但分箱刻度会自动根据断点间距重缩放配色方案,导致低值区间的颜色过于相近,难以区分。
测试代码如下:
library(ggplot2) library(terra) library(tidyterra) # 构造包含稀疏高值的示例数据 r <- rast(nrows=10, ncols=10) values(r) <- abs(rnorm(100)) r[17] <- 12 r[22] <- 17 r[72] <- 7 breaks <- c(0, 0.25, 0.5, 1, 2, 5, 10, 20) ggplot() + geom_spatraster(data=r) + scale_fill_whitebox_b("viridi", breaks=breaks, direction=-1)
我发现scale_fill_whitebox_b()基于ggplot的分箱刻度构造函数,该函数有rescaler参数,但不清楚如何在scale_fill_whitebox_b中正确调用。用ggplot原生的scale_fill_stepsn时,可以通过values和limits参数实现无重缩放的效果:
ggplot(r) + geom_raster(aes(x=x, y=y, fill=lyr.1)) + scale_fill_stepsn(colors=terrain.colors(7), breaks=breaks, values = scales::rescale(breaks), limits = range(breaks))
但scale_fill_whitebox_b不支持values参数,调整rescaler参数也没成功。我希望复刻geom_spatraster_contour_filled的配色效果——让配色在指定的断点区间之间均匀分布,代码示例:
ggplot() + geom_spatraster_contour_filled(data=r, breaks=breaks) + scale_fill_whitebox_d("viridi", direction=-1)
解决方案
方法1:转离散因子后用离散型配色(推荐)
先将连续栅格按自定义断点转换为离散分箱因子,再用scale_fill_whitebox_d()实现均匀配色,完全匹配等高线填充的效果:
library(ggplot2) library(terra) library(tidyterra) # 构造数据 r <- rast(nrows=10, ncols=10) values(r) <- abs(rnorm(100)) r[17] <- 12 r[22] <- 17 r[72] <- 7 breaks <- c(0, 0.25, 0.5, 1, 2, 5, 10, 20) # 将连续栅格转为分箱后的离散因子栅格 r_cut <- cut(r, breaks = breaks, include.lowest = TRUE) # 绘图:用离散型whitebox配色 ggplot() + geom_spatraster(data = r_cut) + scale_fill_whitebox_d("viridi", direction = -1, labels = paste0(breaks[-length(breaks)], "–", breaks[-1])) + labs(fill = "数值区间")
方法2:自定义rescaler参数适配分箱刻度
如果坚持使用scale_fill_whitebox_b(),可以通过自定义rescaler,基于区间索引而非数值间距重缩放,实现均匀配色:
ggplot() + geom_spatraster(data=r) + scale_fill_whitebox_b("viridi", breaks=breaks, direction=-1, rescaler = function(x, to = c(0, 1), from = NULL) { # 获取每个值对应的区间索引,再对索引重缩放 idx <- findInterval(x, breaks) scales::rescale(idx, to = to, from = c(1, length(breaks)-1)) })
原理说明
- 方法1中,
terra::cut()将连续值按断点分割为离散区间并标记为因子,scale_fill_whitebox_d()作为离散型刻度,会把配色方案均匀分配给每个因子水平,不会受区间数值间距影响。 - 方法2通过
findInterval()获取值对应的区间索引,再对索引进行重缩放,让每个区间获得相同的配色权重,避免了原生按数值间距缩放的问题。
内容的提问来源于stack exchange,提问作者Jaken
相关产品推荐
相关产品推荐

