将NAS替换为最新的非NA值

data.frame(或data.table)中,我想要";填充转发";具有最接近的先前非NA值的NAS.下面是一个使用矢量(而不是data.frame)的简单示例:

> y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)

我想要一个fill.NAs()的函数,它允许我构造yy,使得:

> yy
[1] NA NA NA  2  2  2  2  3  3  3  4  4

我需要对许多(总计约1 TB)小型_重复此操作ABC_0(约30-50 MB),其中行是NA,是其所有条目。解决这个问题的好方法是什么?

我提出的丑陋的解决方案使用了这个函数:

last <- function (x){
x[length(x)]
}


fill.NAs <- function(isNA){
if (isNA[1] == 1) {
isNA[1:max({which(isNA==0)[1]-1},1)] <- 0 # first is NAs
# can't be forward filled
}
isNA.neg <- isNA.pos <- isNA.diff <- diff(isNA)
isNA.pos[isNA.diff < 0] <- 0
isNA.neg[isNA.diff > 0] <- 0
which.isNA.neg <- which(as.logical(isNA.neg))
if (length(which.isNA.neg)==0) return(NULL) # generates warnings later, but works
which.isNA.pos <- which(as.logical(isNA.pos))
which.isNA <- which(as.logical(isNA))
if (length(which.isNA.neg)==length(which.isNA.pos)){
replacement <- rep(which.isNA.pos[2:length(which.isNA.neg)],
which.isNA.neg[2:max(length(which.isNA.neg)-1,2)] -
which.isNA.pos[1:max(length(which.isNA.neg)-1,1)])
replacement <- c(replacement, rep(last(which.isNA.pos), last(which.isNA) - last(which.isNA.pos)))
} else {
replacement <- rep(which.isNA.pos[1:length(which.isNA.neg)], which.isNA.neg - which.isNA.pos[1:length(which.isNA.neg)])
replacement <- c(replacement, rep(last(which.isNA.pos), last(which.isNA) - last(which.isNA.pos)))
}
replacement
}

功能fill.NAs的使用方法如下:

y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
isNA <- as.numeric(is.na(y))
replacement <- fill.NAs(isNA)
if (length(replacement)){
which.isNA <- which(as.logical(isNA))
to.replace <- which.isNA[which(isNA==0)[1]:length(which.isNA)]
y[to.replace] <- y[replacement]
}

输出

> y
[1] NA  2  2  2  2  3  3  3  4  4  4

...这似乎很有效。但是,伙计,它很丑吗?有什么建议?

128129 次浏览

您可能希望使用动物园程序包中的na.locf()函数来把最后一次观察向前推进,以替换NA值。

以下是帮助页面中其用法示例的开头部分:

library(zoo)


az <- zoo(1:6)


bz <- zoo(c(2,NA,1,4,5,2))


na.locf(bz)
1 2 3 4 5 6
2 2 1 4 5 2


na.locf(bz, fromLast = TRUE)
1 2 3 4 5 6
2 1 1 4 5 2


cz <- zoo(c(NA,9,3,2,3,2))


na.locf(cz)
2 3 4 5 6
9 3 2 3 2

很抱歉翻出了一个老问题。 我在火车上查不到做这个工作的函数,所以我自己写了一个。

我很自豪地发现它稍微快了一点。
但它不太灵活。

但它可以很好地处理ave,这正是我所需要的。

repeat.before = function(x) {   # repeats the last non NA value. Keeps leading NA
ind = which(!is.na(x))      # get positions of nonmissing values
if(is.na(x[1]))             # if it begins with a missing, add the
ind = c(1,ind)        # first position to the indices
rep(x[ind], times = diff(   # repeat the values at these indices
c(ind, length(x) + 1) )) # diffing the indices + length yields how often
}                               # they need to be repeated


x = c(NA,NA,'a',NA,NA,NA,NA,NA,NA,NA,NA,'b','c','d',NA,NA,NA,NA,NA,'e')
xx = rep(x, 1000000)
system.time({ yzoo = na.locf(xx,na.rm=F)})
## user  system elapsed
## 2.754   0.667   3.406
system.time({ yrep = repeat.before(xx)})
## user  system elapsed
## 0.597   0.199   0.793

编辑

当这成为我最喜欢的答案时,我经常被提醒不要使用我自己的函数,因为我经常需要zoo的maxgap参数。因为当我使用无法调试的dplyr+日期时,Zoo在边缘情况下出现了一些奇怪的问题,所以我今天回到这里来改进我的旧功能。

