如果下一个有效数据点的间隔超过2个间隔,则用零填充R中的NA

Joh*_*sau 13 replace r na

我有多个带有NA的向量,并且打算填充NA,它们距有效数据点0大于2个间隔。例如:

x <- c(3, 4, NA, NA, NA, 3, 3)
Run Code Online (Sandbox Code Playgroud)

预期的输出是

3, 4, NA, 0, NA, 3, 3 
Run Code Online (Sandbox Code Playgroud)

Shr*_*ree 13

更新-

这可能是最简单,最快的解决方案之一(感谢G. Grothendieck的回答)。只需知道该值是否NA在任意值的两侧就可以了NA。因此,使用leadlagdplyr包-

na2zero <- function(x) {
  x[is.na(lag(x, 1, 0)) & is.na(lead(x, 1, 0)) & is.na(x)] <- 0
  x
}

na2zero(x = c(3, 4, NA, NA, NA, 3, 3))
[1]  3  4 NA  0 NA  3  3

na2zero(x = c(3, 4, NA, NA, NA, NA, NA, 3, 3))
[1]  3  4 NA  0  0  0 NA  3  3

na2zero(x = c(3, 4, NA, NA, NA, 3, 3, NA, NA, 1, NA, 0, 0, rep(NA, 4L)))
[1]  3  4 NA  0 NA  3  3 NA NA  1 NA  0  0 NA  0  0 NA
Run Code Online (Sandbox Code Playgroud)

上一个答案(也很快)-

这是使用rlereplace从基数R开始的一种方法。此方法将每个NA(不是运行长度的终点)变成0-

na2zero <- function(x) {
  run_lengths <- rle(is.na(x))$lengths
  replace(x, 
    sequence(run_lengths) != 1 &
    sequence(run_lengths) != rep(run_lengths, run_lengths) &
    is.na(x),
  0)
}

na2zero(x = c(3, 4, NA, NA, NA, 3, 3))
[1]  3  4 NA  0 NA  3  3

na2zero(x = c(3, 4, NA, NA, NA, NA, NA, 3, 3))
[1]  3  4 NA  0  0  0 NA  3  3
Run Code Online (Sandbox Code Playgroud)

更新基准-

set.seed(2)
x <- c(3, 4, NA, NA, NA, 3, 3)
x <- sample(x, 1e5, T)

microbenchmark(
  Rui(x),
  Shree_old(x), Shree_new(x),
  markus(x),
  IceCreamT(x),
  Uwe1(x), Uwe2(x), Uwe_Reduce(x),
  Grothendieck(x),
  times = 50
)

