【问题标题】:How to replace ddply with Dplyr or Data.Table如何用 Dplyr 或 Data.Table 替换 ddply
【发布时间】:2015-11-23 06:39:09
【问题描述】:

我目前正在使用ddply 将我编写的函数应用到数据框。该函数根据列中的值评估每一行,然后将许多其他函数应用于该行中的数据。结果是一个与输入数据帧具有相同结构的数据帧和一个附加列,其中包含每行应用函数的结果。

我的问题是数据集相当大,因此使用 ddply 需要很长时间 - 太长了!

当时间很重要时,我已经阅读了许多其他关于替换 ddply 的 SO 问题和博客文章。大多数帖子要么建议使用 data.table ,要么建议使用 dplyr 包中的一些函数组合 do。虽然速度是最重要的,但我从未使用过 data.table,所以易用性/直观性也很重要。

同样,虽然this question 在解释如何结合您自己的函数使用不同的dplyr 函数时非常有用,但我还需要将其他对象传递给我的函数,我不确定如何使用答案在问题中。

我在下面创建了一个简化的示例。然后我的问题是如何使用dplyrdata table 复制下面的ddply 函数调用给我以上几点。

首先,我设置了一些数据来模拟实际数据的结构

noObs <- 1e5
dataIn <- data.frame(One = rep(c("J", "K"), noObs/2), Two = rep(c("ID", "BR", "LB", "OZ"), noObs/4),
                     Three = runif(noObs))

secondaryData <- data.frame(Two = c("ID", "BR", "LB", "OZ"), Size = c(300, 500, 250, 400))

下面是我的函数的一个简化示例(实际上,函数参数大于2,它本身调用了其他函数)

MyFunction <- function(dataIn, secondaryData){

  groupNames <- c("BR", "LB")

  if(dataIn$One == "J"){
    if(!(dataIn$Two%in%groupNames)){
      if(dataIn$Two == "ID"){
        idx <- match(dataIn$Two, secondaryData$Two)
        value <- secondaryData[idx, "Size"]
        dataIn$newCalc <- dataIn$Three*value
      }else{
        dataIn$newCalc <- dataIn$Three*1000
      }
    }else{
      idx <- match(dataIn$Two, secondaryData$Two)
      value <- secondaryData[idx, "Size"]
      dataIn$newCalc <- dataIn$Three*value+1
    }
  }else{
    idx <- match(dataIn$Two, secondaryData$Two)
    value <- secondaryData[idx, "Size"]
    dataIn$newCalc <- dataIn$Three*value
  }

  return(dataIn)

}

ddply 调用看起来像

dataOut <- ddply(dataIn, names(dataIn), MyFunction, secondaryData)

最后,一些我尝试过的例子(我还没有尝试过data.table

dataIn %>% group_by(names(dataIn)) %>% do(MyFunction(dataIn, secondaryData))
dataIn %>% group_by(names(dataIn)) %>% MyFunction(dataIn, secondaryData)
dataIn %>% group_by(.dots = names(dataIn)) %>% MyFunction(secondaryData)

编辑

我已经能够找到一种使用dplyr 的方法,但它比ddply 还要慢,而且我不知道如何将group_bynames 一起使用。这对我来说似乎不对,因为dplyr 意味着更快。

另外,我一直在尝试使用data.table,但一直无法让它工作。同样,我正在寻找比ddply运行更快的东西

#Plyr
start <- proc.time()
dataOut <- ddply(dataIn, names(dataIn), MyFunction, secondaryData)
plyrTime <- proc.time() - start

#Dplyr
#Works
start <- proc.time()
res <- dataIn %>% group_by(One, Two, Three) %>% do(MyFunction(.,secondaryData))
dplyrTime <- proc.time() - start
#Doesn't work
res <- dataIn %>% group_by(.,names(dataIn)) %>% do(MyFunction(.,secondaryData))

#Data.table
dataInDT <- data.table(dataIn)
dataInDT[,.(MyFunction(.,secondaryData)), by=.(One, Two, Three)] 

【问题讨论】:

  • 也许是library(dplyr); dataIn %&gt;% group_by_(.dots = names(dataIn)) %&gt;% myFunction(projSettings, secondaryData)。请提供一个可重现的例子。
  • SO 不是代码编写服务。向我们展示您已经尝试过的内容(包括reproducible example)。
  • @lukeA 我添加了一个可重现的示例。希望这会有所帮助
  • 谢谢。 dataIn %&gt;% group_by_(.dots = names(dataIn)) %&gt;% MyFunction(secondaryData) 的结果有什么问题?我看不出你试过了。
  • @lukeA 谢谢你的评论。我确实尝试过,但它会产生警告。经过进一步调查,您可以看到建议的方法没有正确应用算法,换句话说,它只需要第一个元素

标签: r data.table dplyr plyr


【解决方案1】:

我找到了使用data.table 的解决方案。值得注意的是,它对每一行执行正确的计算,但速度非常快。函数的格式不同,以适应data.table的不同风格。我确信使用data.table 有更好或更正确的方法来解决它,但下面的解决方案效果很好。

dataInDT <- data.table(dataIn)

groupNames <- c("BR", "LB")
start <- proc.time()
dataInDT[, NewCalc := {
  if(One == "J"){
    if(!(Two%in%groupNames)){
      if(Two == "ID"){
        Three*secondaryData[match(Two, secondaryData$Two), "Size"]
      }else{
        Three*1000
      }
    }else{
      Three*secondaryData[match(Two, secondaryData$Two), "Size"]+1
    }
  }else{
    Three*secondaryData[match(Two, secondaryData$Two), "Size"]
  }}, by=.(One, Two, Three)]
datTableTime <- proc.time() - start

与旧方案对比一下,你可以看到速度有了很大的提升

start <- proc.time()
dataOut <- ddply(dataIn, names(dataIn), MyFunction, secondaryData)
plyrTime <- proc.time() - start

当然,在实践中我使用的data.table 函数更加复杂,尤其是by 部分要长得多。

我无法使用 dplyr 找到解决方案,但我仍然很想知道它是如何工作的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-20
    • 2019-11-23
    • 2019-04-01
    相关资源
    最近更新 更多