我在这里对我改进的函数和所有其他条目进行了基准测试。对于基本特征集,tidyr::fill是最快的,同时也不会使边缘情况失败。@BrandonBertelsen的RCPP输入更快,但输入类型不够灵活(由于对all.equal的误解,他错误地测试了边缘情况)。

如果您需要maxgap,我下面的函数比Zoo更快(并且没有日期的奇怪问题)。

我把我的测试的文档

新功能

repeat_last = function(x, forward = TRUE, maxgap = Inf, na.rm = FALSE) {
if (!forward) x = rev(x)           # reverse x twice if carrying backward
ind = which(!is.na(x))             # get positions of nonmissing values
if (is.na(x[1]) && !na.rm)         # if it begins with NA
ind = c(1,ind)                 # add first pos
rep_times = diff(                  # diffing the indices + length yields how often
c(ind, length(x) + 1) )          # they need to be repeated
if (maxgap < Inf) {
exceed = rep_times - 1 > maxgap  # exceeding maxgap
if (any(exceed)) {               # any exceed?
ind = sort(c(ind[exceed] + 1, ind))      # add NA in gaps
rep_times = diff(c(ind, length(x) + 1) ) # diff again
}
}
x = rep(x[ind], times = rep_times) # repeat the values at these indices
if (!forward) x = rev(x)           # second reversion
x
}

我还将函数放在我的FORMR包中(仅限GitHub)。

试试这个功能。它不需要Zoo包:

# last observation moved forward
# replaces all NA values with last non-NA values
na.lomf <- function(x) {


na.lomf.0 <- function(x) {
non.na.idx <- which(!is.na(x))
if (is.na(x[1L])) {
non.na.idx <- c(1L, non.na.idx)
}
rep.int(x[non.na.idx], diff(c(non.na.idx, length(x) + 1L)))
}


dim.len <- length(dim(x))


if (dim.len == 0L) {
na.lomf.0(x)
} else {
apply(x, dim.len, na.lomf.0)
}
}

示例:

> # vector
> na.lomf(c(1, NA,2, NA, NA))
[1] 1 1 2 2 2
>
> # matrix
> na.lomf(matrix(c(1, NA, NA, 2, NA, NA), ncol = 2))
[,1] [,2]
[1,]    1    2
[2,]    1    2
[3,]    1    2

处理大数据量,为了更高效,我们可以使用Data.Table包。

require(data.table)
replaceNaWithLatest <- function(
dfIn,
nameColNa = names(dfIn)[1]
){
dtTest <- data.table(dfIn)
setnames(dtTest, nameColNa, "colNa")
dtTest[, segment := cumsum(!is.na(colNa))]
dtTest[, colNa := colNa[1], by = "segment"]
dtTest[, segment := NULL]
setnames(dtTest, "colNa", nameColNa)
return(dtTest)
}

这对我很有效:

  replace_na_with_last<-function(x,a=!is.na(x)){
x[which(a)[c(1,1:sum(a))][cumsum(a)+1]]
}




> replace_na_with_last(c(1,NA,NA,NA,3,4,5,NA,5,5,5,NA,NA,NA))


[1] 1 1 1 1 3 4 5 5 5 5 5 5 5 5


> replace_na_with_last(c(NA,"aa",NA,"ccc",NA))


[1] "aa"  "aa"  "aa"  "ccc" "ccc"

速度也很合理:

> system.time(replace_na_with_last(sample(c(1,2,3,NA),1e6,replace=TRUE)))




user  system elapsed


0.072   0.000   0.071

把我的帽子扔进去:

library(Rcpp)
cppFunction('IntegerVector na_locf(IntegerVector x) {
int n = x.size();


for(int i = 0; i<n; i++) {
if((i > 0) && (x[i] == NA_INTEGER) & (x[i-1] != NA_INTEGER)) {
x[i] = x[i-1];
}
}
return x;
}')

设置基本示例和基准:

x <- sample(c(1,2,3,4,NA))


bench_em <- function(x,count = 10) {
x <- sample(x,count,replace = TRUE)
print(microbenchmark(
na_locf(x),
replace_na_with_last(x),
na.lomf(x),
na.locf(x),
repeat.before(x)
), order = "mean", digits = 1)
}

并运行一些基准测试:

bench_em(x,1e6)


