Как заменить plyr::ddply на data.table

#r #data.table #geospatial #plyr #sf

#r #data.table #геопространственный #plyr #sf

Вопрос:

У меня есть операция агрегирования, которая подсчитывает точки в полигонах в R, которая в настоящее время используется plyr::ddply() в качестве основной функции, где мне нужно сгруппировать по 2 переменным: dayofweek и hour . Это довольно медленно, поэтому я хотел бы заменить его более быстрой функцией, например, чем-нибудь из data.table пакета.

Reprex

Создайте фреймы данных

Основная цель операции — взять фрейм данных из точек df и использовать st_intersects() метод из sf пакета, чтобы подсчитать, сколько точек пересекают полигоны в grid.sf .

Создать объект DF

 library(sf)
library(tidyverse)
library(plyr)

df <- data.frame(X = seq(1,100,1), 
                 dayofweek = rep(c("Sun", "Mon", "Tues", "Wed", "Thur"), 20),
                 hour = sample(seq(0, 23, 1),  100, replace = T),
                 lat = sample(seq(37.1234, 37.2345, 0.001),  100, replace = T),
                 lon = sample(seq(-122.5432, -122.4111, 0.001),  100, replace = T)
)


projcrs <- " proj=longlat  datum=WGS84  no_defs  ellps=WGS84  towgs84=0,0,0"

df <- st_as_sf(x = df,                         
               coords = c("lon", "lat"),
               crs = projcrs)
  

Создайте объект grid.sf

 # Function to create the grid we need
buildBaseGrid <- function(x) {
  
  # create a 500m tesseract over these points
  g <- st_make_grid(x, cellsize = 0.005)
  
  # plot to make sure
  # ggplot()  
  #   geom_sf(data = df.sf, size = 3)  
  #   geom_sf(data = g, alpha = 0)
  # 
  # ggplot()  
  #   geom_sf(data = g, alpha = 0)
  grid.sf <- st_sf(g) 
  # Need to expand by day of week
  days <- c('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA')
  hours <- c('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12',
             '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23')
  grid.sf <- expand.grid(g, days, hours)
  grid.sf$id <- 1:nrow(grid.sf)
  
  #### Clean up home grid
  
  # Calc centroid
  grid.sf <- grid.sf %>%
    dplyr::mutate(center = sf::st_centroid(grid.sf$Var1))
  
  # Parse out lat and lon
  grid.sf <- grid.sf %>%
    dplyr::mutate(lonn = sf::st_coordinates(grid.sf$center)[,1]) %>%
    dplyr::mutate(latt = sf::st_coordinates(grid.sf$center)[,2])
  
  # Create primary key field
  grid.sf <- grid.sf %>%
    dplyr::mutate(pkey = paste0(lonn,";",latt,";",Var2,";",Var3))
  
  
  grid.sf <- st_as_sf(grid.sf) 
  
  return(grid.sf)
  
  
}

# Now build the grid.sf object
grid.sf <- buildBaseGrid(df)

  

Текущая операция с plyr::ddply

 # Create function to use in operation
myf <- function(x) {
  x <- as.data.frame(x)
  df <- df %>% dplyr::filter(dayofweek %in% x$Var2)
  df <- df %>% dplyr::filter(hour %in% x$Var3)
  x$count <- sf::st_intersects(x$Var1, df) %>% lengths()
  x %>%
    data.frame(x)
}

# Do the operation
output <-  plyr::ddply(grid.sf, .(Var2, Var3), .fun = myf, .parallel = F) %>% as.data.frame()

  

На моей машине это занимает около 4 секунд, но мне приходится проделывать этот процесс сотни раз, чтобы он действительно складывался.

Я пытался начать с data.table, но обнаружил, что перевод затруднен. Это единственный (нефункционирующий) код, который у меня есть для этой data.table() попытки:

 test4 <- grid.sf[, .(test = myf()), by = key(grid.sf)]
  

Итак, если есть способ перевести это ddply в data.table операцию, я был бы доволен, так как data.table это намного быстрее.

Спасибо!

Комментарии:

1. что g находится в вашем объекте Create grid.sf? вы можете, вероятно, сделать что-то вроде grid.sf[df, on=c("Var2"="dayofweek", "Var3"="hour"), count := length(st_intersects(Var1, df))] и если вам придется запускать это много раз, было бы неплохо добавить идентификатор итерации в grid.sf, чтобы вы могли просматривать весь набор данных за один раз

2. @chinsoon12 ‘g’ — это просто объект grid, который я преобразую в простой объект в следующей строке

3. извините, пропустил полосу прокрутки справа. предполагая, что ваш день недели помечен последовательно, это должно помочь вам начать: library(data.table); setDT(grid.sf); setDT(df); grid.sf[, Var2 := as.character(Var2)]; grid.sf[, Var3 := as.numeric(Var3)]; grid.sf[df, on=c("Var2"="dayofweek", "Var3"="hour"), count := lengths(st_intersects(Var1, i.geometry))]

4. @chinsoon12 Я получаю сообщение об ошибке при запуске приведенного выше фрагмента

5. В чем ошибка сообщения