R问题在data.table中使用cumsum来转发填充

问题描述

library(data.table)
set.seed(123)
dt = data.table( grp=round(runif(10)),val=c(runif(4),NA,runif(4),NA) )
dt

输出为:

    grp        val
 1:   0 0.95683335
 2:   1 0.45333416
 3:   0 0.67757064
 4:   1 0.57263340
 5:   1         NA
 6:   0 0.10292468
 7:   1 0.89982497
 8:   1 0.24608773
 9:   1 0.04205953
10:   0         NA

我想用之前的非 NA 值 val 填充 val

SO 问题“用最新的非 NA 值替换 NA”有 an amazing SO answer,我不完全理解。尽管如此,我还是尝试了:

dt[,val2 := val[1],.(grp,cumsum(!is.na(val))) ]
dt

输出为:

    grp        val       val2
 1:   0 0.95683335 0.95683335
 2:   1 0.45333416 0.45333416
 3:   0 0.67757064 0.67757064
 4:   1 0.57263340 0.57263340
 5:   1         NA 0.57263340
 6:   0 0.10292468 0.10292468
 7:   1 0.89982497 0.89982497
 8:   1 0.24608773 0.24608773
 9:   1 0.04205953 0.04205953
10:   0         NA         NA

这几乎有效(它正确填充了第 5 行)。为什么 dt 的第 10 行的 NA 值仍然是 val2 而不是 0.10292468(之前 grp == 0 的非 NA 值)?>

解决方法

nafill 中有一个 data.table

library(data.table)
dt[,val2 := nafill(val,type = 'locf')]

-输出

dt
#    grp        val       val2
# 1:   0 0.95683335 0.95683335
# 2:   1 0.45333416 0.45333416
# 3:   0 0.67757064 0.67757064
# 4:   1 0.57263340 0.57263340
# 5:   1         NA 0.57263340
# 6:   0 0.10292468 0.10292468
# 7:   1 0.89982497 0.89982497
# 8:   1 0.24608773 0.24608773
# 9:   1 0.04205953 0.04205953
#10:   0         NA 0.04205953

在 OP 的输出中它是 NA 的原因是基于分组

dt[,cumsum(!is.na(val))]
#[1] 1 2 3 4 4 5 6 7 8 8

最后两个元素被分组为 8,其中 'grp' 最后一个元素是 0,所以只有一个元素匹配,如果我们使用 val[1] which NA,我们得到了NA

如果我们用 .GRP 参数检查 by 会更清楚

dt[,.GRP,by = .(grp1 = cumsum(!is.na(val)),grp)]
#      grp1 grp GRP
#1:    1   0   1
#2:    2   1   2
#3:    3   0   3
#4:    4   1   4
#5:    5   0   5
#6:    6   1   6
#7:    7   1   7
#8:    8   1   8
#9:    8   0   9  # -> only a single element for group 9
 

因此,val[1] 返回 NA 元素

但是,如果我们对 'grp' 执行 order 然后使用 cumsum 创建新组,相邻元素是不同的,因此每组有多个元素

,

奇怪。我只是注意到我提到的 SO 答案首先由 dt 订购了 grp。我试过了,它奏效了。

dt = data.table( grp=round(runif(10)),val=c(runif(4),NA,runif(4),NA) )
dt = dt[ order(grp),] ## seems to be critical step
dt[,val2 := val[1],.(grp,cumsum(!is.na(val))) ]
dt

输出为:

    grp        val       val2
 1:   0 0.95683335 0.95683335
 2:   0 0.67757064 0.67757064
 3:   0 0.10292468 0.10292468
 4:   0         NA 0.10292468
 5:   1 0.45333416 0.45333416
 6:   1 0.57263340 0.57263340
 7:   1         NA 0.57263340
 8:   1 0.89982497 0.89982497
 9:   1 0.24608773 0.24608773
10:   1 0.04205953 0.04205953