Unit: microseconds
expr   min    lq  mean median    uq   max neval
na_locf(x)   697   798   821    814   821 1e+03   100
na.lomf(x)  3511  4137  5002   4214  4330 1e+04   100
replace_na_with_last(x)  4482  5224  6473   5342  5801 2e+04   100
repeat.before(x)  4793  5044  6622   5097  5520 1e+04   100
na.locf(x) 12017 12658 17076  13545 19193 2e+05   100

以防万一:

all.equal(
na_locf(x),
replace_na_with_last(x),
na.lomf(x),
na.locf(x),
repeat.before(x)
)
[1] TRUE

更新

对于数值向量,函数稍有不同:

NumericVector na_locf_numeric(NumericVector x) {
int n = x.size();
LogicalVector ina = is_na(x);


for(int i = 1; i<n; i++) {
if((ina[i] == TRUE) & (ina[i-1] != TRUE)) {
x[i] = x[i-1];
}
}
return x;
}

我试过下面的:

nullIdx <- as.array(which(is.na(masterData$RequiredColumn)))
masterData$RequiredColumn[nullIdx] = masterData$RequiredColumn[nullIdx-1]
只要MasterData$RequiredColumn具有NULL/NA值

,NULLIDX就会获取IDX编号。 在下一行中,我们将其替换为相应的idx-1值,即每个NULL/NA

之前的最后一个好值

有一系列软件包提供na.locfNA上次观察结转)功能:

  • xts-xts::na.locf
  • zoo-zoo::na.locf
  • imputeTS-imputeTS::na.locf
  • spacetime-spacetime::na.locf

以及该函数以不同方式命名的其他包。

这对我来说很有效,尽管我不确定它是否比其他建议更有效。

rollForward <- function(x){
curr <- 0
for (i in 1:length(x)){
if (is.na(x[i])){
x[i] <- curr
}
else{
curr <- x[i]
}
}
return(x)
}

跟进Brandon Bertelsen的RCPP贡献。对我来说,NumericVector版本不起作用:它只是取代了第一个NA.这是因为ina向量仅在函数开始时计算一次。

相反,可以采用与整数向量函数完全相同的方法。以下内容对我很有效:

library(Rcpp)
cppFunction('NumericVector na_locf_numeric(NumericVector x) {
R_xlen_t n = x.size();
for(R_xlen_t i = 0; i<n; i++) {
if(i > 0 && !R_finite(x[i]) && R_finite(x[i-1])) {
x[i] = x[i-1];
}
}
return x;
}')

如果您需要CharacterVector版本,也可以使用相同的基本方法:

cppFunction('CharacterVector na_locf_character(CharacterVector x) {
R_xlen_t n = x.size();
for(R_xlen_t i = 0; i<n; i++) {
if(i > 0 && x[i] == NA_STRING && x[i-1] != NA_STRING) {
x[i] = x[i-1];
}
}
return x;
}')

data.table解决方案:

dt <- data.table(y = c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA))
dt[, y_forward_fill := y[1], .(cumsum(!is.na(y)))]
dt
y y_forward_fill
1: NA             NA
2:  2              2
3:  2              2
4: NA              2
5: NA              2
6:  3              3
7: NA              3
8:  4              4
9: NA              4
10: NA              4

这种方法也适用于向前填充零:

dt <- data.table(y = c(0, 2, -2, 0, 0, 3, 0, -4, 0, 0))
dt[, y_forward_fill := y[1], .(cumsum(y != 0))]
dt
y y_forward_fill
1:  0              0
2:  2              2
3: -2             -2
4:  0             -2
5:  0             -2
6:  3              3
7:  0              3
8: -4             -4
9:  0             -4
10:  0             -4

此方法在缩放的数据上非常有用,并且您可能希望按组执行正向填充,这对于data.table是微不足道的。只需将组添加到by子句中,位于cumsum逻辑之前。

dt <- data.table(group = sample(c('a', 'b'), 20, replace = TRUE), y = sample(c(1:4, rep(NA, 4)), 20 , replace = TRUE))
dt <- dt[order(group)]
dt[, y_forward_fill := y[1], .(group, cumsum(!is.na(y)))]
dt
group  y y_forward_fill
1:     a NA             NA
2:     a NA             NA
3:     a NA             NA
4:     a  2              2
5:     a NA              2
6:     a  1              1
7:     a NA              1
8:     a  3              3
9:     a NA              3
10:     a NA              3
11:     a  4              4
12:     a NA              4
13:     a  1              1
14:     a  4              4
15:     a NA              4
16:     a  3              3
17:     b  4              4
18:     b NA              4
19:     b NA              4
20:     b  2              2

