ggplot2中geom_point颜色映射与scale_color_manual匹配异常的原因咨询
嗨,我来帮你理清楚这里面的关键逻辑,其实问题出在ggplot对离散颜色映射的默认排序规则上:
第一次用名称映射时的图例乱序问题
你最开始用命名向量给scale_color_manual传values,比如"300+ " = "#2B4871"这种,这时候ggplot会把你的分组名称(也就是那些区间字符串)当成离散的分类变量,而它默认会对离散变量按字母顺序排序——所以"0 - 75 "因为以0开头排第一,"150 - 300 "排第二,"300+ "第三,"75 - 150 "因为7的字母序比3靠后,反而排最后,这就是图例顺序乱掉的原因。
直接用十六进制颜色在aes里的匹配混乱问题
后来你改成在geom_point的aes(color = "#xxxxxx")里直接传颜色码,这里有个很容易踩的坑:aes里的参数是用来映射变量的,不是直接设置属性! 哪怕你传的是颜色码字符串,ggplot也会把它当成一个离散分类变量的标签,而不是直接用这个颜色。
具体来说,你四个geom_point里的color值分别是"#05BADD"、"#2B4871"、"#FF8007"、"#FFB404",ggplot会先把这些字符串按字母顺序排序,得到的顺序是:
"#05BADD"(0开头,字母序最前)"#2B4871"(2开头)"#FF8007"(FF开头,8的字母序比B小)"#FFB404"(FF开头,B的字母序比8靠后)
而你的scale_color_manual的values是c("#2B4871", "#05BADD", "#FFB404", "#FF8007"),这时候ggplot会把排序后的第一个分类标签对应values的第一个值,第二个标签对应values的第二个值……以此类推:
"#05BADD"→"#2B4871""#2B4871"→"#05BADD""#FF8007"→"#FFB404""#FFB404"→"#FF8007"
这刚好就是你交换geom_point里的颜色码之后的对应关系,所以交换后颜色才匹配上了!
更清晰的写法建议
其实完全不用这么绕,推荐你先在数据里创建一个分组变量,用它来映射颜色,这样既可控又清晰:
# 先给数据加分组变量,同时设置成有序因子控制顺序 weekly_data <- weekly_data %>% mutate( activity_group = case_when( weekly_moderate_m >= 300 ~ "300+ ", weekly_moderate_m >= 150 ~ "150 - 300 ", weekly_moderate_m >= 75 ~ "75 - 150 ", TRUE ~ "0 - 75 " ), activity_group = factor(activity_group, levels = c("300+ ", "150 - 300 ", "75 - 150 ", "0 - 75 ")) ) # 画图时只需要一个geom_point,颜色映射到分组变量 weekly_moderate_p <- ggplot(weekly_data, aes(x = week_number, y = weekly_moderate_m, color = activity_group)) + geom_point(size = 2, position = position_jitter(width = .25, height = 0), alpha = 0.5) + scale_color_manual( name = "Minutes", values = c( "300+ " = "#2B4871", "150 - 300 " = "#05BADD", "75 - 150 " = "#FFB404", "0 - 75 " = "#FF8007" ) ) + # 后续的scale_x、scale_y、theme、annotation等设置保持不变 scale_x_discrete(name = NULL, labels = labels_wy) + scale_y_continuous(breaks = c(0,75,150,300,450,600,750,900,1050), limits = c(0,1060)) + labs(title = "Moderate activity", subtitle = "(minutes per week)") + geom_hline(yintercept = c(75,150,300), linetype = "dashed", color = "black") + ylab("Activity minutes") + theme_minimal() + theme( plot.subtitle = element_text(margin = margin(1,5,5,0), size = 10, hjust = 1, face = "italic"), axis.title.y = element_text(margin = margin(0,5,0,5), size = 12), plot.title = element_text(margin = margin(5,5,5,0), size = 14, hjust = 1, face = "bold"), legend.position = "bottom", plot.margin = margin(1,25,1,1) ) + guides(color = guide_legend(override.aes = list(size = 4))) + coord_cartesian(clip = "off") + annotation_custom( grob = tableGrob(weekly_moderate_75_150_300_table, theme = ttheme_minimal( base_size = 9, base_color = "black", padding = unit(c(4,4), "mm"), core = list( bg_params = list(fill = c("#2B4871", "#05BADD", "#FFB404", "#FF8007"), alpha = .5, col = NA), fg_params = list(hjust = 0, x = 0.15) ), colhead = list(fg_params = list(hjust = 0, x = 0.15)) )), xmin = 4.5, xmax = 6, ymin = 600, ymax = 1050 )
这样写法不仅颜色映射完全对应,图例顺序也和你设置的因子顺序一致,不用再手动交换颜色码啦!
备注:内容来源于stack exchange,提问作者M S

