多geom_sf数据集绘制ggplot时图例颜色不匹配问题排查
问题:ggplot绘制多SF线图层时颜色与图例不匹配
使用ggplot绘制两个简单要素(sf)线数据集的地图,希望线能共用一个图例,但当前代码导致绘图颜色与图例颜色不匹配——图例中A、B、D对应指定调色板颜色,C为黑色,但绘图中D和C的颜色被错误交换。将df2的id改为"X"时显示正常。
复现代码:
library("sf") library("sfheaders") library("ggplot2") df1 <- data.frame( x = c(0,1,0,1,0,1), y = c(1,1,2,2,3,3), id = c("A","A","B","B","D","D") ) df2 <- data.frame( x = c(0,1), y = c(4,4), id= c("C","C") ) set1_sf <- sfheaders::sf_linestring( obj = df1, x = "x", y = "y", linestring_id = "id" ) set2_sf <- sfheaders::sf_linestring( obj = df2, x = "x", y = "y", linestring_id = "id" ) palette <- c("#7FC97F","#BEAED4","#FDC086") ggplot() + geom_sf(set1_sf, mapping=aes(colour=id), lwd=0.8) + geom_sf(set2_sf, mapping=aes(colour=id), lwd=0.8) + scale_colour_manual(name="Lines", values = c(palette,"black"), labels = c(set1_sf$id, set2_sf$id))
错误原因
ggplot处理分类颜色映射时,是按照因子的水平顺序来匹配scale_colour_manual中的values,而非图层加载顺序或labels的顺序。
在你的代码中:
- 两个数据集的
id合并后,默认因子水平按字母排序为:A, B, C, D - 你在
values中指定的顺序是palette(A、B、D的颜色) + black,即#7FC97F, #BEAED4, #FDC086, black,对应逻辑顺序是A,B,D,C labels的顺序是A,B,D,C,但实际因子水平顺序是A,B,C,D,导致:- 因子
C匹配到values的第3个值(原本给D的#FDC086) - 因子
D匹配到values的第4个值(原本给C的black)
因此出现颜色交换问题。当id改为"X"时,字母排序后X在最后,与你指定的顺序一致,所以显示正常。
- 因子
解决方法
方法1:合并两个SF数据集(推荐)
将两个sf对象合并为一个,只需一次geom_sf调用,颜色映射会自动按统一因子水平处理,避免顺序混乱:
# 合并数据集 combined_sf <- rbind(set1_sf, set2_sf) # 手动指定因子水平(可选,确保图例顺序符合预期) combined_sf$id <- factor(combined_sf$id, levels = c("A", "B", "D", "C")) ggplot(combined_sf) + geom_sf(aes(colour = id), lwd = 0.8) + scale_colour_manual( name = "Lines", values = c(A = "#7FC97F", B = "#BEAED4", D = "#FDC086", C = "black"), labels = c("A", "B", "D", "C") )
方法2:用命名向量指定颜色映射
不合并数据集,在scale_colour_manual的values中直接用命名向量绑定每个id与对应颜色,无视因子顺序也能正确匹配:
ggplot() + geom_sf(set1_sf, mapping = aes(colour = id), lwd = 0.8) + geom_sf(set2_sf, mapping = aes(colour = id), lwd = 0.8) + scale_colour_manual( name = "Lines", values = c(A = palette[1], B = palette[2], D = palette[3], C = "black"), labels = c("A", "B", "D", "C") )
方法3:统一两个数据集的因子水平
如果坚持分开绘制图层,可先统一两个数据集的因子水平,确保顺序一致:
# 收集所有id并指定顺序 all_ids <- c(set1_sf$id, set2_sf$id) set1_sf$id <- factor(set1_sf$id, levels = all_ids) set2_sf$id <- factor(set2_sf$id, levels = all_ids) ggplot() + geom_sf(set1_sf, mapping = aes(colour = id), lwd = 0.8) + geom_sf(set2_sf, mapping = aes(colour = id), lwd = 0.8) + scale_colour_manual( name = "Lines", values = c(palette, "black"), labels = all_ids )
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

