ggplot中scale_color_identity()标签自动按颜色名重排问题
scale_color_identity()导致标签位置异常的原因及解决方案
问题描述
希望通过label_col列指定的颜色控制字体颜色,但使用scale_color_identity()时,"black"和"white"会按字母顺序自动排序,导致标签位置与geom_col()的堆叠位置不匹配。统一设置label_col为单一颜色时位置正常,已知scale_color_manual()可解决,但想了解scale_color_identity()失效的原因及对应解决方案。
最小复现代码:
list_order <- c("a", "b", "c", "d", "e", "f") test <- data.frame(indicator = rep(list_order, 2), mean = c(1.1, 0.3, 6.3, 4, 7, 2.1, 1.2, 0.7, 6.9, 3.3, 5.4, 1.8), group = c(rep("Group A", 6), rep("Group B", 6))) test %>% mutate(indicator = factor(indicator, levels = list_order), label = round(mean, 1), label_col = ifelse(indicator %in% c("a", "d", "e", "f"), "black", "white")) %>% ggplot(aes(x = group, y = mean, fill = indicator)) + geom_col(width = 0.7, position = "stack") + geom_text(aes(label = label, color = label_col), size = 3, position = position_stack(vjust = 0.5)) + scale_color_identity() + scale_fill_viridis(discrete = T, option = "plasma")
失效原因
ggplot2中,当在aes()里指定color = label_col时,会自动将label_col视为离散分组变量,默认按字母顺序(black在前,white在后)对数据分组。而position_stack()是按分组的顺序计算堆叠位置的:
geom_col()的堆叠顺序由indicator的因子水平(list_order)决定;geom_text()的堆叠顺序却被label_col的分组顺序覆盖,导致文本的堆叠逻辑与柱子不一致,最终位置错乱。scale_color_identity()仅负责将label_col的字符串直接映射为颜色,不会改变分组的排序逻辑,因此无法解决位置问题。
针对scale_color_identity()的解决方案
核心思路是让geom_text()的堆叠顺序与geom_col()保持一致,即遵循indicator的因子顺序,而非label_col的分组顺序。有两种实现方式:
方法1:在geom_text的aes中指定group=indicator
强制geom_text按indicator分组堆叠,忽略color的分组影响:
test %>% mutate(indicator = factor(indicator, levels = list_order), label = round(mean, 1), label_col = ifelse(indicator %in% c("a", "d", "e", "f"), "black", "white")) %>% ggplot(aes(x = group, y = mean, fill = indicator)) + geom_col(width = 0.7, position = "stack") + # 添加group=indicator,强制按indicator的顺序堆叠文本 geom_text(aes(label = label, color = label_col, group = indicator), size = 3, position = position_stack(vjust = 0.5)) + scale_color_identity() + scale_fill_viridis(discrete = T, option = "plasma")
方法2:将label_col转换为与indicator绑定的因子
让label_col的因子水平顺序与indicator的顺序完全对应,确保分组顺序一致:
test %>% mutate(indicator = factor(indicator, levels = list_order), label = round(mean, 1), label_col = ifelse(indicator %in% c("a", "d", "e", "f"), "black", "white"), # 按indicator的顺序生成label_col的因子水平,避免默认字母排序 label_col = factor(label_col, levels = unique(label_col[match(list_order, indicator)]))) %>% ggplot(aes(x = group, y = mean, fill = indicator)) + geom_col(width = 0.7, position = "stack") + geom_text(aes(label = label, color = label_col), size = 3, position = position_stack(vjust = 0.5)) + scale_color_identity() + scale_fill_viridis(discrete = T, option = "plasma")
内容的提问来源于stack exchange,提问作者A. Piong
相关产品推荐
相关产品推荐

