在R向量中"向下"复制单元格值的惯用法

fma*_*ark 1 performance loops idioms r vectorization

可能重复:
使用先前的非NA值在向量中填充NA?

是否有一种惯用的方法可以在R向量中"向下"复制单元格值?通过"复制",我的意思是用最接近的先前非NA值替换NA.

虽然我可以使用for循环非常简单地执行此操作,但它运行速度非常慢.关于如何矢量化的任何建议将不胜感激.

# Test code
# Set up test data
len <- 1000000
data <- rep(c(1, rep(NA, 9)), len %/% 10) * rep(1:(len %/% 10), each=10)
head(data, n=25)
tail(data, n=25)

# Time naive method
system.time({
  data.clean <- data;
  for (i in 2:length(data.clean)){
    if(is.na(data.clean[i])) data.clean[i] <- data.clean[i-1]
  }
})

# Print results
head(data.clean, n=25)
tail(data.clean, n=25)
Run Code Online (Sandbox Code Playgroud)

试运行结果:

> # Set up test data
> len <- 1000000
> data <- rep(c(1, rep(NA, 9)), len %/% 10) * rep(1:(len %/% 10), each=10)
> head(data, n=25)
 [1]  1 NA NA NA NA NA NA NA NA NA  2 NA NA NA NA NA NA NA NA NA  3 NA NA NA NA
> tail(data, n=25)
 [1]     NA     NA     NA     NA     NA  99999     NA     NA     NA     NA
[11]     NA     NA     NA     NA     NA 100000     NA     NA     NA     NA
[21]     NA     NA     NA     NA     NA
> 
> # Time naive method
> system.time({
+   data.clean <- data;
+   for (i in 2:length(data.clean)){
+     if(is.na(data.clean[i])) data.clean[i] <- data.clean[i-1]
+   }
+ })
   user  system elapsed 
   3.09    0.00    3.09 
> 
> # Print results
> head(data.clean, n=25)
 [1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3
> tail(data.clean, n=25)
 [1]  99998  99998  99998  99998  99998  99999  99999  99999  99999  99999
[11]  99999  99999  99999  99999  99999 100000 100000 100000 100000 100000
[21] 100000 100000 100000 100000 100000
> 
Run Code Online (Sandbox Code Playgroud)

Mar*_*gan 5

我不知道惯用语,但在这里我们识别非NA值(idx),以及最后一个非NA值的索引(cumsum(idx))

f1 <- function(x) {
    idx <- !is.na(x)
    x[idx][cumsum(idx)]
}
Run Code Online (Sandbox Code Playgroud)

这似乎比na.locf示例数据快约6倍.它na.locf默认情况下会降低领先的NA ,所以

f2 <- function(x, na.rm=TRUE) {
    idx <- !is.na(x)
    cidx <- cumsum(idx)
    if (!na.rm)
        cidx[cidx==0] <- NA_integer_
    x[idx][cidx]
}
Run Code Online (Sandbox Code Playgroud)

这似乎增加了大约30%的时间na.rm=FALSE.大概na.locf有其他优点,捕获更多的角落情况,并允许填补而不是下降(cumsum无论如何这是一个有趣的运动在世界上).还有一点很清楚,我们正在做可能大数据的至少五个分配- idx(实际上,我们计算is.na()它的补)cumsum(idx),x[idx]以及x[idx][cumsum(idx)]-所以有进一步改进的余地,比如,在C