如何为上下三角颜色不同的geom_tile热图调整轴顺序并保留颜色
解决指定轴顺序后geom_tile上下三角颜色错位问题
你遇到的问题是:用ggnewscale实现了上下三角颜色不同的热图,但添加scale_x/y_discrete(limits = c("b","a","c"))指定轴顺序后,上下三角的颜色对应关系乱了——比如Var1=b、Var2=a的色块本该是绿色(Lower三角),结果变成了蓝色(Upper三角)。
问题根源
原来的ifelse(Var1 < Var2, "Lower", "Upper")是基于字符字典序判断的,比如字符"b"比"a"大,所以Var1=b、Var2=a会被归为Upper。但修改轴顺序后,坐标轴的位置顺序(b→a→c)和字符字典序(a→b→c)不一致,这就导致判断逻辑和实际的三角位置不匹配了。
解决方法
把Var1和Var2转换成有序因子,指定我们想要的轴顺序,然后基于因子的水平位置(整数索引)来判断上下三角,而不是字符本身的大小。
完整代码
library(ggnewscale) library(ggplot2) library(dplyr) data <- data.frame( Var1 = c("b", "c", "a", "c", "a", "b"), Var2 = c("a", "a", "b", "b", "c", "c"), val = c(-2.1113581, 0.6189813, -0.4770620, 0.8119133, -0.4029937, 0.7977290) ) # 关键修改:将Var1和Var2转为有序因子,指定轴顺序 plot_data = data %>% mutate( Var1 = factor(Var1, levels = c("b", "a", "c"), ordered = TRUE), Var2 = factor(Var2, levels = c("b", "a", "c"), ordered = TRUE), # 基于因子的整数索引判断三角:Var1的位置在Var2下方/右侧为Upper,反之为Lower triangle = ifelse(as.integer(Var1) > as.integer(Var2), "Upper", "Lower") ) %>% group_split(triangle) ggplot() + # Upper三角(蓝色) geom_tile(data=plot_data[[which(sapply(plot_data, function(x) x$triangle[1] == "Upper"))]], aes(x=Var1, y=Var2, fill=val)) + scale_fill_gradient(low = "lightblue", high = "blue", name = "Upper Values") + new_scale_fill() + # Lower三角(绿色) geom_tile(data=plot_data[[which(sapply(plot_data, function(x) x$triangle[1] == "Lower"))]], aes(x=Var1, y=Var2, fill=val)) + scale_fill_gradient(low = "lightgreen", high = "darkgreen", name = "Lower Values") + # 指定轴顺序(这里其实可以省略,因为因子已经指定了levels,ggplot会自动用这个顺序) scale_x_discrete(limits = c("b", "a", "c")) + scale_y_discrete(limits = c("b", "a", "c"))
说明
- 转成有序因子后,
as.integer(Var1)会返回该因子水平在指定顺序中的位置(比如"b"是1,"a"是2,"c"是3),这样比较的是它们在坐标轴上的位置关系,完全匹配我们想要的轴顺序。 - 绘图时用
which(sapply(...))来准确选取Upper/Lower的数据集,避免group_split后顺序变化导致的错误(因为group_split的结果顺序不一定固定)。 - 其实指定因子levels后,
scale_x/y_discrete(limits)可以省略,ggplot会自动使用因子的levels作为轴顺序,不过加上也不影响。
内容的提问来源于stack exchange,提问作者user438383
相关产品推荐
相关产品推荐

