【问题标题】:R data.frame flow data pre-processing for aggregated time statisticsR data.frame 流数据预处理聚合时间统计
【发布时间】:2014-05-20 17:51:44
【问题描述】:

处理流data.frame的最有效方法是什么

> 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,当实际数据集可以超过 100 000 000 行甚至数十 GB 时?

列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/duplicatesecs &gt; 0 行,同时增加扩展行的first(时间戳)值并即时调整amount、size 和tot 指标:

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 的答案是正确的并且帮助很大,因此将其标记为已接受的答案。

【问题讨论】:

  • 你需要用简单的语言解释你是如何从问题中显示的输入到达输出的。
  • 您当前的实现也有一些语法和其他错误。事实上,它不会产生任何输出。我什至看不出它会如何产生您建议的输出,因为您从未将任何内容分配给名为“时间”的列。
  • 是的。添加了简要说明和“时间”分配。
  • 还是不行。请尝试自己测试代码。
  • 对原始的 for 循环进行了编辑以呈现功能。添加了所有美丽的整个代码。如果我们使用 row.names(df2) = "1" "2" "3" "4" 作为时间,您可以完全丢弃“时间”。

标签: r awk dataframe aggregate


【解决方案1】:

以下是使用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

【讨论】:

  • 感谢您的回复,这是对 data.table 基础知识的很好介绍!不幸的是,在我的情况下,第一行 dtr &lt;- dt[rep(seq.int(1, nrow(dt)), secs+1)] 的资源过于密集,整个数据集大约有 100 000 000 行,所以我的内存不足:Error: cannot allocate vector of size 6.8 Gb。稍后我将尝试将此集合拆分为更小的块。
  • 听起来你需要一些比先生成全尺寸数据集更复杂的东西......
  • 是的。我通过使用具有更多 RAM 的服务器解决了这种情况。您可以轻松编写一个循环以将扩展值保存到例如单独的文件,然后从那里汇总统计信息。
  • @ylijumala 很高兴您可以使用旧的蛮力“更多内存”方法来解决它!
猜你喜欢
  • 2017-04-10
  • 1970-01-01
  • 2014-04-10
  • 1970-01-01
  • 2023-02-21
  • 1970-01-01
  • 1970-01-01
  • 2022-10-23
  • 1970-01-01
相关资源
最近更新 更多