#r
#r
Вопрос:
Я пытаюсь сделать следующее в data.table или создать функцию в замене цикла for. Однако я не уверен, как вернуть два столбца с одним в зависимости от вычисления другого. Набор данных содержит единицы продаж и доставки для каждого «места» по месяцам, однако, только начальный инвентарь за первый месяц. Мне нужно рассчитать начальный запас каждого периода, сначала рассчитав конечный запас за последний месяц в этом месте. Конечные запасы для каждого места равны начальным запасам минус единицы продаж плюс единицы доставки.
Вот как я сейчас вычисляю:
data <- data.table(place = c('a','b'),
month = c(1,1,2,2,3,3,4,4,5,5,6,6),
sales = c(20,2,3,5,6,7,8,1,5,1,5,3),
delivery = c(1,1,1,1,1,1,1,1,1,1,1,1),
starting_inv = c(100,100,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA),
ending_inv = c(81,99,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA) )
print(data)
place month sales delivery starting_inv ending_inv
1: a 1 20 1 100 81
2: b 1 2 1 100 99
3: a 2 3 1 NA NA
4: b 2 5 1 NA NA
5: a 3 6 1 NA NA
6: b 3 7 1 NA NA
7: a 4 8 1 NA NA
8: b 4 1 1 NA NA
9: a 5 5 1 NA NA
10: b 5 1 1 NA NA
11: a 6 5 1 NA NA
12: b 6 3 1 NA NA
dt <- data[order(place,month)]
print(dt)
place month sales delivery starting_inv ending_inv
1: a 1 20 1 100 81
2: a 2 3 1 NA NA
3: a 3 6 1 NA NA
4: a 4 8 1 NA NA
5: a 5 5 1 NA NA
6: a 6 5 1 NA NA
7: b 1 2 1 100 99
8: b 2 5 1 NA NA
9: b 3 7 1 NA NA
10: b 4 1 1 NA NA
11: b 5 1 1 NA NA
12: b 6 3 1 NA NA
for (i in 1:nrow(dt)) {
if (dt[i]$month != 1) {
dt$starting_inv[i] <- dt[i-1]$ending_inv
dt$ending_inv[i] <- dt[i]$starting_inv - dt[i]$sales dt[i]$delivery
}
}
print(dt)
place month sales delivery starting_inv ending_inv
1: a 1 20 1 100 81
2: a 2 3 1 81 79
3: a 3 6 1 79 74
4: a 4 8 1 74 67
5: a 5 5 1 67 63
6: a 6 5 1 63 59
7: b 1 2 1 100 99
8: b 2 5 1 99 95
9: b 3 7 1 95 89
10: b 4 1 1 89 89
11: b 5 1 1 89 89
12: b 6 3 1 89 87
Я хотел бы избежать шага, который требует сортировки таблицы по месту и месяцу. Затем вычисление этого в таблице с гораздо большим количеством данных занимает слишком много времени, и у меня возникают проблемы с преобразованием этого в векторизованную функцию.
Ответ №1:
Итерация фиксируется совокупной суммой, остальное затем может быть векторизовано, поэтому должно быть быстрым.
data[, starting_inv := cumsum(shift(delivery-sales, fill = starting_inv[1])), place]
data[, ending_inv := starting_inv delivery-sales]
data
#> place month sales delivery starting_inv ending_inv
#> 1: a 1 20 1 100 81
#> 2: b 1 2 1 100 99
#> 3: a 2 3 1 81 79
#> 4: b 2 5 1 99 95
#> 5: a 3 6 1 79 74
#> 6: b 3 7 1 95 89
#> 7: a 4 8 1 74 67
#> 8: b 4 1 1 89 89
#> 9: a 5 5 1 67 63
#> 10: b 5 1 1 89 89
#> 11: a 6 5 1 63 59
#> 12: b 6 3 1 89 87
Это предполагает, что фактические данные, с которыми вы имеете дело, упорядочены по month
. Если это не так, то вставьте order(month)
после первой квадратной скобки в первой строке.
Ответ №2:
Вот один вариант с accumulate2
из purrr
library(purrr)
library(dplyr)
library(tidyr)
dt %>%
group_by(place) %>%
dplyr::mutate(starting_inv = accumulate2(delivery, sales,
~ ..1 - ..3 ..2 , .init = first(starting_inv))[-n()]) %>%
unnest(c(starting_inv)) %>%
mutate(ending_inv = lead(starting_inv))
# A tibble: 12 x 6
# Groups: place [2]
# place month sales delivery starting_inv ending_inv
# <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 a 1 20 1 100 81
# 2 a 2 3 1 81 79
# 3 a 3 6 1 79 74
# 4 a 4 8 1 74 67
# 5 a 5 5 1 67 59
# 6 a 6 5 1 59 NA
# 7 b 1 2 1 100 99
# 8 b 2 5 1 99 95
# 9 b 3 7 1 95 89
#10 b 4 1 1 89 89
#11 b 5 1 1 89 87
#12 b 6 3 1 87 NA
Это также можно использовать вместе с data.table
dt[, starting_inv := unlist(accumulate2(delivery, sales,
function(x, y, z) x - z y ,
.init = first(starting_inv))[-.N]), place][, ending_inv :=
shift(starting_inv, type = 'lead'), place]