如何用Dplyr或Data.Table替换ddply

时间:2015-11-23 06:39:10

标签: r data.table dplyr plyr

我目前正在使用ddply来应用我写入数据框的函数。该函数根据列中的值计算每一行,然后将许多其他函数应用于该行中的数据。结果是一个数据框,其结构与输入数据框相同,另外一列包含每行应用函数的结果。

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

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

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

我在下面创建了一个简化示例。我的问题是如何使用ddplydplyr复制以下data table函数调用给出我的上述观点。

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

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_by names }}。这对我来说似乎不对,因为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)] 

1 个答案:

答案 0 :(得分:0)

我找到了使用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找到解决方案,我仍然很想知道它是如何工作的。