0

我在两个包含个人和手机信号塔坐标的数据集上有点挣扎:

  • 第一个包含 9,459 个人的数据集,包含 1,214 个变量,包括他们的纬度和经度(以度为单位)。
  • 第二个数据集包含 31,176 个蜂窝塔,包含 4 个变量,包括它们的纬度和经度(度数)和范围(米)。

我想确定一个人是否在至少一个蜂窝塔的范围内,如果是的话,创建一个等于 1 的虚拟对象。

但是,由于数据集的大小,我无法将它们与交叉连接命令合并。我尝试使用geosphere带有以下命令的包:

distm(c(df1$longitude, df2$latitude), c(df2$longitude, df2$latitude), fun= distHaversine)

不幸的是,它不起作用,因为两个数据集的大小不同。知道如何解决这个问题吗?

4

2 回答 2

0

通常,这可以更有效地完成,以最大限度地利用 RAM 和处理器并减少开销。但是,如果您尝试做的是一次性操作,那么以下方法应该就足够了(在当前笔记本上大约需要 5 分钟)。

辅助函数

# More info: https://github.com/RomanAbashin/distGeo_v
distGeo_v <- function(x, y, xx, yy) { 
    if(!"geosphere" %in% installed.packages())  {
        stop("The 'geosphere' package needs to be installed for this function to work.")
    }
    matrix(.Call("_inversegeodesic", 
                 as.double(x), as.double(y), as.double(xx), as.double(yy), 
                 as.double(6378137), 1/298.257223563, PACKAGE='geosphere'), 
           ncol = 3, byrow = TRUE)[,1]
}

数据

library(geosphere)
library(tidyverse)
set.seed(1702)

users <- tibble(userid = 1:10000,
                x = rnorm(10000, 16.3738, 5),
                y = rnorm(10000, 48.2082, 5))

towers <- tibble(lon = rnorm(35000, 16.3738, 10),
                 lat = rnorm(35000, 48.2082, 10),
                 range = runif(35000, 50, 10000))

代码

result <- NULL
for(i in 1:nrow(users)) {

    is_match <- users[i, 1:3] %>%
        tidyr::crossing(towers[, 1:3]) %>%
        filter(distGeo_v(x, y, lon, lat) <= range) %>%
        nrow() > 0

    result <- bind_rows(result, tibble(userid = users$userid[i],
                                       match = is_match))

}

结果

> head(result)
# A tibble: 6 x 2
  userid match
   <int> <lgl>
1      1 TRUE 
2      2 FALSE
3      3 FALSE
4      4 TRUE 
5      5 FALSE
6      6 FALSE

现在您可以left_join将结果转换为您的原始数据。

于 2019-08-20T21:20:50.657 回答
0

我在下面添加了一个使用 spatialrisk 包的解决方案。此包中的关键函数是用 C++ (Rcpp) 编写的,因此速度非常快。

函数 spatialrisk::points_in_circle() 从中心点计算半径内的观测值。请注意,距离是使用 Haversine 公式计算的。由于输出的每个元素都是一个数据框,因此 purrr::map_dfr 用于将它们行绑定在一起:

library(tibble)
library(spatialrisk)
library(dplyr)

set.seed(1702)
users <- tibble(userid = as.character(1:10000),
                lon = rnorm(10000, 16.3738, 1),
                lat = rnorm(10000, 48.2082, 1))

towers <- tibble(lon = rnorm(35000, 16.3738, 1),
                 lat = rnorm(35000, 48.2082, 1))

# Users with tower within 200 meters
purrr::map2_dfr(users$lon, users$lat, 
                   ~points_in_circle(towers, .x, .y, radius = 200)[1,], 
                   .id = "userid") %>%
     mutate(inrange = ifelse(is.na(distance_m), FALSE, TRUE))
于 2019-11-01T20:30:45.147 回答