R-使用数据表循环子集并获得输出的最快方法(计算月度度量)



我遇到了一个问题,我希望为不同的实体计算一些月度度量,但我目前使用的代码似乎非常慢。我想知道你是否知道一个更好的解决方案。

我的数据集的简化版本如下。问题是,其中一个数据集包含大约600万个个人每日观测值,而我目前的方法似乎非常缓慢。

date     event  id return
2000-07-06     2  1   0.1
2000-07-07     1  1   0.2
2000-07-09     0  1   0.6
2000-07-10     0  1   0.4
2000-07-15     2  1   0.7
2000-07-16     1  1   0.3
2000-07-20     0  1   0.1
2000-07-21     1  1   0.2
2000-07-06     1  2   0.3
2000-07-07     2  2   0.4
2000-07-15     0  2   0.6
2000-07-16     0  2   0.8
2000-07-17     2  2   0.9
2000-07-18     1  2   0.1

为了计算这些度量,我正在运行如下代码:


for (j in 1:length(list.of.ids)) {
for (i in 1:(number.of.months) {
temp <- subset(data, data$date < FirstDayMonth[i+1] & data$date >= FirstDayMonth[i] & data$id == list.of.ids[j])
total[i,j+1] <- sum(temp$return, na.rm = TRUE)
}
}

注意:total[,]是一个矩阵,其中有一个时间列,每个id有一列,数据集中每个月的行数相等。我希望有一个矩阵,存储我所有的id和月份的月度指标。这个循环允许我按id计算每月的回报总额,然后将其存储在该矩阵中。

同样,上面的代码允许我在一个月的时间段(通过将我的观察限制在连续两个月的第一天之间(和id上进行子集。问题是,对于我的大型数据集来说,这是非常缓慢的。

代码是否有任何改进,可以让我更快地获得所需的输出?

应该产生加速的改进:

for (j in 1:length(list.of.ids)) {
id1 <- data$id == list.of.ids[j]
# outside 2nd loop so no redundant operations wont be made
for (i in 1:(number.of.months)) {
id2 <- data$date < FirstDayMonth[i+1] & data$date >= FirstDayMonth[i]
total[i, j+1] <- sum(data$return[id1 & id2], na.rm = TRUE)
}
}

(可能有很大的改进,因为我们不需要每次创建新的data.frame对象,我们只需要获得需要计算总和的元素的索引(

但我会使用data.table:

require(data.table)
data <- as.data.table(data)
data[, ym := format(date, '%Y-%m')]
res <- data[, sum(return, na.rm = T), keyby = .(ym, id)]
res
#         ym id  V1
# 1: 2000-07  1 2.6
# 2: 2000-07  2 3.1

如果需要,最终结果可以转换为矩阵:

m <- matrix(res$V1, nrow = length(unique(res$ym)))
m
#      [,1] [,2]
# [1,]  2.6  3.1

更新:

更快的版本:

setDT(data) # converts data to data.table
x <- data[, .(date = unique(date))][, .(date, ym = format(date, '%Y-%m'))]
data[x, ym := i.ym, on = 'date']
res <- data[, sum(return, na.rm = T), keyby = .(ym, id)]
res

(format(date, '%Y-%m')很慢,所以我们只取唯一的日期,计算它们的ym,然后将其合并到数据中。如果你有很多重复的日期,这应该会更快。(

更新2:

转换为矩阵可以通过获得

resdt <- dcast(res, ym ~ id, value.var = 'V1') # change data structure
resdt[1:2, 1:3]
#         ym        1        2
# 1: 2000-01 6.824182 2.535805
# 2: 2000-02 3.825659 6.769578
resdt[, ym := NULL] # delets ym
setcolorder(resdt, neworder = list.of.ids) # reorder columns
m <- as.matrix(resdt)
m[1:2, 1:2]
#             1        2         3
# [1,] 6.824182 2.535805 -1.193692
# [2,] 3.825659 6.769578 -1.117223

这应该会快得多:

for(i in 1:length(number.of.months)) {
inds <- dat$date < FirstDayMonth[i+1] & dat$date >= FirstDayMonth[i]
total[i,] <- rowsum(dat$result[inds], dat$id[inds], na.rm=TRUE)
}

使用aggregate。我们可以使用日期列的第一个到第七个字符的substring创建年月变量ym

m <- with(dat, aggregate(list(return=return),
by=list(ym=substr(date, 1, 7), id=id), sum))
m
#        ym id return
# 1 2000-07  1    2.6
# 2 2000-07  2    3.1

tapply

m <- with(dat, tapply(return, list(ym=substr(date, 1, 7), id=id), sum))
m
#          id
# ym          1   2
#   2000-07 2.6 3.1

数据

dat <- structure(list(date = c("2000-07-06", "2000-07-07", "2000-07-09", 
"2000-07-10", "2000-07-15", "2000-07-16", "2000-07-20", "2000-07-21", 
"2000-07-06", "2000-07-07", "2000-07-15", "2000-07-16", "2000-07-17", 
"2000-07-18"), event = c(2L, 1L, 0L, 0L, 2L, 1L, 0L, 1L, 1L, 
2L, 0L, 0L, 2L, 1L), id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 
2L, 2L, 2L, 2L, 2L), return = c(0.1, 0.2, 0.6, 0.4, 0.7, 0.3, 
0.1, 0.2, 0.3, 0.4, 0.6, 0.8, 0.9, 0.1)), row.names = c(NA, -14L
), class = "data.frame")

最新更新