programing

NA를 최신 비NA 값으로 대체

elecom 2023. 6. 22. 21:27
반응형

NA를 최신 비NA 값으로 대체

data.frame(또는)data.table값에 를 ".), 이전의비앞하 NA 값을 "로작성으고싶습"입니다.벡터를 사용하는 간단한 예(대신)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

의 (총 최대 소형 크기의 소총형여대(에 1Tb) ▁for▁many▁operation▁(▁this▁i합▁sized다니▁need여▁repeattotal러tb▁to야반▁small총복1해▁~(소해▁tb))에 대해 이 작업을 반복해야 합니다.data.frame인 경우 s(~30-50Mb)입니다.그 문제에 접근하는 좋은 방법은 무엇입니까?

내가 만든 추악한 해결책은 다음과 같은 기능을 사용합니다.

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

효과가 있을 것 같습니다.하지만, 친구, 그것은 못생겼나요?좋은 의견이라도 있나?

당신은 아마도 사용하기를 원할 것입니다.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'▁i▁as,,▁need▁i다니상습켰▁because시기▁became▁i▁function자▁my▁own▁most▁often주것▁this▁reminded▁my▁up▁that을▁answers▁oftenvoted는않다▁was저,이것는이는지maxgap. + 가 있어서 하기 위해 .제가 debug를 할 수 없는 plyr + date를 사용했을 때 동물원은 엣지 케이스에 이상한 문제가 있어서, 저는 예전의 기능을 향상시키기 위해 오늘 이것으로 돌아왔습니다.

저는 향상된 기능과 여기에 있는 다른 모든 항목을 벤치마킹했습니다.은, 기본기집경우의합능,,tidyr::fill가장 빠르면서도 에지 케이스를 실패하지 않습니다.@더 입력 이 없습니다는 @BrandonBertelsen의 Rcpp에 대한 를 잘못 했습니다).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만 해당)에 그 기능을 넣었습니다.

a 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

이 접근 방식은 순방향 채우기 0에서도 작동할 수 있습니다.

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

은 규모에 수 유용하게 됩니다. 순방향 는 방 규 와 순 채 별 수 를 위 이 유 합 니 용 다 매 우 서 에 치 기 우 향 방 할 행 룹 그 법 은 터 모 가 큰 데 이 ▁with ( ▁this ▁by ▁and ▁trivial ▁at 합 니 이 다 ▁becomes ▁scale 용 ▁group ▁method ▁where s ▁on ▁is 이는 다음과 같습니다.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

당신은 할 수 .data.tablenafill 있수는에서 할 수 있습니다.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

참고:

현재 이중정수 데이터 유형만 [data.table 1.12.6 지원됩니다.

이 기능은 곧 확장될 것입니다. 미해결 문제 nafill을 참조하고 문자, 요인기타 유형에 대한 setnafill을 참조하십시오. 여기서 임시 해결 방법도 찾을 수 있습니다.

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

내 모자 던지기:

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;
}

빅데이터 볼륨을 처리할 때, 우리는 더 효율적으로 하기 위해 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 

선도적인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)

이 기능을 사용해 보십시오.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

다양한 패키지가 제공됩니다.na.locf(NA마지막 관찰 이월) 기능:

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

또한 이 기능의 이름이 다른 다른 패키지도 있습니다.

Brandon Bertelsen (Rcpp)은 Rcpp을 의미합니다.저에게는 Numeric Vector 버전이 작동하지 않았습니다. 첫 번째 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;
}')

@AdamO의 솔루션을 수정했습니다.이것은 더 빨리 달립니다. 왜냐하면 그것은 그것을 우회하기 때문입니다.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)]

를 사용하는 다음 솔루션을 .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로 작성되었습니다.따라서 뛰어난 효율성을 제공합니다.

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

축소는 유사한 작업에 유용할 수 있는 훌륭한 기능적 프로그래밍 개념입니다.불행게에도그것배은다 ~보다 ~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)
    }

만약 당신이 이 기능을 데이터 프레임에 적용하고 싶다면, 당신의 데이터 프레임이 df라고 불리는 경우, 간단히.

df[]<-lapply(df,replace_na_with_previous)

질문과 유사한 문제를 가진 다른 사람들에게 도움이 될 수 있기 때문에 이 글을 올립니다.

최근의 가장최의근▁thetidyverse를사한솔션을 vctrs는 패지를다결수있다습니합할과음키▁can와 결합할 수 .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"

이 솔루션은 아직 실험 수명 주기 중이므로 구문이 변경될 수 있습니다.

다음을 시도했습니다.

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 앞의 마지막 양호한 값으로 바꿉니다.

다른 제안보다 더 효율적인지는 모르겠지만 저에게는 효과가 있었습니다.

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)
}

파티에 너무 늦었지만, 사용하기에 매우 간결하고 확장 가능한 답변입니다.library(data.table) 따서로사수할있다습니용라▁as로 사용할 수 .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 아닌 값으로 대체하는 것일 수 있습니다.

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

속도가 중요한 경우 루프의 마지막 비-NA 값을 전파하는 루프는 RCPP를 사용하여 작성할 수 있습니다.입력 유형을 유연하게 사용하려면 템플릿을 사용하여 이 작업을 수행할 수 있습니다.

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수따다음과같이결다정니됩라워에▁depending다니결정됩'.data.table::nafill또는vctrs::vec_fill_missing가장 빠릅니다.

언급URL : https://stackoverflow.com/questions/7735647/replacing-nas-with-latest-non-na-value

반응형