Рассчитать среднемесячную сумму по группам из таблицы данных. В R

У меня есть таблица data.table со строкой для каждого дня в течение 30-летнего периода с несколькими различными столбцами переменной. Причиной использования data.table является то, что файл CSV, который я использую, огромен (приблизительно 1,2 миллиона строк), поскольку для нескольких групп, характеризуемых столбцом под названием "ключ", существует 30-летняя стоимость данных.

Примерный набор данных показан ниже:

Key   Date          Runoff
A     1980-01-01    2
A     1980-01-02    1
A     1981-01-01    0.1
A     1981-01-02    3
A     1982-01-01    2
A     1982-01-02    5
B     1980-01-01    1.5
B     1980-01-02    0.5
B     1981-01-01    0.3
B     1981-01-02    2
B     1982-01-01    1.5
B     1982-01-02    4

Выше приведен пример двух "ключей", с некоторыми данными за январь в течение трех лет, чтобы показать, что я имею в виду. Фактический набор данных содержит сотни "ключей" и 30-летнюю ценность данных для каждого "ключа".

То, что я хочу сделать, это вывести вывод, который имеет общее среднее значение для каждого месяца для каждого ключа, как показано ниже:

Key   January  February  March.... etc
A     4.36     ...       ...
B     3.26     ...       ...

то есть. общее среднее значение для января для ключа A = (2 + 1) + (0,1 + 3) + (2 + 5)/3

Когда я сделал этот анализ на одном наборе данных за тридцать лет (т.е. только один ключ), я успешно использовал следующий код для этого:

runoff_tot_average <- rowsum(DF$Runoff, format(DF$Date, '%m')) / 30

Где DF - это кадр данных для одного набора данных за 30 лет.

Могу ли я получить предложения по изменению моего кода выше, чтобы работать с большим набором данных со многими "ключами" или предложить совершенно новое решение?

Спасибо,

J

ИЗМЕНИТЬ

В приведенном ниже примере приведен пример данных выше:

Key <- c("A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B")
Date <- as.Date(c("1980-01-01", "1980-01-02", "1981-01-01", "1981-01-02", "1982-01-01", "1982-01-02", "1980-01-01", "1980-01-02", "1981-01-01", "1981-01-02", "1982-01-01", "1982-01-02"))
Runoff <- c(2, 1, 0.1, 3, 2, 5, 1.5, 0.5, 0.3, 2, 1.5, 4)
DT <- data.table(Key, Date, Runoff)

Ответ 1

Только так я мог подумать, что это было в два шага. Вероятно, это не самый лучший способ, но здесь идет

DT[, c("YM", "Month") := list(substr(Date, 1, 7), substr(Date, 6, 7))]
DT[, Runoff2 := sum(Runoff), by = c("Key", "YM")]
DT[, mean(Runoff2), by = c("Key", "Month")]

##   Key Month       V1
## 1:   A    01 4.366667
## 2:   B    01 3.266667

Просто чтобы показать другой (очень похожий) способ:

DT[, c("year", "month") := list(year(Date), month(Date))]
DT[, Runoff2 := sum(Runoff), by=list(Key, year, month)]
DT[, mean(Runoff2), by=list(Key, month)]

Обратите внимание, что вам не нужно создавать новые столбцы, так как by также поддерживает выражения. То есть вы можете напрямую использовать их в by следующим образом:

DT[, Runoff2 := sum(Runoff), by=list(Key, year = year(Date), month = month(Date))]

Но поскольку вам требуется агрегировать более одного раза, лучше (для скорости) хранить их как дополнительные столбцы, как показано здесь @David.

Ответ 2

Если вы не ищете сложные функции и просто хотите получить среднее значение, тогда должно быть достаточно следующего:

DT[, sum(Runoff) / length(unique(year(Date))), list(Key, month(Date))]
#   Key month       V1
#1:   A     1 4.366667
#2:   B     1 3.266667

Ответ 3

Поскольку вы сказали в своем вопросе, что вы были бы открыты для совершенно нового решения, вы можете попробовать следующее с dplyr:

df$Date <- as.Date(df$Date, format="%Y-%m-%d")
df$Year.Month <- format(df$Date, '%Y-%m')
df$Month <- format(df$Date, '%m')

require(dplyr)

df %>%
  group_by(Key, Year.Month, Month) %>%
  summarize(Runoff = sum(Runoff)) %>%
  ungroup() %>%
  group_by(Key, Month) %>%
  summarize(mean(Runoff))

ИЗМЕНИТЬ № 1 после комментария от @Henrik: То же самое можно сделать:

df %>%
  group_by(Key, Month, Year.Month) %>%
  summarize(Runoff = sum(Runoff)) %>%
  summarize(mean(Runoff))

ИЗМЕНИТЬ № 2: Это еще один способ сделать это (вторая группировка более явна именно так) благодаря @Henrik за его комментарии

df %>%
  group_by(Key, Month, Year.Month) %>%
  summarize(Runoff = sum(Runoff)) %>%
  group_by(Key, Month, add = FALSE) %>%    #now grouping by Key and Month, but not Year.Month
  summarize(mean(Runoff))

Он производит следующий результат:

#Source: local data frame [2 x 3]
#Groups: Key
#
#  Key Month mean(Runoff)
#1   A    01     4.366667
#2   B    01     3.266667

Затем вы можете изменить выход, чтобы он соответствовал желаемому выходу, например, reshape2. Предположим, что вы сохранили вывод вышеуказанной операции в файле data.frame df2, тогда вы могли бы сделать:

require(reshape2)

df2 <- dcast(df2, Key  ~ Month, sum, value.var = "mean(Runoff)")