Я не могу сравнить с тем, что вы сделали, так как вы не поделились кодом для функции duration
. Я могу обновить сравнение, если вы сделаете это.
Некоторые варианты приведены ниже. Я попробовал это с базой R (я использую 3.4.1), используя параллельную обработку и dplyr
(0.7.7, так что я немного устарел). Несколько лет назад я читал, что dplyr
может быть немного медленным при работе с большим количеством групп, но я не знаю, так ли это до сих пор. В этом конкретном примере кажется, что dplyr
все еще примерно в два раза быстрее даже с 30 группами. Его запас превосходства растет через 6000 групп.
Я уверен, что есть datatable
способ сделать это, но я не очень разбираюсь в этой структуре, поэтому я не буду пытаться добавить его сам.
В такой проблеме я ожидал бы, что добавление большего числа отдельных пользователей вызовет более медленное замедление, чем добавление большего количества строк на пользователя, и даже при 6000 пользователей я все еще обрабатываю это за четыре секунды, используя мой самый медленный подход. Это заставляет меня думать, что ваша duration
функция не очень хорошо оптимизирована для этой проблемы. Я бы посоветовал вам поделиться этим кодом, чтобы мы могли показать вам, каких ловушек следует избегать. (Я предполагаю, что duration
является функцией вашего собственного создания, поскольку ?duration
ничего для меня не вызывает)
# Base R
calculate_duration <- function(df){
df$end_time <- dplyr::lead(df$time)
df <- df[df$state == "on", ]
df$duration <- difftime(df$end_time, df$time, units = "secs")
df
}
conn <- connections[order(connections$name, connections$time), ]
conn <- split(conn, conn$name)
conn <- lapply(conn, calculate_duration)
conn <- do.call("rbind", conn)
conn
# parallel
library(parallel)
cl <- makeCluster(detectCores() - 1)
clusterExport(cl, "calculate_duration")
conn <- connections[order(connections$name, connections$time), ]
conn <- split(conn, conn$name)
conn <- parLapply(cl, conn, calculate_duration)
conn <- do.call("rbind", conn)
conn
stopCluster(cl)
# dplyr
library(dplyr)
connections %>%
arrange(name, time) %>%
group_by(name) %>%
mutate(end_time = lead(time)) %>%
filter(state == "on") %>%
mutate(duration = difftime(end_time, time, units = "secs"))
Тесты
ndup_option <- c(1, 10, 50, 100, 500, 2000)
for (i in ndup_option){
connections <- data.frame(name = c("sam","sam","bob","mike","bob","mike","sam","sam","bob","mike","bob","mike"),
state = c("on","off","on","on","off","off","on","off","on","on","off","off"),
time = as.POSIXct(c("2019-06-19 00:00:02","2019-06-19 01:11:22",
"2019-06-19 10:09:19","2019-06-19 02:12:06",
"2019-06-19 10:45:18","2019-06-19 06:24:43",
"2019-06-19 14:12:06","2019-06-19 15:15:43",
"2019-06-19 17:54:08","2019-06-19 15:17:47",
"2019-06-19 19:27:55","2019-06-19 21:22:36")),
stringsAsFactors = FALSE)
ndup <- i
conn_list <- vector("list", ndup)
for (j in seq_len(ndup)){
tmp <- connections
tmp$name <- sprintf("%s%s", connections$name, j)
conn_list[[j]] <- tmp
}
connections <- do.call("rbind", conn_list)
out <- microbenchmark(
base = {
conn <- connections[order(connections$name, connections$time), ]
conn <- split(conn, conn$name)
conn <- lapply(conn, calculate_duration)
conn <- do.call("rbind", conn)
conn
},
parallel = {
conn <- connections[order(connections$name, connections$time), ]
conn <- split(conn, conn$name)
conn <- parLapply(cl, conn, calculate_duration)
conn <- do.call("rbind", conn)
conn
},
dplyr = {
connections %>%
arrange(name, time) %>%
group_by(name) %>%
mutate(end_time = lead(time)) %>%
filter(state == "on") %>%
mutate(duration = difftime(end_time, time))
},
times = 10
)
message(sprintf("Benchmark for %s groups:",
length(unique(connections$name))))
print(out)
}
Benchmark for 3 groups:
Unit: milliseconds
expr min lq mean median uq max neval
base 1.387948 1.390001 1.481524 1.491905 1.564191 1.587064 10
parallel 3.535705 4.435393 4.557561 4.734508 4.965587 5.136552 10
dplyr 3.222515 3.272074 4.515245 3.534385 3.676465 13.978893 10
Benchmark for 30 groups:
Unit: milliseconds
expr min lq mean median uq max neval
base 10.800952 11.201530 12.020516 11.339943 11.670142 15.609064 10
parallel 9.992464 10.990684 11.496216 11.497125 12.073653 12.782729 10
dplyr 5.699297 6.019231 6.056093 6.092104 6.127148 6.347964 10
Benchmark for 150 groups:
Unit: milliseconds
expr min lq mean median uq max neval
base 53.52881 54.47601 57.01772 56.41424 58.62636 63.47641 10
parallel 35.42127 37.91183 39.10031 39.05301 39.89185 42.53285 10
dplyr 17.31812 17.52427 19.01509 17.93761 19.95384 24.30478 10
Benchmark for 300 groups:
Unit: milliseconds
expr min lq mean median uq max neval
base 111.00359 114.57917 119.35063 117.68527 123.43090 131.21256 10
parallel 71.35337 72.95979 76.53514 76.74857 79.88487 84.47363 10
dplyr 31.50023 32.21957 33.53007 33.71852 34.38932 36.30571 10
Benchmark for 1500 groups:
Unit: milliseconds
expr min lq mean median uq max neval
base 647.6213 677.7236 692.9642 687.5113 725.7461 741.0704 10
parallel 423.8719 437.3221 485.8728 449.2985 506.2456 627.6593 10
dplyr 159.6991 166.2746 171.3379 169.5461 173.5134 187.0929 10
Benchmark for 6000 groups:
Unit: milliseconds
expr min lq mean median uq max neval
base 3454.545 3517.1275 3616.9079 3579.5780 3773.7299 3789.4571 10
parallel 2506.242 2556.1848 2601.5728 2607.1978 2639.6757 2695.3605 10
dplyr 657.422 681.0403 704.7698 691.7343 713.3387 784.5798 10