有一个前导NA有点麻烦,但我发现当前导项缺失时,执行LOCF的一种可读性很强(且矢量化)的方法是:

na.omit(y)[cumsum(!is.na(y))]

通常情况下,可读性稍差的修改是可行的:

c(NA, na.omit(y))[cumsum(!is.na(y))+1]

给出所需的输出:

c(NA, 2, 2, 2, 2, 3, 3, 4, 4, 4)

下面是对@Adamo解决方案的修改。这个运行得更快,因为它绕过了na.omit功能。这将覆盖矢量y中的NA值(前导NA除外)。

   z  <- !is.na(y)                  # indicates the positions of y whose values we do not want to overwrite
z  <- z | !cumsum(z)             # for leading NA's in y, z will be TRUE, otherwise it will be FALSE where y has a NA and TRUE where y does not have a NA
y  <- y[z][cumsum(z)]

您可以使用data.table功能nafill(可从data.table >= 1.12.3获得)。

library(data.table)
nafill(y, type = "locf")
# [1] NA  2  2  2  2  3  3  4  4  4

如果向量是data.table中的列,则还可以通过引用setnafill来更新它:

d <- data.table(x = 1:10, y)
setnafill(d, type = "locf", cols = "y")
d
#      x  y
#  1:  1 NA
#  2:  2  2
#  3:  3  2
#  4:  4  2
#  5:  5  2
#  6:  6  3
#  7:  7  3
#  8:  8  4
#  9:  9  4
# 10: 10  4

如果您有_ABC,则在多个列中_0。

d <- data.table(x = c(1, NA, 2), y = c(2, 3, NA), z = c(4, NA, 5))
#     x  y  z
# 1:  1  2  4
# 2: NA  3 NA
# 3:  2 NA  5

...您可以通过引用一次填写它们:

setnafill(d, type = "locf")
d
#    x y z
# 1: 1 2 4
# 2: 1 3 4
# 3: 2 3 5

请注意:

只有使加倍整数数据类型当前是[data.table 1.12.6] 支持.

该功能很可能很快就会得到扩展;请参阅打开问题NAFILL、SETNAFILL用于字符、因子和其他类型,您还可以在其中找到临时解决方法

fill.NAs <- function(x) {is_na<-is.na(x); x[Reduce(function(i,j) if (is_na[j]) i else j, seq_len(length(x)), accumulate=T)]}


fill.NAs(c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA))


[1] NA  2  2  2  2  3  3  4  4  4

Reduce是一个很好的函数式编程概念,可能对类似的任务有用。不幸的是,在R中,它比上面答案中的repeat.before慢约70倍。

我个人使用这个功能。我不知道它是快还是慢。但它不需要使用库就可以完成工作。

replace_na_with_previous<-function (vector) {
if (is.na(vector[1]))
vector[1] <- na.omit(vector)[1]
for (i in 1:length(vector)) {
if ((i - 1) > 0) {
if (is.na(vector[i]))
vector[i] <- vector[i - 1]
}
}
return(vector)
}

如果你想在数据帧中应用这个函数,如果你的数据帧名为DF,那么只需

df[]<-lapply(df,replace_na_with_previous)

tidyr程序包(tidyverse程序包套件的一部分)有一种简单的方法来实现这一点:

y = c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)


# first, transform it into a data.frame


df = as.data.frame(y)
y
1  NA
2   2
3   2
4  NA
5  NA
6   3
7  NA
8   4
9  NA
10 NA


library(tidyr)
fill(df, y, .direction = 'down')
y
1  NA
2   2
3   2
4   2
5   2
6   3
7   3
8   4
9   4
10  4

我想添加下一个解决方案,它使用runner R CRAN包。

library(runner)
y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
fill_run(y, FALSE)
[1] NA  2  2  2  2  3  3  4  4  4

对整个软件包进行了优化,其中大部分是用CPP编写的。因此提供了很高的效率。

我把这个贴在这里,因为这可能对其他有类似问题的人有帮助。

使用vctrs包的最新tidyverse解决方案可以与mutate组合以创建新列

library(dplyr)
library(magrittr)
library(vctrs)


as.data.frame(y) %>%
mutate(y_filled = vec_fill_missing(y, direction = c("down")) )

回报

   y  y_filled
1  NA       NA
2   2        2
3   2        2
4  NA        2
5  NA        2
6   3        3
7  NA        3
8   4        4
9  NA        4
10 NA        4

