作者:戴维 · 卡尔 & 哈德利 · 威克姆,翻译:李博。
原文:ggmap: Spatial Visualization with ggplot2
原文发布于作者知乎、简书,欢迎大家关注支持。本文已获作者授权原创形式发布,欢迎点击【阅读原文】查看!
知乎:https://www.zhihu.com/people/li-bo-90-94/pins/posts
简书:http://www.jianshu.com/u/43365c904dba
PS:由于原文较长,故翻译分为三次进行(会进行多次修正)。
06
ggmap函数
只要 get_map 抓取了有意思的地图,ggmap 就可以绘制了。get_map 抓取的结果是一个特殊类别的栅格(raster)对象(一个十六进制字符串的颜色矩阵)
paris <- get_map(location = "paris")
str(paris)
qmap(baylor, zoom = 14, maptype = 53428, api_key = api_key,
source = "cloudmade")
qmap("houston", zoom = 10, maptype = 58916, api_key = api_key,
source = "cloudmade")
chr [1:1280, 1:1280] "#C6DAB6" "#C2D6B3" "#C2D6B3" ...
- attr(*, "class")= chr [1:2] "ggmap" "raster"
- attr(*, "bb")=’data.frame’: 1 obs. of 4 variables:
..$ ll.lat: num 48.6
..$ ll.lon: num 1.91
..$ ur.lat: num 49.1
..$ ur.lon: num 2.79
图 6:两千万用户制作的 CloudMade 地图样式。左图与图 3 和图 5 相当,右图包含图 4 中的水域。(图 1—图 5,参考上篇)
以下为个人加入内容(代码):
##下面的地址链接表明了图片的位置
> paris <- get_map(location = "paris")
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=paris&zoom=10&size=640x640&scale=2&maptype=terrain&language=en-EN&sensor=false
Information from URL : http://maps.googleapis.com/maps/api/geocode/json?address=paris&sensor=false
> str(paris)#这里需要掌握str()函数的用法,即用于紧凑的显示任意R对象结构
chr [1:1280, 1:1280] "#CBE5A2" "#CBE5A2" "#C8E3A1" "#C8E3A1" "#C8E09D" "#C3DD9D" ...
- attr(*, "class")= chr [1:2] "ggmap" "raster"
- attr(*, "bb")='data.frame': 1 obs. of 4 variables:
..$ ll.lat: num 48.6
..$ ll.lon: num 1.91
..$ ur.lat: num 49.1
..$ ur.lon: num 2.79
- attr(*, "source")= chr "google"
- attr(*, "maptype")= chr "terrain"
- attr(*, "zoom")= num 10
>
ggmap 的目的是把地图从栅格对象中输出到屏幕,并通过创建一个 ggplot 对象来实现这一目的。图像打印时,在图形设备中绘制所需的地图。如图 7 所示。
而 ggmap 需要一个 ggmap 对象,它接受一小部分参数——范围,底图(base_layer),距离(maprange),图例,填充和亮度。
若无这些附加参数,则 ggmap 有效返回以下 ggplot 对象。
ggplot(aes(x = lon, y = lat), data = fourCorners) +
geom_blank() + coord_map("mercator") +
annotation_raster(ggmap, xmin, xmax, ymin, ymax)
其中 fourCorners 是,将 expand.grid 应用于 ggmap 对象的 bb 属性中,并指定的经纬度范围产生的数据集。因此,由 ggmap 创建的 ggplot2 对象的默认底图(即原始画框)是,ggplot(aes(x = lon,y = lat),data = fourCorners),默认的 x、y 轴是根据地图的纬度范围计算而来。
范围参数指定地图覆盖了多少图形范围。它接受三个字符串:图 7 所示的 “normal(标准)”,图 10 和图 12 所示的“panel(面板)” 和每个图中所示的“device(图案)”。
“normal”通过 ggplot2 提供默认坐标来定位地图,因此可以看到它后面的面板。 “panel”则将绘图面板的极限设置为 scale_ [x,y] _continuous(expand = c(0,0)))的地图的经纬度范围。而 “device” 则通过使用新的 theme_nothing 命令,将这一功能用到了极致。
base_layer 是一个将默认基本层替换为用户要求的命令。因此,在上述代码中,用户可以将ggplot(aes(x = lon,y = lat),data = fourCorners)更改为不同的命令。由于 ggplot2 函数中 facet_wrap 和 facet_grid 处于基础层,因此这对于 faceting plots(局部小块绘图)非常重要。
同时,由于更改基本层会改变基本尺度并因此改变绘图的范围,所以当基本层改变时,只有部分地图可见。将 maprange 参数设置为 TRUE(默认为 FALSE),并通过 ggmap 对象本身的 bb 属性确定 x 和 y 轴(经度和纬度),而不是 base_layer 参数。
ggmap(paris, extent = "normal")
图 7:在 ggmap 中设置 extent =“normal”,说明 ggmap 中的地图和 ggplot2 绘制图形的相似性。
ggmap 有关说明性的参数是图例和填充,它们只适用于 extent =“device”。图例参数确定了在地图上绘制图例的位置。它的选项是 “left”,“right”(默认), “bottom”, “top”, “topleft”, “bottomleft”, “topright”, “bottomright” 和 “none”。
前四个根据 ggplot2 的标准规格(没有任何轴)绘制图例;后四个绘制地图上的图例就像 ArcGIS(一个可伸缩的,全面的 GIS 平台)一样; 最后的 “none” 用于减去图例。填充控制主要是定义图例应该绘制在距离边角多远的地方。
亮度参数,是由 Jean-Olivier Irisson 提出的一个建议,用于调整图像色彩。默认值 c(0,“black”)表示完全半透明的黑色层,即完全没有色彩。
通常情况下,第一个参数对应于 alpha 混合(0 = 不可见(invisible),1 = 不透明 (opaque)),第二个参数是色调的颜色。如果只有一个数字被提供给 ggmap(假定一个黑色色彩的亮度参数),色调本身是通过在地图的上添加一个 geom_rect 层来制作的。图 2 提供了一个例子,其中黑色色彩被添加到地图以增强点的可视性。
由于 ggmap 返回一个 ggplot 对象,ggmap 的输出本身可以作为 ggplot2 框架中的基础层。这是一个非常难得的重要的实现——它允许 ggplot2 的全部功能。我们现在通过在德克萨斯州休斯顿市中心的一个暴力犯罪案例研究来说明这些方法的有效性 。
07
ggmap实践
数据 data
犯罪数据是由休斯顿警察局的网站,在 2010 年 1 月至 8 月期间汇编的。使用 plyr(韦翰,2011 年) 对这些数据进行了轻微的清理和汇总,同时使用了谷歌地图的地理编码;因此,完整的数据在 ggmap 中可用来作为犯罪数据集。
> str(crime)
’data.frame’: 86314 obs. of 17 variables:
$ time : POSIXt, format: "2010-01-01 0...
$ date : chr "1/1/2010" "1/1/2010" "1...
$ hour : int 0 0 0 0 0 0 0 0 0 0 ...
$ premise : chr "18A" "13R" "20R" "20R" ...
$ offense : chr "murder" "robbery" "aggr...
$ beat : chr "15E30" "13D10" "16E20" ...
$ block : chr "9600-9699" "4700-4799" ...
$ street : chr "marlive" "telephone" "w...
$ type : chr "ln" "rd" "ln" "st" ...
$ suffix : chr "-" "-" "-" "-" ...
$ number : int 1 1 1 1 1 1 1 1 1 1 ...
$ month : Factor w/ 12 levels "january"...
$ day : Factor w/ 7 levels "monday" ...
$ location: chr "apartment parking lot" ...
$ address : chr "9650 marlive ln" "4750 ...
$ lon : num -95.4 -95.3 -95.5 -95.4 ...
$ lat : num 29.7 29.7 29.6 29.8 29.7...
由于我们只对在市中心发生的暴力犯罪感兴趣,所以我们需要对数据进行一些设置限定。首先要确定一个地理位置的边界,使用 gglocator,一个 ggmap 输出的类似于定位的函数 ggplot2。 gglocator 不仅用于 ggmap 绘图,而且可用于一般的 ggplot2 制图。
##原文如下
> # find a reasonable spatial extent(找到一个合理的空间范围)
> qmap(’houston’, zoom = 13)
> gglocator(2)
> lon lat
> 1 -95.39681 29.78400
> 2 -95.34188 29.73631
>
> # only violent crimes(仅限暴力犯罪)
> violent_crimes <- subset(crime,
+ offense != "auto theft" & offense != "theft" & offense != "burglary")
>
> # order violent crimes(攻击性暴力犯罪)
> violent_crimes$offense <- factor(violent_crimes$offense,
+ levels = c("robbery", "aggravated assault", "rape", "murder"))
>
> # restrict to downtown(市中心范围)
> violent_crimes <- subset(violent_crimes,
+ -95.39681 <= lon & lon <= -95.34188 &
+ 29.73631 <= lat & lat <= 29.78400)
分析只涉及严重伤害袭击,抢劫,强奸和谋杀的暴力犯罪数据。请注意,尽管为确保数据的质量做出了一些努力,但是数据还是被轻微清理,同时数据集可能仍然包含错误。
08
分析
第一步,我们能做的是看看个体犯罪发生的地方。通过模拟一些简单的 ggplot2 类型(主要是通过 ggplot2 具有导向功能的图例字体和键型),图 8 左侧包含生成显示空间气泡图的代码。
泡泡图的其中一个问题是图的重叠和点的大小,我们不能依靠感觉去确定犯罪的发生地。
处理这一问题的一个方法是将点分开,并剔除没有任何样本的点。(图 8 右图)向我们展示了犯罪行为的发生情况。
图 8: 休斯顿市中心的暴力犯罪泡泡图 (左) 和同样的箱图(右)
theme_set(theme_bw(16))
HoustonMap <- qmap("houston", zoom = 14, color = "bw", legend = "topleft")
HoustonMap +
geom_point(aes(x = lon, y = lat, colour = offense, size = offense),
data = violent_crimes)
HoustonMap +
stat_bin2d(
aes(x = lon, y = lat, colour = offense, fill = offense),
size = .5, bins = 30, alpha = 1/2,
data = violent_crimes
)
这个箱图是我们第一次开始在 ggplot2 框架中看到地图的情况。虽然它实际上并不是一个非常好的绘图,但它在说明 ggplot2 框架与地图的上下文信息中有很大优势 ——根据犯罪变量将数据框架暴力行为划分为块的过程,将每个地点的点数汇总到一个数据集,绘图最后由 ggplot2 在幕后完成。
那么,一般暴力犯罪怎么办?如果我们忽略了犯罪的类型,我们可以通过使用轮廓图来了解暴力犯罪的空间分布。由于地图图像本身是基于 ggplot2 的 annotation_raster,它没有绘制坐标,但我们可以访问添加的坐标,以形成一个填充的轮廓图。这在图 9 中看到(左图)。
图 9: 暴力犯罪的轮廓图 (左),加入一个小插图 (右图右下角)。
houston <- get_map("houston", zoom = 14)
HoustonMap <- ggmap("houston", extent = "device", legend = "topleft")
HoustonMap +
stat_density2d(
aes(x = lon, y = lat, fill = ..level.., alpha = ..level..),
size = 2, bins = 4, data = violent_crimes,
geom = "polygon"
)
overlay <- stat_density2d(
aes(x = lon, y = lat, fill = ..level.., alpha = ..level..),
bins = 4, geom = "polygon",
data = violent_crimes
)
HoustonMap + overlay + inset(
grob = ggplotGrob(ggplot() + overlay + theme_inset()),
xmin = -95.35836, xmax = Inf, ymin = -Inf, ymax = 29.75062
)
这种类型叠加图是非常有效,然而,他们传达信息的能力可能受到阻碍,即叠加图与地图本身存在的视觉混淆。这在使用彩色地图时尤其常见。
为了解决这个问题,插图功能可以用于在白色背景上插入包含叠加层的地图插图,辅助轴线用于 theme_inset 函数实现的图例说明。这在图 9(右图)中可以明显看到。
同时,该图像表明有三个主要的热点活动。这三个中的每一个对应于休斯敦人经常活动且特别危险的地点。
从东到西,热点是由(1)一个县监狱,每天释放囚犯两次,大量的囚犯在该区域活动,(2)在大量无家可归和贫困地区的商业公交车站, 3)一个卖淫窝点,处在城市繁华步行街地区。
除了单个图,ggmap 或 qmap 的 base_layer 参数允许小面绘图(见图 10)。
houston <- get_map(location = "houston", zoom = 14, color = "bw",
source = "osm")
HoustonMap <- ggmap(houston, base_layer = ggplot(aes(x = lon, y = lat),
data = violent_crimes))
HoustonMap +
stat_density2d(aes(x = lon, y = lat, fill = ..level.., alpha = ..level..),
bins = 5, geom = "polygon",
data = violent_crimes) +
scale_fill_gradient(low = "black", high = "red") +
facet_wrap(~ day)
这对于具有离散时间分量(日,月,季度,年等)的空间数据特别有用。
最后一个绘图显示 ggplot2 中轮廓线的问题之一 ——“剪切”或 “撕裂” 的轮廓。除了这个直观现象(这可能在后续的 ggplot2 版本中可能会被修正),我们可以看到,实际上大多数暴力犯罪发生在星期一,其次是星期五。星期五的小图很容易解释——在市中心的酒吧区有一个小圆点,在西南部较大地区,夜生活较为活跃。而星期一的小图不是很容易解释。