【问题标题】:R - dividing a huge dataframe of latitude/longitude points into groups according to locationR - 根据位置将纬度/经度点的巨大数据帧分组
【发布时间】:2016-12-01 06:54:19
【问题描述】:

我是 R 的新手,但我听说使用 for 循环确实是个坏主意。我有使用它们的工作代码,但我想改进它,因为大数据非常慢。我已经有了一些如何改进算法的想法,但我不知道如何对其进行矢量化,或者不使用for 循环。

我只是将 lat/lng 点分组为一个以半径为参数的圆。

函数的示例输出(仅填充 circle_id 列中的值),半径设置为 100 米:

[1] "Locations: "
   latitude  longitude sensor_time sensor_time2         circle_id
   48.15144  17.07569  1447149703  2015-11-10 11:01:43         1
   48.15404  17.07452  1447149743  2015-11-10 11:02:23         2
   48.15277  17.07514  1447149762  2015-11-10 11:02:42         3
   48.15208  17.07538  1447149771  2015-11-10 11:02:51         1
   48.15461  17.07560  1447149773  2015-11-10 11:02:53         4
   48.15139  17.07562  1447149811  2015-11-10 11:03:31         1
   48.15446  17.07517  1447149866  2015-11-10 11:04:26         2
   48.15266  17.07330  1447149993  2015-11-10 11:06:33         5

所以我有 2 个 for 循环,loop1 遍历每一行,loop2 遍历每个先前的 circle_id,并检查 loop1 的当前位置是否在 loop2 的现有圆的半径内。每个 circle_id 的中心是在所有先前的半径之外找到的第一个位置。

代码如下:

init_circles = function(datfr, radius) {
  cnt = 1
  datfr$circle_id[1] = 1
  longitude = datfr$longitude[1]
  latitude = datfr$latitude[1]
  circle_id = datfr$circle_id[1]
  datfr2 <- data.frame(longitude, latitude, circle_id)

  for (i in 2:NROW(datfr)) {
      for (j in 1:NROW(datfr2)) {
        tmp = distHaversine(c(datfr$longitude[i],datfr$latitude[i]) ,c(datfr2$longitude[j],datfr2$latitude[j]))
        if (tmp < radius){
          datfr$circle_id[i] = datfr2$circle_id[j]
          break
        }
      }
      if (datfr$circle_id[i]<1){
        cnt = cnt +1
        datfr$circle_id[i] = cnt
        datfr2[nrow(datfr2)+1,] = c(datfr$longitude[i],datfr$latitude[i],datfr$circle_id[i])
      }
  }
  return(datfr)
}

datfr 是没有设置circle_id 的输入数据框,datfr2 是包含已有圆圈的临时数据框。

编辑:这是一个视觉输出:

你可以看到这些圆圈的用途,上面的红色圆圈有 21 个其他位置适合它的半径 (21 + 1 original = 22)

非常感谢您的帮助, 阿莱娜

