使用for循环为已有ggplot对象添加geom_point仅显示最后一个点的问题
嘿,我完全懂你现在的困扰!你手动逐个添加geom_point能显示所有城市,但用循环就只剩最后一个点,这其实是ggplot2的aes()函数延迟计算的特性导致的——它不会在循环迭代时就把当前i对应的值固定下来,而是等到最终绘图时才去查找变量,这时候i已经是循环的最后一个值了,所以所有添加的geom_point都指向了最后一行城市的数据。
最简单的正确做法:直接传递整个数据框
其实根本不需要手动逐个添加或者用循环,ggplot2本来就是为处理整个数据框设计的,你只需要把cities数据框传给geom_point,并在aes里直接用列名就好:
# 直接一次性添加所有城市点,大小按pop映射 map <- map + geom_point(data = cities, aes(x = long, y = lat, size = pop))
这样ggplot会自动遍历cities的每一行数据,为每个城市生成对应的点,并且自动根据pop列的值调整点的大小,完全替代你手动写多个geom_point的繁琐操作,还不会出错。
为什么你的循环会失败?
当你在循环里写aes(x = cities$long[i], y = cities$lat[i], size = cities$pop[i])时,i是一个动态变量,ggplot并不会在每次循环时就把i的当前值(比如1、2、3...)代入进去,而是会记住“要使用变量i对应的值”。等循环结束后,i已经变成了nrow(cities),所以所有之前添加的geom_point都会指向最后一行的数据,最终就只显示最后一个点了。
如果真的要强行用循环(非常不推荐,因为没必要),你可以把aes换成aes_()或者把值硬编码到aes外面,但这完全是舍近求远的做法,直接用整个数据框的方式才是ggplot2的正确打开方式。
额外小提示
你提到cities已经没有缺失值了,所以不用额外处理;如果以后遇到有缺失值的数据,可以在geom_point里加上na.rm = TRUE来忽略缺失的点:
map <- map + geom_point(data = cities, aes(x = long, y = lat, size = pop), na.rm = TRUE)
内容的提问来源于stack exchange,提问作者StillLearningBeGentle

