加速ddply

Nic*_*ton 2 r plyr data.table

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)),]
Run Code Online (Sandbox Code Playgroud)

我需要做什么,对每个独特的价值group,是制作部分,即其中新列产生xend,yend并且zend,它们是x,y,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))
Run Code Online (Sandbox Code Playgroud)

当它n很小时,这是微不足道的,然而,当n变大时,执行上述操作的时间变得非常重要,是否有更快的方法来执行此操作,也许使用data.table

Jaa*_*aap 6

您可以使用包中的shift功能执行此操作data.table.一个例子xend:

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

对于所有列:

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]
Run Code Online (Sandbox Code Playgroud)

这给你:

> 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
Run Code Online (Sandbox Code Playgroud)

@akrun在评论中提出的另一种方法:

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

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


在问题中,你说:

对于组中的最后一个值,结束将作为组中的第一个点.

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

对于组中的第一个值,将结束视为组中的最后一个点.


使用数据:

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)),]
Run Code Online (Sandbox Code Playgroud)