Я ищу способ сделать sh максимально эффективным, как я работаю с большими наборами данных (всего ~ 7 миллионов строк). Сравнение dplyr
против data.table
было бы очень полезно. В любой день года я хочу знать, сколько акций у того или иного поставщика. Мы знаем, сколько на складе в 1-й день года, и это вопрос создания скользящей суммы на основе того, перемещен ли запас from
или to
другому поставщику. Каждый ряд представляет одну часть перемещаемого запаса. Если есть NA
, это просто означает, что есть внешний ход from
или to
и обрабатывается таким же образом. Хотя запас не может упасть ниже 0 (см. Поставщика a
ниже), и если это происходит в любом из поставщиков, кумулятивная сумма должна быть установлена на ноль, а сумма продолжена. Я не думаю, что решение с широким форматом имеет здесь смысл, поскольку у меня более 100000 поставщиков.
Небольшой пример:
library(tidyverse)
library(data.table)
set.seed(100)
df <- data.frame(date = sample(seq.Date(from = as.Date("01/01/2018", "%d/%m/%Y"),
to=as.Date("30/01/2018", "%d/%m/%Y"), by = "day"), 20, replace = TRUE),
from = sample(letters[c(1:4, 12)], 20, replace = TRUE),
to = sample(letters[c(1:4, 14, 20)], 20, replace = TRUE), stringsAsFactors = FALSE) %>%
dplyr::arrange(date)
df[14, 2] <- NA
df[10, 3] <- NA
df[5, 3] <- NA
df[6, 2] <- NA
df
# date from to
# 1 2018-01-02 c t
# 2 2018-01-04 l c
# 3 2018-01-06 d n
# 4 2018-01-06 d t
# 5 2018-01-06 a <NA>
# 6 2018-01-07 <NA> d
# 7 2018-01-07 b t
# 8 2018-01-10 b t
# 9 2018-01-11 l n
# 10 2018-01-12 c <NA>
# 11 2018-01-14 b t
# 12 2018-01-16 c a
# 13 2018-01-19 c n
# 14 2018-01-22 <NA> a
# 15 2018-01-23 l t
# 16 2018-01-23 d a
# 17 2018-01-23 c a
# 18 2018-01-23 l c
# 19 2018-01-25 b d
# 20 2018-01-26 a c
и подсчет запаса базовой линии на 1-й день года для все поставщики:
base_line <- data.frame(supplier =c("l", "b", "d", "c", "a", "n", "t"),
count = c(10, 20, 12, 5, 0, 2, 10))
base_line
# supplier count
# 1 l 10
# 2 b 20
# 3 d 12
# 4 c 5
# 5 a 0
# 6 n 2
# 7 t 10
Желаемый результат (количество товара на каждый день):
date from to cumsum_var supplier
1 2018-01-02 c t 11 t
2 2018-01-06 d t 12 t
3 2018-01-07 b t 13 t
4 2018-01-10 b t 14 t
5 2018-01-14 b t 15 t
6 2018-01-23 l t 16 t
7 2018-01-06 d n 3 n
8 2018-01-11 l n 4 n
9 2018-01-19 c n 5 n
10 2018-01-06 a <NA> 0 a note 0, not -1
11 2018-01-16 c a 1 a
12 2018-01-22 <NA> a 2 a
13 2018-01-23 d a 3 a
14 2018-01-23 c a 4 a
15 2018-01-26 a c 3 a
16 2018-01-06 d n 11 d
17 2018-01-06 d t 10 d
18 2018-01-07 <NA> d 11 d
19 2018-01-23 d a 10 d
20 2018-01-25 b d 11 d
21 2018-01-02 c t 4 c
22 2018-01-04 l c 5 c
23 2018-01-12 c <NA> 4 c
24 2018-01-16 c a 3 c
25 2018-01-19 c n 2 c
26 2018-01-23 c a 1 c
27 2018-01-23 l c 2 c
28 2018-01-26 a c 3 c
29 2018-01-07 b t 19 b
30 2018-01-10 b t 18 b
31 2018-01-14 b t 17 b
32 2018-01-25 b d 16 b
33 2018-01-04 l c 9 l
34 2018-01-11 l n 8 l
35 2018-01-23 l t 7 l
36 2018-01-23 l c 6 l
Мой подход было до filter
обоих наборов данных, основанных на поставщике, выполните cumsum
и затем полностью объедините их в список в конце, но не учтите, что подсчет запасов не может go ниже 0 (см. Проблему с a
в моем выводе).
base_line2 <- data.frame(date = rep(as.Date("31/12/2017", "%d/%m/%Y"), 7),
from = c("l", "b", "d", "c", "a", "n", "t"),
from_new = c(10, 20, 12, 5, 0, 2, 10), stringsAsFactors = FALSE)
#get all suppliers (in real dataset >100000)
vars2 <- c("l", "b", "d", "c", "a", "n", "t")
#function
my_fun <- function(x) {
df %>%
filter_at(vars(from, to), any_vars(. == {{x}})) %>%
mutate(from_new = ifelse(from == {{x}}, -1, 0),
to_new = ifelse(to == {{x}}, 1, 0)) %>%
bind_rows({base_line2 %>% filter(from == {{x}})}) %>%
dplyr::arrange(date) %>%
mutate(count_test = rowSums(select(., from_new, to_new), na.rm = T),
cumsum_var = cumsum(count_test))
}
#use function over list
tmp <- lapply(vars2, my_fun)
output = rbindlist(tmp)
output
output %>%
filter(date > as.Date("2017-12-31"))
# date from to from_new to_new count_test cumsum_var
# 1 2018-01-04 l c -1 0 -1 9
# 2 2018-01-11 l n -1 0 -1 8
# 3 2018-01-23 l t -1 0 -1 7
# 4 2018-01-23 l c -1 0 -1 6
# 5 2018-01-07 b t -1 0 -1 19
# 6 2018-01-10 b t -1 0 -1 18
# 7 2018-01-14 b t -1 0 -1 17
# 8 2018-01-25 b d -1 0 -1 16
# 9 2018-01-06 d n -1 0 -1 11
# 10 2018-01-06 d t -1 0 -1 10
# 11 2018-01-07 <NA> d NA 1 1 11
# 12 2018-01-23 d a -1 0 -1 10
# 13 2018-01-25 b d 0 1 1 11
# 14 2018-01-02 c t -1 0 -1 4
# 15 2018-01-04 l c 0 1 1 5
# 16 2018-01-12 c <NA> -1 NA -1 4
# 17 2018-01-16 c a -1 0 -1 3
# 18 2018-01-19 c n -1 0 -1 2
# 19 2018-01-23 c a -1 0 -1 1
# 20 2018-01-23 l c 0 1 1 2
# 21 2018-01-26 a c 0 1 1 3
# 22 2018-01-06 a <NA> -1 NA -1 -1
# 23 2018-01-16 c a 0 1 1 0
# 24 2018-01-22 <NA> a NA 1 1 1
# 25 2018-01-23 d a 0 1 1 2
# 26 2018-01-23 c a 0 1 1 3
# 27 2018-01-26 a c -1 0 -1 2
# 28 2018-01-06 d n 0 1 1 3
# 29 2018-01-11 l n 0 1 1 4
# 30 2018-01-19 c n 0 1 1 5
# 31 2018-01-02 c t 0 1 1 11
# 32 2018-01-06 d t 0 1 1 12
# 33 2018-01-07 b t 0 1 1 13
# 34 2018-01-10 b t 0 1 1 14
# 35 2018-01-14 b t 0 1 1 15
# 36 2018-01-23 l t 0 1 1 16
Я думаю, что вместо этого подход data.table
может повысить эффективность или вообще лучше dplyr
? У кого-нибудь есть предложения по эффективному хранению акций на уровне 0 или выше?
спасибо