【问题讨论】:

    标签: r optimization


    【解决方案1】:

    我假设我们有一个数据框 circles,其中包含每个圆的中心和半径,并且您问题中发布的示例数据位于名为 dat 的数据框中。下面的代码将距离的计算向量化,并使用lapply计算每个点到每个圆心的距离,并确定每个点是否在该圆的半径内。

    library(geosphere)
    
    # We'll check the distance of each data point from the center of each 
    #  of these circles
    circles = data.frame(ID=1:2, lon=c(17.074, 17.076), lat=c(48.1513, 48.15142), 
                         radius=c(180,190))
    
    datNew = lapply(1:nrow(circles), function(i) {
    
      df = dat
    
      df$dist = distHaversine(df[,c("longitude", "latitude")], 
                              circles[rep(i,nrow(df)), c('lon','lat')])
    
      df$in_circle = ifelse(df$dist <= circles[i, "radius"], "Yes", "No")
    
      df$circle_id = circles[i, "ID"]
    
      df
    
    })
    
    datNew = do.call(rbind, datNew)
    
    datNew
    
       latitude longitude sensor_time sensor_time2    time3      dist in_circle circle_id
    1  48.15144  17.07569  1447149703   2015-11-10 11:01:43 126.47756       Yes         1
    2  48.15404  17.07452  1447149743   2015-11-10 11:02:23 307.45048        No         1
    3  48.15277  17.07514  1447149762   2015-11-10 11:02:42 184.24465        No         1
    4  48.15208  17.07538  1447149771   2015-11-10 11:02:51 134.32601       Yes         1
    5  48.15461  17.07560  1447149773   2015-11-10 11:02:53 387.15358        No         1
    6  48.15139  17.07562  1447149811   2015-11-10 11:03:31 120.73138       Yes         1
    7  48.15446  17.07517  1447149866   2015-11-10 11:04:26 362.34236        No         1
    8  48.15266  17.07330  1447149993   2015-11-10 11:06:33 160.07179       Yes         1
    9  48.15144  17.07569  1447149703   2015-11-10 11:01:43  23.13059       Yes         2
    10 48.15404  17.07452  1447149743   2015-11-10 11:02:23 311.68096        No         2
    11 48.15277  17.07514  1447149762   2015-11-10 11:02:42 163.29068       Yes         2
    12 48.15208  17.07538  1447149771   2015-11-10 11:02:51  86.70762       Yes         2
    13 48.15461  17.07560  1447149773   2015-11-10 11:02:53 356.34955        No         2
    14 48.15139  17.07562  1447149811   2015-11-10 11:03:31  28.41890       Yes         2
    15 48.15446  17.07517  1447149866   2015-11-10 11:04:26 343.97933        No         2
    16 48.15266  17.07330  1447149993   2015-11-10 11:06:33 243.44024        No         2
    

    所以我们现在有了一个数据框,告诉我们每个点是否在给定的圆圈内。数据框是长格式的,这意味着原始数据框dat中的每个点都有n行,其中ncircles数据框中的行数。从这里,您可以进行进一步的处理,例如只为多个圆圈中的每个点保留一行等。

    这是一个例子。我们将返回一个数据框列表,其中包含一个点在其中的圆圈,或者如果该点不在任何圆圈内,则返回“None”:

    library(dplyr)
    
    datNew %>%
      group_by(latitude, longitude) %>% 
      summarise(in_which_circles = if(any(in_circle=="Yes")) paste(circle_id[in_circle=="Yes"], collapse=",") else "None")
    
      latitude longitude in_which_circles
         <dbl>     <dbl>            <chr>
    1 48.15139  17.07562              1,2
    2 48.15144  17.07569              1,2
    3 48.15208  17.07538              1,2
    4 48.15266  17.07330                1
    5 48.15277  17.07514                2
    6 48.15404  17.07452             None
    7 48.15446  17.07517             None
    8 48.15461  17.07560             None
    

    【讨论】:

    • i67.tinypic.com/vgnc0o.png 在这里你可以看到这些圆圈的用途,上面的红色圆圈有 21 个其他位置适合其半径(21 + 1 original = 22)
    • 如果我理解你的图片,那么我只需要一个数据框,其中包含每个圆的中心的纬度和经度以及每个圆的半径,然后我计算样本中每个点的距离从每个圆的中心获取数据,看看它是否在半径范围内。但我仍然不明白你想要的输出是什么。如果给定点在多个圆圈内怎么办?
    • 据我了解,这是有道理的,对于我想要完成的视觉输出来说应该足够了。但是,我将不得不尝试确定:)
    • 如果它在两个圆圈内,它只会保留第一个或第二个的 id,没关系......我在这里不详细介绍。如果这更容易,它甚至可以是方形的,而不是圆形的。唯一的规则是所有数据都被划分为具有半径的一些组,并且每个位置都只分配给一个组。 circle_id 是在算法运行时创建的,我们事先不知道哪个点将被选为新的位置组
    • 我试图让你以前的答案工作,它看起来很棒。但是由于 hasrsine 函数只返回以米为单位的距离,它会不会将两个点放在同一个组中,它们与第一个记录位置的距离相同但角度不同?
    【解决方案2】:

    在我看来,使用 for 循环并不是一个坏主意,有时我更喜欢使用循环来使我的代码更简洁,而不是嵌套 apply

    但是在你的情况下,你可以尝试这样的事情:

    library(dplyr)
    library(tidyr)
    library(purrr)
    
    # I only load the coordinate for now
    df <- tibble(latitude  = c(48.15144, 48.15404, 48.15277, 48.15208, 48.15461, 48.15139, 48.15446, 48.15266),
                 longitude = c(17.07569, 17.07452, 17.07514, 17.07538, 17.07560, 17.07562, 17.07517, 17.07330))
    
    df %>% 
      unite(coord, latitude, longitude, sep = ", ") %>% 
      mutate(coord2 = coord) %>% 
      expand(coord, coord2) %>% 
      mutate(coord = map(coord, function(x) {xx <- as.numeric(unlist(strsplit(x, ","))); tibble(lat = xx[1], lon = xx[2])})) %>% 
      mutate(coord2 = map(coord2, function(x) {xx <- as.numeric(unlist(strsplit(x, ","))); tibble(lat2 = xx[1], lon2 = xx[2])})) %>% 
      unnest() %>% 
      rowwise() %>% 
      mutate(dist = distHaversine(c(lon, lat), c(lon2, lat2))) %>% 
      group_by(lat, lon) %>% 
      mutate(group = 1:n()) %>% 
      group_by(group) %>% 
      filter(dist < 100) %>% 
      group_by(lat, lon) %>% 
      summarise(group = min(group))
    

    您最终对坐标有不同的想法。但是,您的数据顺序会丢失。

    【讨论】:

      猜你喜欢
      • 2016-03-15
      • 1970-01-01
      • 2018-06-11
      • 2020-03-23
      • 1970-01-01
      • 2013-01-11
      • 2018-09-07
      • 2018-05-30
      • 2020-07-13
      相关资源
      最近更新 更多