使用 ggmap、geom_point 和循环映射 long-lat 数据集的最近邻居

Mapping nearest neighbours of a long-lat data set using ggmap, geom_point and a loop

我的最终目标是使用 ggplot2 包中的 geom_path 在 ggmap 上连接一组建筑物的所有最近邻居(基于欧氏距离)。我需要一个循环的帮助,它可以让我尽可能轻松地绘制所有邻居

我在北京的 3 种类型的建筑物之间创建了一个距离矩阵(称为 'kmnew'):B (x2)、D (x2) 和 L (x1):

   B        B        D        D        L
B NA 6.599014 5.758531 6.285787 3.770175
B NA       NA 7.141096 3.873296 5.092667
D NA       NA       NA 3.690725 2.563017
D NA       NA       NA       NA 2.832083
L NA       NA       NA       NA       NA

我尝试通过声明一个矩阵并使用循环来确定最近的邻居建筑物来逐行辨别每个建筑物的最近邻居:

nn <- matrix(NA,nrow=5,ncol=1)


for (i in 1:nrow(kmnew)){
  nn[i,] <- which.min(kmnew[i,]) 
}

这 return 出现以下错误(不确定原因):

Error in nn[i, ] <- which.min(kmnew[i, ]) : replacement has length zero

但似乎 return nn:

的正确答案
     [,1]
[1,]    5
[2,]    4
[3,]    5
[4,]    5
[5,]   NA

我将其附加到名为 newbjdata 的原始数据框:

colbj <- cbind(newbjdata,nn)

return

  Name Store sqft     long      lat nn
1    B     1 1200 116.4579 39.93921  5
2    B     2  750 116.3811 39.93312  4
3    D     1  550 116.4417 39.88882  5
4    D     2  600 116.4022 39.90222  5
5    L     1 1000 116.4333 39.91100 NA

然后我通过 ggmap 检索我的地图:

bjgmap <- get_map(location = c(lon = 116.407395,lat = 39.904211),
                  zoom = 13, scale = "auto",
                  maptype = "roadmap",
                  messaging = FALSE, urlonly = FALSE,
                  filename = "ggmaptemp", crop = TRUE,
                  color = "bw",
                  source = "google", api_key)

我的最终目标是使用 ggplot 包中的 geom_path 将最近的邻居映射到一起。

例如,B型第1栋楼(第1行)的nn是L型第1栋楼(第5行)。显然,我可以通过对数据帧的上述 2 行进行子集化来绘制这条线:

ggmap(bjgmap) +
geom_point(data = colbj, aes(x = long,y = lat, fill = factor(Name)),
           size =10, pch = 21, col = "white") +
geom_path(data = subset(colbj[c(1,5),]), aes(x = long,y = lat),col = "black")

但是,我需要一个像循环一样工作的解决方案,但我不知道如何实现这一点,因为我需要引用 nn 列并将其引用回 long lat 数据 n 次。我完全相信我没有使用最有效的方法,所以我愿意接受其他方法。非常感谢任何帮助。

这是我的尝试。我使用 geosphere 包中的 gcIntermediate() 来设置线路。首先,我需要重新排列你的数据。当您使用gcIntermediate()时,您需要出发和到达long/lat。那就是你需要四列。为了以这种方式排列您的数据,我使用了 dplyr 包。 mutate_each(colbj, funs(.[nn]), vars = long:lat) 为您取车long/lat。 . 适用于 'long' 和 'lat'。 [nn] 是变量的向量索引。然后,我雇佣了gcIntermediate()。这将创建空间线。您需要使对象成为 SpatialLinesDataFrame。然后,您需要将输出转换为 "normal" data.frame。此步骤必不可少,以便 ggplot 可以读取您的数据。 fortify() 正在做这项工作。

library(ggmap)
library(geosphere)
library(dplyr)
library(ggplot2)

### Arrange the data: set up departure and arrival long/lat

mutate_each(colbj, funs(.[nn]), vars = long:lat) %>%
rename(arr_long = vars1, arr_lat = vars2) %>%
filter(complete.cases(nn)) -> mydf

### Get line information

rts <- gcIntermediate(mydf[,c("long", "lat")],
                      mydf[,c("arr_long", "arr_lat")],
                      50,
                      breakAtDateLine = FALSE,
                      addStartEnd = TRUE,
                      sp = TRUE)

### Convert the routes to a data frame for ggplot use

rts <- as(rts, "SpatialLinesDataFrame")
rts.df <- fortify(rts)


### Get a map (borrowing the OP's code)                   
bjgmap <- get_map(location = c(lon = 116.407395,lat = 39.904211),
                  zoom = 13, scale = "auto",
                  maptype = "roadmap",
                  messaging = FALSE, urlonly = FALSE,
                  filename = "ggmaptemp", crop = TRUE,
                  color = "bw",
                  source = "google", api_key)

# Draw the map
ggmap(bjgmap) +
geom_point(data = colbj,aes(x = long, y = lat, fill = factor(Name)),
           size = 10,pch = 21, col = "white") +
geom_path(data = rts.df, aes(x = long, y = lat, group = group),
          col = "black")

编辑

如果您想在一个序列中执行所有数据操作,以下是一种方法。 foo 与上面的 rts.df 相同。

mutate_each(colbj, funs(.[nn]), vars = long:lat) %>%
rename(arr_long = vars1, arr_lat = vars2) %>%
filter(complete.cases(nn)) %>%
do(fortify(as(gcIntermediate(.[,c("long", "lat")],
                          .[,c("arr_long", "arr_lat")],
                          50,
                          breakAtDateLine = FALSE,
                          addStartEnd = TRUE,
                          sp = TRUE), "SpatialLinesDataFrame"))) -> foo

identical(rts.df, foo)
#[1] TRUE

数据

colbj <- structure(list(Name = structure(c(1L, 1L, 2L, 2L, 3L), .Label = c("B", 
"D", "L"), class = "factor"), Store = c(1L, 2L, 1L, 2L, 1L), 
sqft = c(1200L, 750L, 550L, 600L, 1000L), long = c(116.4579, 
116.3811, 116.4417, 116.4022, 116.4333), lat = c(39.93921, 
39.93312, 39.88882, 39.90222, 39.911), nn = c(5L, 4L, 5L, 
5L, NA)), .Names = c("Name", "Store", "sqft", "long", "lat", 
"nn"), class = "data.frame", row.names = c("1", "2", "3", "4", 
"5"))