将“填充方向”更改为_ABC时,_0导致:

    y  y_filled
1  NA        2
2   2        2
3   2        2
4  NA        3
5  NA        3
6   3        3
7  NA        4
8   4        4
9  NA       NA
10 NA       NA

可能还需要尝试"downup""updown"

请注意,此解决方案仍处于实验生命周期,因此语法可能会更改。

对当事人来说太晚了,但这是一个非常简洁和可扩展的答案,可用于library(data.table),因此可用作dt[,SomeVariable:= FunctionBellow, by = list(group)]

library(imputeTS)
y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
y
[1] NA  2  2 NA NA  3 NA  4 NA NA
imputeTS::na_locf(imputeTS::na_locf(y,option = "nocb"),option="locf")
[1] 2 2 2 3 3 3 4 4 4 4

根据@Montgomery-Clift和@Adamo的回答,基于中的一个选项,NA替换为最新的非NA可以是:

y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)


i <- c(TRUE, !is.na(y[-1]))
y[i][cumsum(i)]
# [1] NA  2  2  2  2  3  3  4  4  4

当仅存在几个ABC_0的_时,它们可以被最新的非NA值的值覆盖,而不是创建新的向量。

fillNaR <- function(y) {
i <- which(is.na(y[-1]))
j <- which(diff(c(-1L,i)) > 1)
k <- diff(c(j, length(i) + 1))
i <- rep(i[j], k)
`[<-`(y, i + sequence(k), y[i])
}
fillNaR(y)
# [1] NA  2  2  2  2  3  3  4  4  4

当速度很重要时,可以使用RCPP写入传播循环中最后一个非NA值的循环。为了在输入类型上更加灵活,可以使用模板来实现。

Rcpp::sourceCpp(code=r"(
#include <Rcpp.h>
using namespace Rcpp;


template <int RTYPE>
Vector<RTYPE> FNA(const Vector<RTYPE> y) {
auto x = clone(y);  //or overwrite original
LogicalVector isNA = is_na(x);
size_t i = 0;
while(isNA[i] && i < x.size()) ++i;
for(++i; i < x.size(); ++i) if(isNA[i]) x[i] = x[i-1];
return x;
}


// [[Rcpp::export]]
RObject fillNaC(RObject x) {
RCPP_RETURN_VECTOR(FNA, x);
}
)")
fillNaC(y)
# [1] NA  2  2  2  2  3  3  4  4  4

这些函数可在lapply内使用,以将其应用于data.frame所有列

DF[] <- lapply(DF, fillNaC)

使用RCPP的其他答案(专门用于数据类型)如下所示,但也会更新输入向量。

y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)


Rcpp::cppFunction("NumericVector fillNaCN(NumericVector x) {
for(auto i = x.begin()+1; i < x.end(); ++i) if(*i != *i) *i = *(i-1);
return x;
}")


fillNaCN(y)
# [1] NA  2  2  2  2  3  3  4  4  4
y
# [1] NA  2  2  2  2  3  3  4  4  4

基准

fillNaR <- function(y) {
i <- which(is.na(y[-1]))
j <- which(diff(c(-1L,i)) > 1)
k <- diff(c(j, length(i) + 1))
i <- rep(i[j], k)
`[<-`(y, i + sequence(k), y[i])
}


Rcpp::sourceCpp(code=r"(
#include <Rcpp.h>
using namespace Rcpp;


template <int RTYPE>
Vector<RTYPE> FNA(const Vector<RTYPE> y) {
auto x = clone(y);  //or overwrite original
LogicalVector isNA = is_na(x);
size_t i = 0;
while(isNA[i] && i < x.size()) ++i;
for(++i; i < x.size(); ++i) if(isNA[i]) x[i] = x[i-1];
return x;
}


// [[Rcpp::export]]
RObject fillNaC(RObject x) {
RCPP_RETURN_VECTOR(FNA, x);
}
)")


repeat.before <- function(x) {   # @Ruben
ind = which(!is.na(x))
if(is.na(x[1])) ind = c(1,ind)
rep(x[ind], times = diff(c(ind, length(x) + 1) ))
}


RB2 <- function(x) {
ind = which(c(TRUE, !is.na(x[-1])))
rep(x[ind], diff(c(ind, length(x) + 1)))
}


MC <- function(y) { # @Montgomery Clift
z  <- !is.na(y)
z  <- z | !cumsum(z)
y[z][cumsum(z)]
}


MC2 <- function(y) {
z <- c(TRUE, !is.na(y[-1]))
y[z][cumsum(z)]
}


