na 在特定值后填充

Na fill after a specific value

我想在 1 之后使用 NA.fill,但在 -1 之后保留 NA。有没有简单的解决方案?

Old New
1 1
NA 1
NA 1
NA 1
-1 -1
NA NA
NA NA
1 1
NA 1
NA 1

可重现的示例数据

dat <- read.table(text = "
Old New
1   1
NA  1
NA  1
NA  1
-1  -1
NA  NA
NA  NA
1   1
NA  1
NA  1", header = TRUE)

编辑:我在列中只有 1 和 -1。谢谢大家,答案很有帮助。我的 'New' 专栏现在正是我想要的。

cumsum:

df$Old[as.logical(cumsum(replace(df$Old, is.na(df$Old), 0)))] <- 1

你可以使用循环

x = c(1,NA,NA,NA,-1,NA,NA,1,NA,NA)
for (i in seq_along(x)[-1]) {
  if (!is.na(x[i-1]) & x[i-1] == 1 & is.na(x[i])) x[i] = 1
}
# [1]  1  1  1  1 -1 NA NA  1  1  1

这是一种使用 rle 的方法,提供或接受 hack。

r <- rle(ifelse(is.na(dat$Old), -Inf, dat$Old))
r$values[is.infinite(r$values)] <- NA_integer_
r
# Run Length Encoding
#   lengths: int [1:6] 1 3 1 2 1 2
#   values : num [1:6] 1 NA -1 NA 1 NA

ind <- is.na(r$values[-1]) & r$values[-length(r$values)] == 1
ind
# [1]  TRUE FALSE FALSE FALSE  TRUE
r$values[c(FALSE, ind)] <- r$values[c(ind, FALSE)]
inverse.rle(r)
#  [1]  1  1  1  1 -1 NA NA  1  1  1

备注:

  • rle 将所有缺失值(即 NA)视为 不相等 ,这违背了我们预期使用的 运行 长度编码;我通过首先将 NA 转换为 -Inf(有点随意,我假设极不可能出现在真实数据中)、运行 和 rle,然后转换回 NA
  • is.na(r$values[-1]) & r$values[-length(r$values)] == 1判断一个值是否为NA且前一个值为1;
  • 我们使用该值(如 ind)来确定要替换哪些值 (c(F, ind)) 以及用哪些值替换它们 (c(ind, F));
  • inverse.rle 做了它应该做的事情:重新生成矢量,但现在 1-following-NA 值更改为 1,没有其他更改

如果逻辑改为 “填充 NA,除非先前的值不是 -1”(如果还有非 1 值应该填充)通过将 ind 计算从 == 1 更改为 != -1.

使用data.table:

library(data.table)

setDT(dat)[, x := fifelse(is.na(Old) & head(Old, 1) == 1, head(Old, 1), Old), 
          by = cumsum(!is.na(Old)) ]

df
#     Old New  x
#  1:   1   1  1
#  2:  NA   1  1
#  3:  NA   1  1
#  4:  NA   1  1
#  5:  -1  -1 -1
#  6:  NA  NA NA
#  7:  NA  NA NA
#  8:   1   1  1
#  9:  NA   1  1
# 10:  NA   1  1

您可以通过 fillifelse

library(tidyverse)
dat <- structure(list(Old = c(1L, NA, NA, NA, -1L, NA, NA, 1L, NA, NA
)), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame"
)) 

dat %>% 
mutate(New = Old) %>% 
fill(New) %>% 
mutate(New = ifelse(New == -1, Old, New)) %>% 
select(Old, New)

结果:

# A tibble: 10 x 2
     Old   New
   <int> <int>
 1     1     1
 2    NA     1
 3    NA     1
 4    NA     1
 5    -1    -1
 6    NA    NA
 7    NA    NA
 8     1     1
 9    NA     1
10    NA     1

我认为 也可能有帮助。

类似于@Otto Kässi 使用 zoo::na.locf -

的逻辑
transform(dat, New = zoo::na.locf(Old)) |>
  transform(New = ifelse(New == -1, Old, New))

#   Old New
#1    1   1
#2   NA   1
#3   NA   1
#4   NA   1
#5   -1  -1
#6   NA  NA
#7   NA  NA
#8    1   1
#9   NA   1
#10  NA   1

purrr::reduce:

library(tidyverse)

reduce(2:nrow(dat), function(x,y) {
  if (is.na(x$Old[y]) & !is.na(x$Old[y-1]) & x$Old[y-1] == 1) x$Old[y] <- 1; x},
  .init=dat)
#>    Old New
#> 1    1   1
#> 2    1   1
#> 3    1   1
#> 4    1   1
#> 5   -1  -1
#> 6   NA  NA
#> 7   NA  NA
#> 8    1   1
#> 9    1   1
#> 10   1   1