all.equal(Shree_dplyr(x), Rui(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Shree_rle(x)) # [1] TRUE
all.equal(Shree_dplyr(x), markus(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Uwe1(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Uwe2(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Uwe_Reduce(x)) # [1] TRUE
all.equal(Shree_dplyr(x), Grothendieck(x)) # [1] TRUE


Unit: milliseconds
           expr        min         lq        mean     median          uq        max neval
         Rui(x) 286.026540 307.586604  342.620266 318.404731  363.844258  518.03330    50
   Shree_rle(x)  51.556489  62.038875   85.348031  65.012384   81.882141  327.57514    50
 Shree_dplyr(x)   3.996918   4.258248   17.210709   6.298946   10.335142  207.14732    50
      markus(x) 853.513854 885.419719 1001.450726 919.930389 1018.353847 1642.25435    50
   IceCreamT(x)  12.162079  13.773873   22.555446  15.021700   21.271498  199.08993    50
        Uwe1(x) 162.536980 183.566490  225.801038 196.882049  269.020395  439.17737    50
        Uwe2(x)  83.582360  93.136277  115.608342  99.165997  115.376903  309.67290    50
  Uwe_Reduce(x)   1.732195   1.871940    4.215195   2.016815    4.842883   25.91542    50
Grothendieck(x) 620.814291 688.107779  767.749387 746.699435  850.442643  982.49094    50
Run Code Online (Sandbox Code Playgroud)

PS:请检查出TiredSquirell的答案,这似乎是Uwe超前-落后答案的基本版本,但速度更快(未在上面进行基准测试)。


Rui*_*das 8

也许有更简单的解决方案,但这是可行的。

na2zero <- function(x){
  ave(x, cumsum(abs(c(0, diff(is.na(x))))), FUN = function(y){
    if(anyNA(y)){
      if(length(y) > 2) y[-c(1, length(y))] <- 0
    }
    y
  })
}

na2zero(x)
#[1]  3  4 NA  0 NA  3  3

X <- list(x, c(x, x), c(3, 4, NA, NA, NA, NA, 3, 3))
lapply(X, na2zero)
Run Code Online (Sandbox Code Playgroud)


Ice*_*can 8

这是一个data.table选项

library(data.table)

na0_dt <- function(x){
  replace(x, rowid(r <- rleid(xna <- is.na(x))) > 1 & rev(rowid(rev(r))) > 1 & xna, 0)
}
Run Code Online (Sandbox Code Playgroud)


Uwe*_*Uwe 8

为了完整起见,这里还有其他三种data.table方法:

x <- c(3, 4, NA, NA, NA, 3, 3, NA, NA, 1, NA, 0, 0, rep(NA, 4L))

library(data.table)
data.table(x)[, x := replace(x, which(is.na(x))[-c(1L, .N)], 0), by =.(rleid(is.na(x)))]$x
Run Code Online (Sandbox Code Playgroud)
[1]  3  4 NA  0 NA  3  3 NA NA  1 NA  0  0 NA  0  0 NA
Run Code Online (Sandbox Code Playgroud)
x[data.table(x)[, .I[is.na(x)][-c(1L, .N)], by =.(rleid(is.na(x)))]$V1] <- 0
x
Run Code Online (Sandbox Code Playgroud)
[1]  3  4 NA  0 NA  3  3 NA NA  1 NA  0  0 NA  0  0 NA
Run Code Online (Sandbox Code Playgroud)

shift()Reduce()

我非常专注于找到创建小组的正确方法,以至于我开始考虑直接方法的时间很晚。规则很简单:

将所有NA替换为另一个NA之前和之后的零。

这可以通过以下方式实现zoo::rollapply()G.格罗腾迪克的回答,或者使用lag()lead()斯里最新的编辑

然而,我自己的基准(这里就不贴避免重复与斯里”基准)显示,data.table::shift()并且Reduce()是迄今为止最快的方法。

  isnax <- is.na(x) 
  x[Reduce(`&`, data.table::shift(isnax, -1:1))] <- 0
  x
Run Code Online (Sandbox Code Playgroud)

这也是稍比使用快lag()lead()(请注意,与此不同斯里的版本,is.na()只调用一次):

  isnax <- is.na(x) 
  x[isnax & dplyr::lag(isnax) & dplyr::lead(isnax)] <- 0
  x
Run Code Online (Sandbox Code Playgroud)

  • @Shree,感谢您对所有不同的解决方案进行基准测试。顺便说一句:我已经从“基准测试”切换为“基准测试”,因为它可以轻松地改变问题的大小并创建图表。(我决定不发布图表,因为您已经负担了做所有基准测试的负担。) (3认同)

G. *_*eck 6

基于该示例,我假设您的意思是,如果该值为NA,并且两个方向上的相邻值均为NA(或者,一个方向为第一个或最后一个,则为一个方向),然后将该值替换为0。使用居中滚动窗口长度为3的值如果全部为NA,则返回TRUE,然后将TRUE位置替换为0。这将提供以下一线

library(zoo)

replace(x, rollapply(c(TRUE, is.na(x), TRUE), 3, all), 0)
## [1]  3  4 NA  0 NA  3  3
Run Code Online (Sandbox Code Playgroud)


Tir*_*rel 5

这是一个“非常简单”的解决方案:

is_na <- is.na(x)       # Vector telling you whether each position in x is NA
na_before <- c(F,is_na[1:(length(x)-1)])    # Whether each position has an NA before it
na_after <- c(is_na[2:length(x),F)          # Whether each position has an NA after it
x[is_na & na_before & na_after] <- 0        # Set to 0 if all three are true
Run Code Online (Sandbox Code Playgroud)

na_before和na_after的创建基于向右移动一个或向左移动一个。为了说明这是如何工作的,请考虑以下字母(我将T和F分别写为1和0,以便于区分):

              美国广播公司
is_vowel 1 0 0 0 1
元音前0 1 0 0 0
元音后0 0 0 1 0

制作vowel_before时,采用is_vowel的“ 10001”序列并将其向右移动一个(因为每个字母现在都指向其左侧的字母)。您删除最后一个1(您不必担心F之前是否有元音,因为不包括F),并且在开头添加0(第一个字母之前没有字母,因此不能元音之前)。使用相同的逻辑创建vowel_after。

编辑。(由Rui Barradas添加)

根据我的基准,该解决方案是最快的。
作为功​​能:

TiredSquirrel <- function(x){
  is_na <- is.na(x)
  na_before <- c(FALSE, is_na[1:(length(x) - 1)])
  na_after <- c(is_na[2:length(x)], FALSE)
  x[is_na & na_before & na_after] <- 0
  x
}
Run Code Online (Sandbox Code Playgroud)

和基准。

x <- c(3, 4, NA, NA, NA, 3, 3)

r <- na2zero(x)
all.equal(r, TiredSquirrel(x))
#[1] TRUE

x <- sample(x, 1e3, TRUE)
r <- na2zero(x)
all.equal(r, TiredSquirrel(x))
#[1] TRUE

microbenchmark(
  Rui = na2zero(x),
  Uwe_Reduce = Uwe_Reduce(x),
  TiredSquirrel = TiredSquirrel(x)
)
#Unit: microseconds
#          expr      min        lq       mean    median        uq      max neval cld
#           Rui 3134.293 3198.8180 3365.70736 3263.7980 3391.7900 5593.111   100   b
#    Uwe_Reduce   99.895  104.3510  125.81417  113.9995  146.7335  244.280   100  a 
# TiredSquirrel   65.205   67.4365   72.41129   70.6430   75.8315  122.061   100  a 
Run Code Online (Sandbox Code Playgroud)