在ggmap/ggplot中如何设置绝对而非相对的线条尺寸?
我明白你遇到的这个痛点——当用数据变量映射geom_path的size时,ggplot根本不是按文档说的毫米来渲染,而是把数据里的极值拉到默认的最大/最小宽度,改somevalue只要还是最大值就完全没用对吧?别着急,我来给你拆解问题和解决办法:
问题根源
当你在aes()里写size = size时,你是在把数据中的size变量映射到ggplot的尺寸尺度,而不是直接设置绝对宽度。ggplot默认会自动将数据中的最小值对应到尺度的最小宽度(通常是1mm左右),最大值对应到最大宽度(通常是6mm左右),中间值按比例分配。这就是为什么不管你把somevalue改成3还是10,只要它是当前数据里的最大值,显示的线条粗细都不会变——它始终被映射到默认的最大宽度。
解决方案
要控制实际的线条宽度,你需要手动干预这个尺度转换,有几种方式可以选:
1. 手动指定连续尺度的宽度范围(适合数值型size变量)
用scale_size_continuous()的range参数,直接定义数据最小值和最大值对应的绝对宽度(单位是毫米)。这样不管数据里的极值是多少,都会被映射到你指定的宽度范围:
library(ggmap) base_layer <- get_googlemap(center = c(lon = 28.5, lat = 37) , zoom = 3 , maptype="roadmap" , size = c(640,640) , scale = 2 , color = "bw") somevalue <- 3 df <- data.frame( group = c("g1","g1","g2","g2"), size = c(1,1,somevalue,somevalue), color = c("blue","blue", "red", "red"), lon = c(10,20,10,-10), lat = c(52,60,52,60) ) ggmap(base_layer) + geom_path(data = df, aes(x = lon, y = lat, alpha = 0.6, group = group, color = color, size = size)) + scale_size_continuous(range = c(0.5, 2)) # 最小值对应0.5mm,最大值对应2mm,可按需调整
比如上面的代码里,不管somevalue是3还是10,红色线条的宽度都会固定是2mm,蓝色线条是0.5mm,完全在你的控制范围内。
2. 手动指定分类尺度的宽度(适合分类型size变量)
如果你的size变量其实是用来区分不同组的(比如g1对应细线条,g2对应粗线条),可以先把size转成因子,再用scale_size_manual()直接给每个类别指定宽度:
library(ggmap) base_layer <- get_googlemap(center = c(lon = 28.5, lat = 37) , zoom = 3 , maptype="roadmap" , size = c(640,640) , scale = 2 , color = "bw") somevalue <- 3 df <- data.frame( group = c("g1","g1","g2","g2"), size = c(1,1,somevalue,somevalue), color = c("blue","blue", "red", "red"), lon = c(10,20,10,-10), lat = c(52,60,52,60) ) # 把size转成因子 df$size_group <- factor(df$size) ggmap(base_layer) + geom_path(data = df, aes(x = lon, y = lat, alpha = 0.6, group = group, color = color, size = size_group)) + scale_size_manual(values = c("1" = 0.5, "3" = 2)) # 直接指定每个类别对应的宽度
这种方式更灵活,适合每个组需要固定宽度的场景,哪怕后续数据里的size数值变化,只要类别对应上,宽度就不会变。
3. 直接给不同组设置固定宽度(适合组数量少的情况)
如果你不需要用size变量做映射,只是想给不同组设置不同宽度,那可以直接把size放在geom_path()外面,不用放到aes()里,分两次绘制不同组:
library(ggmap) base_layer <- get_googlemap(center = c(lon = 28.5, lat = 37) , zoom = 3 , maptype="roadmap" , size = c(640,640) , scale = 2 , color = "bw") somevalue <- 3 df <- data.frame( group = c("g1","g1","g2","g2"), size = c(1,1,somevalue,somevalue), color = c("blue","blue", "red", "red"), lon = c(10,20,10,-10), lat = c(52,60,52,60) ) ggmap(base_layer) + # 绘制g1组,宽度0.5mm geom_path(data = subset(df, group == "g1"), aes(x = lon, y = lat, alpha = 0.6, color = color), size = 0.5) + # 绘制g2组,宽度2mm geom_path(data = subset(df, group == "g2"), aes(x = lon, y = lat, alpha = 0.6, color = color), size = 2)
这种方式不需要处理尺度映射,直接明了,适合组数量不多的情况。
总结
核心区别就是:
- 放在
aes()里的size是映射,ggplot会自动用尺度转换,所以需要用scale_size_*()系列函数控制实际宽度; - 放在
aes()外面的size是固定参数,直接按毫米设置绝对宽度。
根据你的需求选择对应的方式,就能轻松控制线条的实际粗细了。
内容的提问来源于stack exchange,提问作者user2530062

