【问题标题】:Efficient multiplication of columns in a data frame数据框中列的有效乘法
【发布时间】:2012-09-03 16:02:07
【问题描述】:

我有一个大型数据框,其中我将两列相乘以获得另一列。起初我正在运行一个 for 循环,如下所示:

for(i in 1:nrow(df)){
    df$new_column[i] <- df$column1[i] * df$column2[i]
}

但这需要 9 天。

另一种选择是plyr,实际上我可能错误地使用了变量:

new_df <- ddply(df, .(column1,column2), transform, new_column = column1 * column2)

但这需要很长时间

【问题讨论】:

  • df$new_column &lt;- df$column1 * df$column2 有什么问题?你的数据框有多大?
  • 400000 行即使使用 plyr 也应该很快......但如果你只是想将两列相乘,@BlueMagister 有最好的解决方案。即便如此,for 循环版本也不应该花费 9 天...
  • R 中的大多数操作都是向量化的,因此您可以将向量乘以向量,它会将相同索引的条目相乘。 for 循环的问题是R 为循环的每次迭代创建一个新的数据框。我建议的解决方案只创建一个新数据帧,而不是 400K 新数据帧。
  • “time-efficient”和“plyr”一般不在同一个句子中使用。如果速度是您的目标,您应该查看“data.table”包。 (......虽然现在 400K x 7 是一个很小的数据集,但您在下面的答案中提供的普通功能就足够了。)
  • plyr 在这里这么慢的原因是您不必要地对 column1 和 column2 进行分组。这会为这些列的每个唯一组合创建组。 ddply 不是必需的。 data.table 将有效地创建新列。

标签: r dataframe plyr


【解决方案1】:

您可以简单地创建一个函数来处理所有类型的乘法,如下所示:

GetMultiplication <- function(x,y) {
x *y
}

# for example:
xCol<-c(1,2,3,4,5)
yCol<-c(10,20,30,40,50)
const = 0.055

#Case 1: Column 1 * Column 2
ZCol_1 <- GetMultiplication (xCol,yCol)
print(ZCol_1)
#> [1]  10  40  90 160 250

#Case 2: Column 1 * (Column 1 * 10 + 1000)
ZCol_2 <- GetMultiplication (xCol,xCol*10 + 1000)
print(ZCol_2)
#> [1] 1010 2040 3090 4160 5250

#Case 3: Column 1 * a constant value
ZCol_3 <- GetMultiplication (xCol,const)
print(ZCol_3)
#> [1] 0.055 0.110 0.165 0.220 0.275

【讨论】:

    【解决方案2】:

    data.table 解决方案将避免大量内部复制,同时具有不使用$ 散布代码的优点。

     library(data.table)
     DT <- data.table(df)
     DT[ , new := column1 * column2]
    

    【讨论】:

      【解决方案3】:

      Sacha 的答案的一个次要版本是使用 transform()within()

      df <- transform(df, new = column1 * column2)
      

      df <- within(df, new <- column1 * column2)
      

      (我讨厌用$ 溅出我的用户代码。)

      【讨论】:

      • 为什么效率会降低? $&lt;- 优化不复制数据框了吗?
      • 最近对数据帧上的某些操作进行了一些优化,但不记得是否是其中之一。我认为这会稍微降低效率,因为transform()within() 在计算表达式的那一行之上包含了很多额外的 R 代码行。
      • @GaborCsardi 不幸的是,$&lt;- 没有优化到不能在 R 中复制;它会复制整个 dftransformwithin 也是如此;他们也复制了整个 df。这就是 data.table 引入 := 以允许通过引用分配的原因,如 mnel 的答案中演示的那样。
      • @GavinSimpson := 以外的所有解决方案都复制整个 df,因此不要扩展。相比之下,transformwithin 中额外的代码行数很少。除非调用是循环的,例如在transform-by-group 中,但它甚至更受所有副本的支配。如果transform没有复制那么行数将进入其中。
      • @MatthewDowle 这不是一个大问题,只要正确使用基本 R 解决方案就可以为 OP 节省大量时间。我同意将这些东西转移到 data.table 会带来丰厚的回报,但它需要掌握一组额外的函数和语法。我不需要在 data.table 上出售;我已经从那本赞美诗书上签名了。我只需要花一些时间将它更多地集成到我的工作流程中,这样语法就会保持不变。
      【解决方案4】:

      正如蓝魔导师在 cmets 中所说,

      df$new_column <- df$column1 * df$column2
      

      应该可以正常工作。当然,如果我们没有数据示例,我们永远无法确定。

      【讨论】:

      • 更漂亮,但基本相同:df$new_column &lt;- with( df , column1 * column2)
      • @DWin 使用 with() 似乎有点奇怪,但随后通过 $&lt;- 进行分配。 within()transform()? 有什么问题
      • 如果您只分配一个(新的或其他)列,我认为您需要使用with 而不是within,因为within 将返回整个data.frame。反正这是我的理解。 (...并且测试确认 dat$new
      • Yes 应该适用于 20MB 数据集(400k x 7 行)。但它会复制整个 20MB,所以不要重复太多。例如,每次使用$&lt;- 添加 10 列需要 400MB (400e3*sum(7:17)*8/1024^2)。通过 5GB (400e3*sum(7:57)*8/1024^2) 添加 50 次流失以获得 400k x 57 的结果(仅 173MB)。只是要注意。
      • 因此,如果可以,请批量添加列,而不是一一添加。因为即使在 20MB 大小的情况下也能咬人。以防万一那将是您的下一步。或者,使用:=,它不会复制整个 20MB。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多