fill.NAs <- function(x) { # @Valentas
is_na <- is.na(x)
x[Reduce(function(i,j) if (is_na[j]) i else j, seq_len(length(x)), accumulate=T)]}


M <- alist(
fillNaR = fillNaR(y),
fillNaC = fillNaC(y),
repeat.before = repeat.before(y),
RB2 = RB2(y),
MC = MC(y),
MC2 = MC2(y),
fill.NAs = fill.NAs(y),
tidyr = tidyr::fill(data.frame(y), y)$y,
zoo = zoo::na.locf(y, na.rm=FALSE),
data.table = data.table::nafill(y, type = "locf"),
data.table2 = with(data.table::data.table(y)[, y := y[1], .(cumsum(!is.na(y)))], y),
imputeTS = imputeTS::na_locf(y, na_remaining = "keep"),
runner = runner::fill_run(y, FALSE),
vctrs = vctrs::vec_fill_missing(y, direction = "down"),
ave = ave(y, cumsum(!is.na(y)), FUN = \(x) x[1])
)

结果

n <- 1e5
set.seed(42); y <- rnorm(n); is.na(y) <- sample(seq_along(y), n/100)
bench::mark(exprs = M)  #1% NA
#   expression         min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc
#   <bch:expr>    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>
# 1 fillNaR       399.82µs   1.02ms    459.      3.56MB    31.9    230    16
# 2 fillNaC       672.85µs 883.74µs    976.      1.15MB    22.0    488    11
# 3 repeat.before   1.28ms    2.8ms    290.      7.57MB    58.0    145    29
# 4 RB2             1.93ms   3.66ms    229.      9.86MB    57.7    115    29
# 5 MC              1.01ms   1.98ms    289.      5.33MB    37.9    145    19
# 6 MC2            884.6µs   1.96ms    393.      6.09MB    53.5    198    27
# 7 fill.NAs       89.37ms   93.1ms     10.1     4.58MB    13.5      6     8
# 8 tidyr           8.42ms   11.3ms     86.3     1.55MB     5.89    44     3
# 9 zoo             1.83ms   3.19ms    216.      7.96MB    31.9    108    16
#10 data.table     73.91µs 259.71µs   2420.    797.38KB    36.0   1210    18
#11 data.table2    54.54ms  58.71ms     16.9     3.47MB     3.75     9     2
#12 imputeTS      623.69µs   1.07ms    494.      2.69MB    30.0    247    15
#13 runner          1.36ms   1.58ms    586.    783.79KB    10.0    293     5
#14 vctrs         149.98µs 317.14µs   1725.      1.53MB    54.0    863    27
#15 ave           137.87ms 149.25ms      6.53   14.77MB     8.17     4     5


set.seed(42); y <- rnorm(n); is.na(y) <- sample(seq_along(y), n/2)
bench::mark(exprs = M)  #50% NA
#  expression         min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc
#   <bch:expr>    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>
# 1 fillNaR         2.15ms   3.13ms    217.      7.92MB    59.7    109    30
# 2 fillNaC       949.22µs   1.09ms    728.      1.15MB    28.0    364    14
# 3 repeat.before   1.36ms   1.89ms    287.      4.77MB    49.6    185    32
# 4 RB2             1.64ms   2.44ms    347.      7.06MB    39.9    174    20
# 5 MC              1.48ms   1.92ms    443.      4.77MB    34.0    222    17
# 6 MC2             1.09ms   1.72ms    479.      5.53MB    45.9    240    23
# 7 fill.NAs       93.17ms 104.28ms      9.58    4.58MB     9.58     5     5
# 8 tidyr           7.09ms  10.07ms     96.7     1.55MB     3.95    49     2
# 9 zoo             1.62ms   2.28ms    344.      5.53MB    29.8    173    15
#10 data.table    389.69µs 484.81µs   1225.    797.38KB    14.0    613     7
#11 data.table2    27.46ms  29.32ms     33.4      3.1MB     3.93    17     2
#12 imputeTS        1.71ms    2.1ms    413.      3.44MB    25.9    207    13
#13 runner          1.62ms   1.75ms    535.    783.79KB     7.98   268     4
#14 vctrs         144.92µs 293.44µs   2045.      1.53MB    48.0   1023    24
#15 ave            66.38ms  71.61ms     14.0    10.78MB    10.5      8     6

根据填充的NA数量,data.table::nafillvctrs::vec_fill_missing最快。