使用data修改一行(在循环中)



我是数据新手。表,但我需要加速一个dplyr代码,到目前为止,我将处理时间除以20,所以不用说,我想掌握这个库。通过这个介绍,你可以理解处理时间是最重要的。

我必须修改行使用循环,因为一些列是相互连接的。这里不讨论循环的使用问题:我将使用循环,没有其他解决方法,其他原因没有说明。我已经知道我会在我的代码中使用什么来获得所需的结果,因为它显然是最快的,但我知道有一种。sd的方式来做到这一点,这将让我更深入地了解该库,因此我寻求您的建议。

所以,只是要清楚:把这篇文章看作是一个练习,它将帮助我理解使用。sd的微妙之处。我将展示一个简单的表格和一个简单的函数(均值),但这些远非实际数据(我使用自制的窗口均值)。但是如果任何人都能通过使用"mean"得到相同的结果和。sd,那么问题就解决了,我会学到一些我还不知道的东西。对不起,我在这里使用了权威的语气,我只是想说清楚:我想知道我的方法出了什么问题。

非常简化的表和目标如下:

temp <- data.table(a=c(0,10), b=c(15,25))
#initialize 1rst row
temp[1, `:=`(worksA=a, worksB=b)]
#in the (not shown) loop, starting row 2, worksA & worksB update a mean with fresh data:
temp[2, `:=`(worksA=mean(temp$a[1:2]), worksB=mean(temp$b[1:2]))]

这样你就得到了我想要的(但请注意,我将使用自制的"mean")。函数带有滚动窗口,因此实际使用cummean是不行的):

    a  b worksA worksB
1:  0 15      0     15
2: 10 25      5     20

我的第一个失败是:

temp[2, `:=`(tryA=mean(a[1:2]),tryB=mean(b[1:2]))]

创建NA。我猜我不能使用行选择来通过引用创建列,所以我使用"$"(工作解决方案);但我还是怀疑。sd能起作用,所以试了试。2:

temp[2, c("tryA", "tryB"):=lapply(.SD[1:2], mean), .SDcols=c("a", "b")]

相同。有趣的是,如果使用name .rm:

temp[2, `:=`(tryA=mean(a[1:2], na.rm=TRUE),tryB=mean(b[1:2], na.rm=TRUE))]

或:

temp[2, c("tryA", "tryB"):=lapply(.SD[1:2], mean, na.rm=TRUE), .SDcols=c("a", "b")]

you get try &tryB行2更新值a &B来自同一行,就好像它只计算了第2行值的平均值。关于这件事,我尽量不要在第一个参数上使用行选择(没有"2")第1括号后):

temp[, c("tryA", "tryB"):=lapply(.SD[1:2], mean), .SDcols=c("a", "b")]

当然给出:

    a  b worksA worksB tryA tryB
1:  0 15      0     15    5   20
2: 10 25      5     20    5   20

。我想要在所有行上打印的值。好一点,但不是我想要的。

Microbenchmark告诉我,我的工作解决方案比lapply函数快近20倍,所以我放弃了。但是,有人能解释为什么我的尝试(除了最后一个,这个是非常清楚的)被错误地编码,以及我如何能够使用用户定义的函数和。sd一次编辑1行吗?

Thanks in advance

您可以使用Reduceaccumulate=T选项来生成每列的累积列表:

library(data.table)
temp <- data.table(a=c(0,10), b=c(15,25))
temp[,lapply(.SD, function(x) Reduce(x,f=function(x,y) c(x,y),accumulate=T))]
#        a      b
#   <list> <list>
#1:      0     15
#2:   0,10  15,25

这样你就可以用sapply对它们应用任何汇总函数(在这个例子中是平均值):

temp[,paste0('works',colnames(temp)):=lapply(.SD, function(x) sapply(Reduce(x,f=function(x,y) c(x,y),accumulate=T),function(x) mean(unlist(x))))][]
#>        a     b worksa worksb
#>    <num> <num>  <num>  <num>
#> 1:     0    15      0     15
#> 2:    10    25      5     20

或具有递归平均值:

recursive.mean <- function(x) tail(stats::filter(x/2,1/2),1)
temp[,paste0('works',colnames(temp)):=lapply(.SD, function(x) sapply(Reduce(x,f=function(x,y) c(x,y),accumulate=T),function(x) recursive.mean(unlist(x))))][]
#       a     b worksa worksb
#   <num> <num>  <num>  <num>
#1:     0    15    0.0   3.75
#2:    10    25    2.5   6.25

好吧好吧,来个自我回答怎么样?

不要认为这是自恋的举动,因为我现在明白我的问题是怎么回事了…愚蠢的。这完全是数据的误用。表语言:我想编辑&同时选择

。SD是数据的一个子集,所以temp[2, lapply(.SD[1:2], mean), .SDcols=c("a", "b")]只能给出NA,因为我要求R从单行数据中返回几行子集(.SD[1:2])。表(temp[2,)。第一部分([2,)被R认为是一个选择,而不是一个版本。

temp[, lapply(.SD[1:2], mean), .SDcols=c("a", "b")]给出了我想要的结果,所以这是不可触及的部分,我必须将编辑到表中,该版本请求写在代码的上层,其中"2"应该是。

因此temp[2, c("worksA", "worksB"):=temp[, lapply(.SD[1:2], mean), .SDcols=c("a", "b")]]完全符合我的意思:第一部分(LHS)要求修改,第二部分(:=的RHS)是我想要插入的。. sd将返回一个输出,而不是在从中提取的表中编辑自己。太明显了,我的错。

在基准测试中,$ reference比.SD快2.8倍,正如预期的那样:

microbenchmark("$ref"={temp[2, `:=`(worksA=mean(temp$a[1:2]), worksB=mean(temp$b[1:2]))]}, ".SD"={temp[2, c("worksA", "worksB"):=temp[, lapply(.SD[1:2], mean), .SDcols=c("a", "b")]]}, times=10000L)
#Unit: microseconds
# expr      min        lq      mean   median        uq       max neval cld
# $ref  390.601  427.2185  593.3664  479.921  558.4865  38580.97 10000  a 
#  .SD 1102.362 1224.5490 1681.0160 1373.182 1576.1190 269964.59 10000   b

抱歉大惊小怪,问题解决了,我现在知道更多了(双关语!),让我们回去工作吧。非常感谢大家!

最新更新