使用 ggmap、geom_point 和循环映射 long-lat 数据集的最近邻居
Posted
技术标签:
【中文标题】使用 ggmap、geom_point 和循环映射 long-lat 数据集的最近邻居【英文标题】:Mapping nearest neighbours of a long-lat data set using ggmap, geom_point and a loop 【发布时间】:2015-06-17 17:07:32 【问题描述】:我的最终目标是使用 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,])
这会返回以下错误(不知道为什么):
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 栋建筑(第 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 列并将其引用回长 lat 数据 n 次。我完全可以相信我没有使用最有效的方法,所以我愿意接受替代方案。非常感谢任何帮助。
【问题讨论】:
您能解释一下“B 型第 1 栋建筑(第 1 行)的 nn 是 L 型(第 5 行)的第 1 栋建筑”的意思吗?我不明白这个。你想怎么画线?在这里,您的地图中有 5 个点。你最后想要什么? 我的数据框的最近邻(nn)列指的是最近邻是哪一行。所以第 1 行(B 商店 1)的 nn(最近邻)是第 5 行(L 商店 1)。我的目标是通过一条线(geom_path)连接所有最近的邻居,因为我在最小示例中手动连接了这两个,除了使用比我使用“子集”实现的更自动化的方式。非常感谢! 这意味着你有一条线从每个数据点到某个地方。对吗? 完成。希望以下是您所追求的。 【参考方案1】:这是我的尝试。我使用来自geosphere
包的gcIntermediate()
来设置线路。首先,我需要重新排列您的数据。当您使用gcIntermediate()
时,您需要从长/纬度出发和到达。那就是你需要四列。为了以这种方式排列您的数据,我使用了dplyr
包。 mutate_each(colbj, funs(.[nn]), vars = long:lat)
为您提供所需的到达时间长/纬度。 .
代表“长”和“纬”。 [nn]
是变量的向量索引。然后,我雇用了gcIntermediate()
。这将创建空间线。您需要将对象设为 SpatialLinesDataFrame。然后,您需要将输出转换为“正常”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"))
【讨论】:
这正是我想要的结果。非常感谢,爵士乐。我不熟悉 gcIntermediate,但看起来我应该熟悉。我需要一些时间才能理解它,但这再次非常有帮助,超出了我的预期。谢谢! @RichS 我很高兴听到这就是您想要的。可能还有其他方法可以完成相同的工作。但是,这是我根据我的经验所知道的。我通常以这种方式处理在两点之间画线的任务。看看geosphere的CRAN手册。或者搜索其他包。您可能会找到更短的解决方案。 :)以上是关于使用 ggmap、geom_point 和循环映射 long-lat 数据集的最近邻居的主要内容,如果未能解决你的问题,请参考以下文章
使用带有geom_point的for循环将点添加到现有ggplot对象中