提问人:Ryogi 提问时间:10/12/2011 最后编辑:AlSubRyogi 更新时间:11/13/2023 访问量:138620
将 NA 替换为最新的非 NA 值
Replacing NAs with latest non-NA value
问:
在 (或 ) 中,我想用最接近的先前非 NA 值“向前填充”NA。使用向量(而不是 )的简单示例如下:data.frame
data.table
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)小尺寸 (~30-50 Mb) 重复此操作,其中一行是 NA 是它的所有条目。解决问题的好方法是什么?data.frame
我制作的丑陋解决方案使用此功能:
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
...这似乎有效。但是,伙计,是不是很丑!有什么建议吗?
答:
您可能希望使用 zoo 包中的函数来转发最后一个观测值以替换 NA 值。na.locf()
以下是帮助页面中其使用示例的开头:
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
评论
na.locf
na.rm
na.locf(cz, na.rm=FALSE)
NA
对不起,挖了一个老问题。 我无法查找在火车上完成这项工作的功能,所以我自己写了一个。
我很自豪地发现它快了一点点。
不过,它不太灵活。
但它与 ,这是我需要的。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
编辑
当这成为我最赞成的答案时,我经常被提醒我不使用自己的函数,因为我经常需要动物园的论点。因为当我使用 dplyr + dates 时,zoo 在边缘情况下会遇到一些奇怪的问题,我无法调试,所以我今天回到这里来改进我的旧函数。maxgap
我在这里对改进的功能和所有其他条目进行了基准测试。对于基本功能集,速度最快,同时也不会在边缘情况下失败。@BrandonBertelsen 的 Rcpp 条目仍然更快,但它在输入类型方面不灵活(由于对 的误解,他错误地测试了边缘情况)。tidyr::fill
all.equal
如果你需要,我下面的函数比动物园快(并且没有日期的奇怪问题)。maxgap
我把我的测试文档放了出来。
新功能
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
}
我还将该函数放在我的前包中(仅限 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
评论
if (!anyNA(x)) return(x)
处理大数据量,为了提高效率,我们可以使用 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)
}
评论
replaceNaWithLatest <- function( dfIn, nameColsNa = names(dfIn)[1] ){ dtTest <- data.table(dfIn) invisible(lapply(nameColsNa, function(nameColNa){ 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
评论
replace_na_with_last(c(NA,1:4,NA))
imputeTS::na.locf(x, na.remaining = "rev")
replace_na_with_last<-function(x,p=is.na,d=0)c(d,x)[cummax(seq_along(x)*(!p(x)))+1]
把我的帽子扔进去:
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]
nullIdx 获取 masterData$RequiredColumn 具有 Null/NA 值的 idx 编号。 在下一行中,我们将其替换为相应的 Idx-1 值,即每个 NULL/NA 之前的最后一个良好值
评论
1 NA NA
1 1 NA
as.array()
有一堆软件包提供(Last Observation Carry Forward)功能:na.locf
NA
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
相反,可以采用与 IntegerVector 函数完全相同的方法。以下内容对我有用:
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
评论
拥有前导有点皱纹,但是我发现在不缺少前导术语的情况下,一种非常可读(和矢量化)的LOCF方法是:NA
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 解决方案的修改。这个运行速度更快,因为它绕过了该函数。这将覆盖 vector 中的值(前导 s 除外)。na.omit
NA
y
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
如果你有几列...NA
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
请注意:
目前只有 double 和 integer 数据类型是 [] 支持。
data.table 1.12.6
该功能很可能很快就会得到扩展;请参阅未解决的问题 nafill、setnafill for character、factor 和其他类型,您还可以在其中找到临时解决方法。
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 中,它比上面的答案慢 ~70 倍。repeat.before
我个人使用这个功能。我不知道它有多快或多慢。但它无需使用库即可完成其工作。
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)
}
如果要在 DataFrame 中应用此函数,如果 DataFrame 称为 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
评论
as.data.frame()
data.frame
我想添加下一个使用 r cran 包的解决方案。runner
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 编写的。从而提供了很高的效率。
我在这里发布这个,因为这可能对其他有类似问题的人有所帮助。
可以使用该包的最新解决方案来创建新列tidyverse
vctrs
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
在更改“填充方向”时,会导致:'up'
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
值,base 中的一个选项可能是:
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
当只有少数几个存在时,它们可以被最新的非 NA 值的值覆盖,而不是创建一个新的向量。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 数量或最快的 NA 数量。data.table::nafill
vctrs::vec_fill_missing
您可以使用我的函数,该函数针对由许多组组成的数据进行了优化。roll_na_fill()
示例基准测试
# remotes::install_github("NicChr/timeplyr")
library(timeplyr)
library(vctrs)
library(data.table)
library(zoo)
library(imputeTS)
library(ggplot2)
library(microbenchmark)
x <- sample.int(10^2, 10^5, TRUE)
x[sample.int(10^5, round(10^5/3))] <- NA
groups <- sample.int(10^3, 10^5, TRUE)
dt <- data.table(x, groups)
### No groups
m1 <- microbenchmark(data.table = dt[, filled1 := data.table::nafill(x, type = "locf")][]$filled1,
vctrs = dt[, filled3 := vctrs::vec_fill_missing(x)][]$filled3,
zoo = dt[, filled4 := zoo::na.locf0(x)][]$filled4,
timeplyr = dt[, filled5 := .roll_na_fill(x)][]$filled5,
imputeTS = dt[, filled6 := na_locf(x, na_remaining = "keep")][]$filled6,
times = 20)
autoplot(m1)
### With groups
m2 <- microbenchmark(data.table = dt[, filled1 := data.table::nafill(x, type = "locf"),
by = groups][]$filled1,
vctrs = dt[, filled3 := vctrs::vec_fill_missing(x), by = groups][]$filled3,
zoo = dt[, filled4 := zoo::na.locf0(x), by = groups][]$filled4,
timeplyr1 = dt[, filled5 := .roll_na_fill(x), by = groups][]$filled5,
timeplyr2 = dt[, filled7 := roll_na_fill(x, g = groups)][]$filled7,
imputeTS = dt[, filled6 := na_locf(x, na_remaining = "keep"),
by = groups][]$filled6,
times = 20)
autoplot(m2)
创建于 2023-11-12 with reprex v2.0.2
下一个:如何从列表中删除元素?
评论
roll=TRUE
data.table