【问题标题】:Speeding up ddply加速ddply
【发布时间】:2015-08-08 08:56:59
【问题描述】:

我有一个这样的data.frame

n  = 50
df = data.frame(group=sample(1:as.integer(n/2),n,replace=T),
                x = runif(n),
                y = runif(n),
                z = runif(n))
df = df[with(df,order(group)),]

对于group 的每个唯一值,我需要做的是生成段,即生成新列的位置,xendyendzend,它们是xy, z 该组中前一个点的值。对于组中的最后一个值,将端点作为组中的第一个点。

我可以通过以下方式做到这一点:

res = ddply(df,"group",function(d){ 
  ixc  = c("x","y","z")
  dfE  = d[,ixc]
  dfE  = rbind(dfE[nrow(dfE),],dfE[1:(nrow(dfE)-1),])
  colnames(dfE) = paste0(ixc,"end")
  cbind(d,dfE)
})
print(head(res))

n 很小时这很简单,但是当n 变大时,执行上述操作的时间变得很重要,有没有更快的方法来做到这一点,也许使用data.table

【问题讨论】:

    标签: r data.table plyr


    【解决方案1】:

    您可以使用 data.table 包中的 shift 函数来完成此操作。 xend 的示例:

    library(data.table) 
    setDT(df)[, xend := shift(x, 1L, fill = x[.N], type = "lag"), by = group]
    

    对于所有列:

    setDT(df)[, c("xend","yend","zend") := .(shift(x, 1L, fill = x[.N], type = "lag"),
                                             shift(y, 1L, fill = y[.N], type = "lag"),
                                             shift(z, 1L, fill = z[.N], type = "lag")),
              by = group]
    

    这给了你:

    > head(df)
       group          x         y          z       xend      yend       zend
    1:     1 0.56725304 0.7539735 0.20542455 0.71538606 0.3864990 0.01586889
    2:     1 0.64251519 0.1255183 0.93371528 0.56725304 0.7539735 0.20542455
    3:     1 0.14182485 0.7351444 0.89199415 0.64251519 0.1255183 0.93371528
    4:     1 0.06613097 0.7625182 0.92669617 0.14182485 0.7351444 0.89199415
    5:     1 0.71538606 0.3864990 0.01586889 0.06613097 0.7625182 0.92669617
    6:     4 0.27188921 0.5496977 0.09282217 0.27188921 0.5496977 0.09282217
    

    @akrun 在 cmets 中建议的另一种方法:

    setDT(df)[, c("xend","yend","zend") := lapply(.SD, function(x) shift(x, fill = x[.N]))
              , by = group]
    

    虽然这种方法需要更少的输入,并且在包含变量方面提供了更大的灵活性,但它也相当慢。


    在问题中,您说:

    对于组中的最后一个值,以端点作为第一个点 在群里。

    但是,根据您描述的所需行为,对于组中的最后一个值,使用组中的前一个值。我猜你的意思是:

    对于组中的第一个值,端点作为最后一个点 在群里。


    使用过的数据:

    set.seed(1)
    n  = 1e5
    df = data.frame(group=sample(1:as.integer(n/2),n,replace=T),
                    x = runif(n),
                    y = runif(n),
                    z = runif(n))
    df = df[with(df,order(group)),]
    

    【讨论】:

    • 你用的是哪个版本的data.table,我的是1.9.2版本,不包含任何'shift'功能。
    • 错误[.data.table(setDT(df), , :=(xend, shift(x, 1L, fill = x[.N], : 找不到函数“shift”跨度>
    • @NicholasHamilton 另请参阅我对最后/第一个值的评论。这是你的意思吗?
    • 我认为对于多个列 setDT(df)[, (nm1) := lapply(.SD, function(x) shift(x, fill=x[.N])), group] ,其中 nm1 <- paste0(names(df)[-1], 'end')
    • @akrun 谢谢!将它添加到我的答案中:-)(不幸的是它要慢得多:大约 2 倍)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多