Перебирать фрейм данных для вычисления

#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 цикл как a function (т. Е. 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