使用R中的data.table按组计算平均每月总数

14

我有一个data.table,每天都有一行数据,覆盖了30年的时间段,包含了不同的变量列。使用data.table的原因是我正在使用的.csv文件非常大(大约120万行),因为有一个名为“key”的列特征化了若干组的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 1月份的总体平均值 = (2 + 1) + (0.1 + 3) + (2 + 5) / 3
当我对一个30年的数据集(即仅一个关键字)进行此分析时,我已成功地使用以下代码来实现此目标:
runoff_tot_average <- rowsum(DF$Runoff, format(DF$Date, '%m')) / 30

DF是一个包含30年数据的数据框。

那么,我能否请您提出如何修改上面的代码以处理包含许多“键”的更大数据集的建议,或提供完全新的解决方案!

编辑

下面的代码生成了上述数据示例:

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)

runoff_tot_average_A <- rowsum(DF$Runoff[DF$Key=="A"], format(DF$Date, '%m')) / sum(DF$Key=="A") 这行代码是否能够得到你想要的结果?如果不行,能否提供一个简短可重现的例子? - Vincent
嗨,文森特,感谢您提供解决方案。我已经添加了一个可重现的示例数据集。您提供的代码将为每个键生成一个输出。我需要的是一个输出,其中包含一个键列和每个月份的总平均值列。 - Catchment_Jack
3个回答

14

我只能想到分两步来完成它。也许不是最好的方法,但是我们还是试一下吧。

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 在这里展示的那样。


感谢David的初步回答和Arun的编辑。这个方法很有效,结合beginneR的reshape答案,我得到了我需要的结果。我会在尝试另一种方法后标记一个答案。J - Catchment_Jack
1
我已经选中了这个答案,因为它使用了数据表包,而我大部分的分析都在使用该包。 - Catchment_Jack

6
如果您不需要复杂的功能,只是想要平均值,那么以下内容应该就够了:
DT[, sum(Runoff) / length(unique(year(Date))), list(Key, month(Date))]
#   Key month       V1
#1:   A     1 4.366667
#2:   B     1 3.266667

4

既然您在问题中表示愿意尝试全新的解决方案,您可以尝试使用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))

在@Henrik的评论后进行编辑 #1: 同样的操作可以通过以下方式完成:

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将输出重塑以匹配所需的输出。假设您将上述操作的输出存储在数据框df2中,则可以执行以下操作:

require(reshape2)

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

嗨,感谢您回答我的问题。我正在使用的R版本无法支持dplyr,而且我暂时无法下载新版本的R。我会告诉您我的进展情况。J - Catchment_Jack
2
@beginneR,您可以稍微简化dplyr代码。 ungroup() %.% group_by(Key, Month) 可以替换为 group_by(Key, Month, add = FALSE)。或者,如果您在第一个 group_by 中更改变量的顺序为 group_by(Key, Month, Year.Month),则根本不需要第二个 group_by。请参见此处:“当您按多个变量分组时,每个摘要都会剥离一级分组。这使得逐步滚动数据集变得容易。” - Henrik
1
@Henrik 感谢您的评论 - 很高兴知道这一点,肯定是一个改进。我会相应地更新我的答案。 - talat
@Henrik 顺便说一下,add = FALSE 将成为 dplyr 0.2 的默认设置。 - hadley
@hadley,感谢您的信息!对我来说,这似乎是更直观的默认设置。 - Henrik
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接