R: быстрый / параллельный многоколонный поиск - PullRequest
0 голосов
/ 17 мая 2019

У меня есть много больших (300k - 1M строк) фреймов данных, к которым я пытаюсь добавить значения, перебирая фреймы данных (df_i) и для каждого из них, перебирая строки и спрашивая, какое значение во второмФрейм данных (do2) соответствует широте, долготе, месяцу и глубине.Широта / долгота / месяц будут точно совпадать, глубина сложнее, так как в do2 есть 57 столбцов для значений в увеличивающихся ячейках глубины: do2 head

Ядро моего цикла кода для строки -подмножество 3-х строк и подмножество столбцов:

for (j in 1:nrow(df_i)) {
  df_i[j,"DO2"] <- do2[do2$Latitude == df_i[j,"latbin"] &
                       do2$Longitude == df_i[j,"lonbin"] &
                       do2$Month == month(df_i[j,"DateTimeUTCmin5"]),
                       which.min(abs(depthbins - df_i[j, "Depth.m."])) + 3]
}

Это работает, но медленно.Я знаю, что это может быть ускорено, но мои усилия по распараллеливанию продолжают бить по стенам, и отладка / трассировка намного сложнее параллельно.Я пробовал FBM после прочтения this , но значение

должно быть уникальным или иметь размерность x [i, j]

около 200k строкЯ понимаю, что индексы data.table быстрые , поэтому, возможно, что-то вроде комментарий Фрэнка здесь может сработать, может быть, многострочное подмножество в data.table?Но, по-видимому, это будет тот же подход, что и мое существующее решение (поскольку мне также нужно подмножество / поиск столбцов), просто, может быть, немного быстрее?

Кто-нибудь знает о более разумном подходе?Раньше я был сбит с толку функциями применения, но не удивлюсь, если там что-нибудь полезное?

Заранее спасибо.

Воспроизводимый (упрощенный месяц, добавлены глубинные ячейки, как было опущено ранее):

depthbins <- c(0,5,10,15,20,25,50,75,100,125,150,200,250,300,350,400)
df_i <- data.frame(latbin = c(-77.5, -78, -78.5),
                   lonbin = c(-178.5, -177.5, -176.5),
                   month = c(1,2,3),
                   Depth.m. = c(130,120,110))
do2 <- tibble(Month = c(1,1,1),
              Latitude = c(-78,-78,-79),
              Longitude = c(-178.5, -177.5, -177.5),
              "0" = c(214, 223, 345),
              "5" = c(123,234,345),
              "10" = c(345,456,567))

Окончательное редактирование: некоторые изменения в коде Мариуса:

do2 %<>% gather(.vars = colnames(do2)[4:length(colnames(do2))],
                key = "depbin", value = "DO2")
do2$depbin <- as.numeric(do2$depbin)
depthbins <- sort(unique(do2$depbin))
df_i$depbin = sapply(df_i$Depth.m., function(d) depthbins[which.min(abs(depthbins - d))])

df_i %<>% left_join(do2, by = c("Month" = "Month",
                                "latbin" = "Latitude",
                                "lonbin" = "Longitude",
                                "depbin" = "depbin")) %>%
          select(-Month, -latbin, -lonbin, -depbin)

1 Ответ

1 голос
/ 17 мая 2019

Я думаю, что с небольшой реорганизацией вы можете сделать это как слияние. Часть слияния должна быть намного, намного быстрее, чем ваш подход for loop, который будет слегка смещен из-за увеличенного размера do2 и времени подготовки. Примечание. Мне пришлось немного изменить данные вашего примера, чтобы в каждой строке было что-то для сравнения:

depthbins <- c(0,5,10,15,20,25,50,75,100,125,150,200,250,300,350,400)
df_i <- data.frame(latbin = c(-77.5, -78, -78.5),
                   lonbin = c(-178.5, -177.5, -176.5),
                   month = c(1,2,3),
                   Depth.m. = c(130,120,110))
do2 <- tibble(Month = c(1,2,3),
              Latitude = c(-77.5,-78,-78.5),
              Longitude = c(-178.5, -177.5, -176.5),
              "100" = c(214, 223, 345),
              "125" = c(123,234,345),
              "150" = c(345,456,567))


library(tidyverse)
# Precalculate closest bin for each row
df_i$bin = sapply(df_i$Depth.m., function(d) depthbins[which.min(abs(depthbins - d))])

# Convert do2 to long
do2_long = do2 %>%
    gather(bin, DO2, -Month, -Latitude, -Longitude) %>%
    mutate(bin = as.numeric(bin))

# Now everything can just be done as a merge
# The merge syntax would be a bit cleaner if you give the two df's
#   matching column names to start with
df_i %>%
    left_join(do2_long, by = c("month" = "Month", "latbin" = "Latitude", 
                               "lonbin" = "Longitude", "bin" = "bin"))
...