使用ggmap、geom_point和循环来映射长纬数据集的最近邻。

4
我的最终目标是基于欧几里得距离,使用ggplot2软件包中的geom_path将一组建筑物的所有最近邻居连接起来。我需要帮助编写一个循环,以便尽可能轻松地绘制所有邻居。
我已经创建了一个名为'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,]) 
}

这会返回以下错误(不确定原因):
Error in nn[i, ] <- which.min(kmnew[i, ]) : replacement has length zero

但似乎对nn返回了正确答案:

     [,1]
[1,]    5
[2,]    4
[3,]    5
[4,]    5
[5,]   NA

我将此附加到名为newbjdata的原始数据框中:
colbj <- cbind(newbjdata,nn)

返回的函数
  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行)的最近邻居是类型为L的第一座建筑物(第5行)。显然,我可以通过对数据帧中这两行进行子集操作来绘制这条线。
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列并将其参考回n次的long lat数据。 我完全相信我没有使用最有效的方法,所以我开放接受其他建议。非常感谢任何帮助。


你能解释一下你所说的“B型建筑物第一排的nn是L型建筑物第一排的1号建筑物”是什么意思吗?我不理解。你想要如何画线呢?在你的地图上有5个点。你最终想要什么? - jazzurro
我的数据框中的最近邻(nn)列指的是最近邻居所在的行。因此,第1行(B商店1)的最近邻居是第5行(L商店1)。我的目标是通过一条线(geom_path)连接所有最近邻居,就像我在最小示例中手动连接这两个点一样,只不过比我使用“subset”更自动化。非常感谢! - RichS
这意味着每个数据点都有一条线路连接到某个地方。是这样吗? - jazzurro
完成。希望以下是您需要的程序相关内容的翻译。 - jazzurro
1个回答

1
这是我的尝试。我使用了geosphere包中的gcIntermediate()来设置线条。首先,我需要重新排列你的数据。当你使用gcIntermediate()时,你需要出发和到达的经纬度。也就是说,你需要四列。为了按照这种方式安排你的数据,我使用了dplyr包。mutate_each(colbj, funs(.[nn]), vars = long:lat)可以帮助你选择所需的到达经纬度。其中.代表'long'和'lat'。[nn]是变量的向量索引。然后,我使用了gcIntermediate()。这将创建空间线条。你需要将对象转换为空间线条数据框。然后,你需要将输出转换为“普通”数据框。这一步非常重要,以便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")

enter image description here

编辑

如果您想在一个序列中进行所有数据操作,以下是一种方法。 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"))

这正是我想要的结果。非常感谢您,jazzurro。我不熟悉gcIntermediate,但看起来我应该了解一下。需要一些时间来理解它,但再次感谢您,这比我预期的要帮助得多。谢谢! - RichS
@RichS 很高兴听到这正是你想要的。可能有其他方法来完成相同的工作。但是,根据我的经验,这就是我所知道的。我通常用这种方式处理在两个点之间画线的任务。可以查看geosphere的CRAN手册。或者搜索其他包,你可能会找到更短的解决方案。 :) - jazzurro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接