大型空间数据集的更快且内存高效的距离计算

问题描述 投票:0回答:1

我试图使用两个标准来匹配两个数据集(我们称它们为 dfa 和 dfb):距离和时间。两者都有事件发生的时间/地点的准确时间和坐标。我想将 dfb 中 a) 2 公里内和 b) 之前 12 小时内发生的事件数分配给 dfa。数据库 dfa 有 1300 万个观测值,数据库 dfb 有 50 万个观测值。

我使用了两种不同的方法:a)使用矩阵和b)for循环。然而,对于第一种方法,我的内存不足,而对于第二种方法,我需要 6 天以上的时间。有没有更有效的方法来完成这项任务?

示例数据集:

require(chron)

dfa<-data.frame(matrix(ncol = 6, nrow = 13000000))
colnames(dfa)<-c('eventid','date','time','date_time','lat','long')
dfa$eventid<-1:nrow(dfa)
dfa$date<-as.Date(sample(as.Date("2015-01-01"):as.Date("2020-12-31"),2000, replace = T),origin = '1970-01-01')
dfa$time<-sample(0:23,2000,replace=T)
dfa$date_time<-chron(dates=as.numeric(dfa$date),
                      times=paste0(dfa$time,":00:00"),
                      format=c(dates="Y-M-D",times="h:m:s"))
dfa$lat<-sample(-4.5:1.5,2000,replace=T)
dfa$long<-sample(-90.5:-75.5,2000,replace=T)

dfb<-data.frame(matrix(ncol = 6, nrow = 500000))
colnames(dfb)<-c('eventid','date','time','date_time','lat','long')
dfb$eventid<-1:nrow(dfb)
dfb$date<-as.Date(sample(as.Date("2015-01-01"):as.Date("2020-12-31"), 2000, replace = T),origin = '1970-01-01')
dfb$time<-sample(0:23,2000,replace=T)
dfb$date_time<-chron(dates=as.numeric(dfb$date),
                     times=paste0(dfb$time,":00:00"),
                     format=c(dates="Y-M-D",times="h:m:s"))
dfb$lat<-sample(-4.5:1.5,2000,replace=T)
dfb$long<-sample(-90.5:-75.5,2000,replace=T)

For 循环选项:

require(spatialrisk)
data<-data.frame(matrix(ncol = ncol(dfa), nrow = 1))
colnames(data)<-names(dfa)
data$nbr<-NA
data$timediff<-NA
data$distance<-NA

nro<-0
for(i in 1:nrow(dfa)){
  nro<-nro+1
  temp<-as.data.frame(dfa[i,])
  buff<-points_in_circle(dfb, temp[,6], temp[,5], long, lat, radius= 2000)
  buff$timediff<-buff$date_time-temp$date_time
  buff<-subset(buff,buff$timediff>=0&buff$timediff<=0.5)
  if(nrow(buff)>0){
    temp$nbr<-nrow(buff)
    temp$timediff<-toString(buff$timediff)
    temp$distance<-toString(buff$distance)
  } else {
    temp$nbr<-0
    temp$timediff<-NA
    temp$distance<-NA
  }
  data<-rbind(data,temp)
  
  #Process:
  print(paste0(round(nro/nrow(dfa)*100,5),"% done!"))
}
r geospatial
1个回答
0
投票

下面描述的方法要快得多,不到一分钟即可完成示例数据集的计算。加速主要是由于数据中只有 2K 个唯一的位置/时间组合。一旦按位置和时间汇总观察结果和事件,所得表格就会小得多

按位置和时间总结

dfa
dfb
后,我们的想法是首先识别在其时间窗口内进行每个观测的
dfa
的子集。然后,仅针对那些观察包含在其时间窗口中的事件(在
points_in_circle
中)对每个观察(在
dfb
中)调用
dfa

library(data.table)

system.time({
  dfb <- setDT(dfb)[,.(nbr = .N), .(date_time, lat, long)]
  dfa <- setDT(dfa)[,.(eventid = .(eventid)), .(date_time, lat, long)]
  setkey(dfa, date_time)
  setkey(dfb, date_time)
  dfa[,`:=`(r = .I, nbr = 0L)]
  
  dfb[,date_time12 := date_time + 0.5][
    , c("r1", "r2") := dfa[
      dfa[dfb, .(date_time12 = date_time12, r1 = r), roll = -Inf, mult = "first"],
      .(r1 = r1, r2 = r), on = .(date_time = date_time12),
      roll = Inf, mult = "first"
    ]
  ][r1 <= r2][dfa$date_time[r1] >= date_time][,r := .I][
    , {
      i <- points_in_circle(dfa, long, lat, long, lat, radius = 2e3)$r
      dfa$nbr[i] <<- dfa$nbr[i] + nbr
      NULL
    }, by = r
  ]
  
  dfa <- dfa[
    ,lapply(.SD, \(x) if (is.list(x)) unlist(x) else rep(x, lengths(eventid)))
  ]
  
  setorder(dfa, eventid)
})
#>    user  system elapsed 
#>   28.28   11.38   35.87

结果:

dfa
#>                     date_time  lat  long  eventid    r  nbr
#>        1: (15-09-25 08:00:00)  1.5 -80.5        1  248 2000
#>        2: (17-07-28 12:00:00) -0.5 -83.5        2  894 1000
#>        3: (17-10-15 02:00:00) -1.5 -86.5        3  962 1500
#>        4: (20-07-01 20:00:00) -1.5 -83.5        4 1843 1250
#>        5: (18-10-17 04:00:00) -0.5 -78.5        5 1289 1250
#>       ---                                                  
#> 12999996: (20-03-28 03:00:00)  0.5 -78.5 12999996 1765 2000
#> 12999997: (15-02-13 17:00:00) -2.5 -87.5 12999997   45 2500
#> 12999998: (15-07-18 18:00:00) -1.5 -85.5 12999998  185 1000
#> 12999999: (16-07-19 04:00:00)  1.5 -75.5 12999999  554 1250
#> 13000000: (16-05-29 15:00:00) -3.5 -87.5 13000000  506 2250
© www.soinside.com 2019 - 2024. All rights reserved.