处理流数据的最有效方法是什么?如
> df <- data.frame(amount=c(4,3,1,1,4,5,9,13,1,1), size=c(164,124,131,315,1128,331,1135,13589,164,68), tot=1, first=c(1,1,3,3,2,2,2,2,4,4), secs=c(2,2,0,0,1,1,1,1,0,0))
> df
amount size tot first secs
1 4 164 1 1 2
2 3 124 1 1 2
3 1 131 1 3 0
4 1 315 1 3 0
5 4 1128 1 2 1
6 5 331 1 2 1
7 9 1135 1 2 1
8 13 13589 1 2 1
9 1 164 1 4 0
10 1 68 1 4 0
到每次汇总总计
> df2
time tot amount size
1 1 2 3.5 144
2 2 6 34.5 16327
3 3 8 36.5 16773
4 4 2 2.0 232
..使用R,当实际数据集可以超过10万行甚至数十千兆字节?
列first
表示持续时间为secs
的流的开头,其中包含指标amount
,size
和tot
。在聚合总计中,size
和amount
以双精度均匀划分为时间范围,而tot
则作为整数求和到每个时隙。持续时间secs
表示除了值first
之外流量持续的时间段数:如果secs
为1且first
为5,则流量持续时间段5和6.我当前的实现使用了丑陋且死慢的for循环,这不是一个选项:
df2 = data.frame()
for (i in 1:nrow(df)) {
items <- df[i, 'secs']
idd <- df[i, 'first']
for (ss in 0:items) { # run once for secs=0
if (items == 0) { items <- 1 }
df2[idd+ss, 'time'] <- idd+ss
if (is.null(df2[idd+ss, 'tot']) || is.na(df2[idd+ss, 'tot'])) {
df2[idd+ss, 'tot'] <- df[i, 'tot']
} else {
df2[idd+ss, 'tot'] <- df2[idd+ss, 'tot'] + df[i, 'tot']
}
if (is.null(df2[idd+ss, 'amount']) || is.na(df2[idd+ss, 'amount'])) {
df2[idd+ss, 'amount'] <- df[i, 'amount']/items
} else {
df2[idd+ss, 'amount'] <- df2[idd+ss, 'amount'] + df[i, 'amount']/items
}
if (is.null(df2[idd+ss, 'size']) || is.na(df2[idd+ss, 'size'])) {
df2[idd+ss, 'size'] <- df[i, 'size']/items
} else {
df2[idd+ss, 'size'] <- df2[idd+ss, 'size'] + df[i, 'size']/items
}
}
}
您可以使用循环来优化这一点并获得良好的性能,但我敢打赌,存在更好的算法。也许您可以expand/duplicate secs > 0
行first
,同时增加展开行的amount
(时间戳)值并调整size
,tot
和{ {1}}动态指标:
now original data..
amount size tot first secs
1 4 164 1 1 0
2 4 164 1 1 1
3 3 124 1 1 2
magically becomes
amount size tot first
1 4 164 1 1
2 2 82 1 1
3 2 82 1 2
4 1 41.33 1 1
5 1 41.33 1 2
6 1 41.33 1 3
在预处理步骤之后,使用plyr ddply进行聚合将是微不足道的,当然在高效的并行模式下。
所有示例ddply,apply等函数示例我能够找到按行或按列操作,这使得很难修改其他行。希望我不必依赖awk-magic。
更新:当扩展“按原样”完成时,上述算法很容易耗尽内存。因此,某种“动态”计算是首选,我们不会将所有内容映射到内存。然而,Mattrition的答案是正确的并且有很多帮助,因此将其标记为已接受的答案。
答案 0 :(得分:0)
以下是使用data.table
的实现。我选择data.table
作为其聚合能力,但它也是一个非常有效的课程。
library(data.table)
dt <- as.data.table(df)
# Using the "expand" solution linked in the Q.
# +1 to secs to allow room for 0-values
dtr <- dt[rep(seq.int(1, nrow(dt)), secs+1)]
# Create a new seci column that enumerates sec for each row of dt
dtr[,seci := dt[,seq(0,secs),by=1:nrow(dt)][,V1]]
# All secs that equal 0 are changed to 1 for later division
dtr[secs==0, secs := 1]
# Create time (first+seci) and adjusted amount and size columns
dtr[,c("time", "amount2", "size2") := list(first+seci, amount/secs, size/secs)]
# Aggregate selected columns (tot, amount2, and size2) by time
dtr.a <- dtr[,list(tot=sum(tot), amount=sum(amount2), size=sum(size2)), by=time]
dtr.a
time tot amount size
1: 1 2 3.5 144
2: 2 6 34.5 16327
3: 3 8 36.5 16773
4: 4 2 2.0 232