#r #dataframe #for-loop
#r #фрейм данных #для цикла
Вопрос:
У меня есть фрейм данных с двумя строками, которые представляют пассажиров, загружающихся в автобус, и пассажиров, покидающих автобус:
A B C D E F
In 9 10 6 9 14 10
Out 0 1 2 3 4 3
И я хотел выполнить вычисление, которое приведет к получению еще двух строк информации, где первая — это пассажиры, которые находятся в автобусе, когда он прибывает на станцию A / B / C / etc, а строка 2 — это количество пассажиров, которые вышли из автобуса на этой станции.
Числа в строке 1 должны совпадать с предыдущими числами в строке 2, а строка 2 для станции B, например, равна `9 (количество людей, оставшихся в автобусе с предыдущей остановки) BIn (количество людей, садящихся на остановке) — BOut(количество людей, которые выходят из автобуса на остановке).
Конечный результат должен выглядеть следующим образом:
A B C D E F
In 9 10 6 9 14 10
Out 0 1 2 3 4 3
1 0 9 18 22 28 38
2 9 18 22 28 38 45
Как мне выполнить итерацию по фрейму данных, чтобы я мог получить эти числа? Необходим ли цикл for или есть более простой способ выполнить это вычисление?
Ответ №1:
Во-первых, я думаю, что имеет больше смысла иметь их в виде столбцов, а не строк. Таким образом, вы можете воспользоваться преимуществами векторизованных операций в R.
library(data.table)
df <- suppressWarnings(fread('
A B C D E F
In 9 10 6 9 14 10
Out 0 1 2 3 4 3'))
setDT(df) # only required if not starting with a data.table
df
#> V1 A B C D E F
#> <char> <int> <int> <int> <int> <int> <int>
#> 1: In 9 10 6 9 14 10
#> 2: Out 0 1 2 3 4 3
df_tp <- transpose(df, make.names = 'V1', keep.names = 'station')
df_tp
#> station In Out
#> <char> <int> <int>
#> 1: A 9 0
#> 2: B 10 1
#> 3: C 6 2
#> 4: D 9 3
#> 5: E 14 4
#> 6: F 10 3
Теперь ваша последняя строка — это совокупная сумма In минус совокупная сумма Out . Другой — это просто запаздывающая версия этого.
df_tp[, net := cumsum(In) - cumsum(Out)]
df_tp[, lagged_net := shift(net, fill = 0)]
df_tp
#> station In Out net lagged_net
#> <char> <int> <int> <int> <int>
#> 1: A 9 0 9 0
#> 2: B 10 1 18 9
#> 3: C 6 2 22 18
#> 4: D 9 3 28 22
#> 5: E 14 4 38 28
#> 6: F 10 3 45 38
Создано 2021-12-07 пакетом reprex (v2.0.1)
Комментарии:
1. Спасибо! Я не слишком хорошо знаком с векторными операциями, поэтому мне определенно придется искать некоторые из этих функций.
Ответ №2:
Я думаю, вам следует воспользоваться советом и ответом @IceCreamToucan, но если вы хотите сохранить ту же структуру по определенным причинам, этот неэлегантный for
цикл перебора приведет к желаемому результату:
df <- data.frame(A = c(9,0),
B = c(10,1),
C = c(6,2),
D = c(9,3),
E = c(14, 4),
F = c(10, 3))
for (i in 1:ncol(df)){
if (i == 1){df[3:4,1] <- c(0,df[1,1])}
else{
df[3,i] <- df[4,i-1]
df[4,i] <- sum(df[4,i-1], df[1,i]) - df[2,i]
}
}
df
# A B C D E F
#1 9 10 6 9 14 10
#2 0 1 2 3 4 3
#3 0 9 18 22 28 38
#4 9 18 22 28 38 45
Комментарии:
1. Спасибо! Я посмотрю на ответ @IceCreamToucan (я новичок в векторных операциях), но это то, что я ищу, если бы хотел использовать циклы for
2. Как бы я это сделал для списка фреймов данных?
3. Определите
for
цикл как afunction
(т. Е.loopfun <- function(x) { ...code here...}
Затем используйтеlapply
, что-то вродеlapply(df.lists, loopfun)
Ответ №3:
Или для tidyverse
способа сделать это:
Загрузите данные в том формате, в котором вы ими поделились:
library(tidyverse)
df <- data.frame(A = c(9,0),
B = c(10,1),
C = c(6,2),
D = c(9,3),
E = c(14,4),
F = c(10,3))
> df
A B C D E F
1 9 10 6 9 14 10
2 0 1 2 3 4 3
Преобразование в длинный формат:
df <- as_tibble(t(df), rownames = "row_names") %>%
rename('In' = V1, 'Out' = V2)
> df
# A tibble: 6 x 3
row_names In Out
<chr> <dbl> <dbl>
1 A 9 0
2 B 10 1
3 C 6 2
4 D 9 3
5 E 14 4
6 F 10 3
Добавьте переменные, которые вы хотите с cumsum
и lag
:
df %>% mutate(net = cumsum(In) - cumsum(Out),
lag = replace_na(lag(net), 0))
> df
# A tibble: 6 x 5
row_names In Out net lag
<chr> <dbl> <dbl> <dbl> <dbl>
1 A 9 0 9 0
2 B 10 1 18 9
3 C 6 2 22 18
4 D 9 3 28 22
5 E 14 4 38 28
6 F 10 3 45 38