我遇到了一个问题,我希望为不同的实体计算一些月度度量,但我目前使用的代码似乎非常慢。我想知道你是否知道一个更好的解决方案。
我的数据集的简化版本如下。问题是,其中一个数据集包含大约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
。我们可以使用日期列的第一个到第七个字符的substr
ing创建年月变量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")