R data.frame流数据预处理用于聚合时间统计

时间:2014-05-15 11:38:12

标签: r awk dataframe aggregate

处理流数据的最有效方法是什么?如

> 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的流的开头,其中包含指标amountsizetot。在聚合总计中,sizeamount以双精度均匀划分为时间范围,而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 > 0first,同时增加展开行的amount(时间戳)值并调整sizetot和{ {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的答案是正确的并且有很多帮助,因此将其标记为已接受的答案。

1 个答案:

答案 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