【问题标题】:Fast melted data.table operations快速融化的数据表操作
【发布时间】:2012-12-07 18:16:01
【问题描述】:

我正在寻找用于操作data.table 对象的模式,其结构类似于使用reshape2 包中的melt 创建的数据帧。我正在处理具有数百万行的数据表。性能至关重要。

问题的一般形式是是否有一种方法可以根据列中的值子集执行分组,并让分组操作的结果创建一个或多个新列。

问题的一种具体形式可能是如何使用data.table 来完成dcast 在以下方面所做的等效工作:

input <- data.table(
  id=c(1, 1, 1, 2, 2, 2, 3, 3, 3, 3), 
  variable=c('x', 'y', 'y', 'x', 'y', 'y', 'x', 'x', 'y', 'other'),
  value=c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
dcast(input, 
  id ~ variable, sum, 
  subset=.(variable %in% c('x', 'y')))

输出是

  id  x  y
1  1  1  5
2  2  4 11
3  3 15  9

【问题讨论】:

  • 我很困惑,抱歉 - dcast() 函数在没有 as.data.frame() 强制的情况下工作。您是否尝试在不使用 plyr 包的情况下完成此操作?
  • @AnthonyDamico 是的,我想在没有dcast 的情况下完成此操作,它位于reshape2 中。我已经删除了 as.data.frame()——感谢您指出这一点。

标签: r dataframe data.table aggregation reshape2


【解决方案1】:

未经测试的快速答案:似乎您正在寻找 by-without-by,也就是 grouping-by-i

setkey(input,variable)
input[c("x","y"),sum(value)]

这就像 SQL 中的快速 HAVING。 j 会针对i 的每一行进行评估。换句话说,上面是相同的结果,但比:

input[,sum(value),keyby=variable][c("x","y")]

在仅选择感兴趣的组之前,对所有组(浪费地)进行后一个子集和评估。前者(by-without-by)只直接进入组的子集。

组结果将一如既往地以长格式返回。但是之后在(相对较小的)聚合数据上重新调整到广泛的范围应该是相对即时的。反正就是这么想的。

如果input 有很多不感兴趣的列,第一个setkey(input,variable) 可能会咬人。如果是这样,可能值得对所需的列进行子集:

DT = setkey(input[ , c("variable","value")], variable)
DT[c("x","y"),sum(value)]

将来实现辅助键时会更容易:

set2key(input,variable)              # add a secondary key 
input[c("x","y"),sum(value),key=2]   # syntax speculative

也可以按id 分组:

setkey(input,variable)
input[c("x","y"),sum(value),by='variable,id']

根据您的数据,在密钥中包含 id 可能值得 setkey 的成本:

setkey(input,variable,id)
input[c("x","y"),sum(value),by='variable,id']

如果您将 by-without-by 与 by 结合起来,如上所述,那么 by-without-by 就像子集一样运行;即,j 仅在缺少 by 时为 i 的每一行运行(因此名称为 by-without-by)。因此,您需要再次将variable 包含在by 中,如上所示。

或者,以下内容应按 id 而非“x”和“y”的联合进行分组(但以上是您在问题中所要求的,iiuc):

input[c("x","y"),sum(value),by=id]

【讨论】:

  • 辅助键会很有帮助。或者,一种通过使用不同键的引用来构建“变体”的方法。我不断发现的模式是需要为具有相同 by 子句但不同 select 子句的不同操作多次更改键,然后从每个操作的数据表中手动组装最终结果。
【解决方案2】:
> setkey(input, "id")
> input[ , list(sum(value)), by=id]
   id V1
1:  1  6
2:  2 15
3:  3 34

> input[ variable %in% c("x", "y"), list(sum(value)), by=id]
   id V1
1:  1  6
2:  2 15
3:  3 24

最后一个:

> input[ variable %in% c("x", "y"), list(sum(value)), by=list(id, variable)]
   id variable V1
1:  1        x  1
2:  1        y  5
3:  2        x  4
4:  2        y 11
5:  3        x 15
6:  3        y  9

【讨论】:

  • 这对所有变量执行sum,而不仅仅是xy。就我而言,有数百个不同的变量,所以我需要一个子集子句。我不想通过创建一个完整的临时数据表来做到这一点。
  • 这更接近但仍然不完美 keycols
  • @Sim:我只是在向你展示选项。最后一个就是您所需要的,它不会创建任何中间表。
  • @Dwin 不确定“最后一个”是什么意思。你认为我可以在j 表达式中使用.BY 来过滤变量值吗?
  • @AnthonyDamico 是id 上的索引,因为无论如何都需要扫描表格?
【解决方案3】:

我不确定这是否是最好的方法,但你可以试试:

input[, list(x = sum(value[variable == "x"]), 
             y = sum(value[variable == "y"])), by = "id"]
#    id  x  y
# 1:  1  1  5
# 2:  2  4 11
# 3:  3 15  9

【讨论】:

  • 这不会为variable 的每个id 的值创建两个临时向量吗?
猜你喜欢
  • 1970-01-01
  • 2016-02-01
  • 2020-09-30
  • 1970-01-01
  • 1970-01-01
  • 2021-01-29
  • 2023-03-07
  • 1970-01-01
  • 2016-08-27
相关资源
最近更新 更多