如果被少于五个连续零包围,则将向量中的零更改为一

Change zero to ones in vector if surrounded by less than five consecutive zeros

我有一个 0s 和 1s 的向量,我想识别 0s 的字符串被 1s 包围的索引。如果 1s 之间的 0s 的数量小于或等于 5,我想将这些零更改为 1s.

这是一个例子:

> x <- c(0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1)

7,8,9位置我只有3个零,需要改成1,其他零都超过5个,不用改。

生成的向量应如下所示:

> x_converted <- c(0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1)

我正在使用 for 循环和 if else 语句来执行此操作,但我确信必须有更快的方法来执行此操作。

谢谢。

rle() (run-length-encoding) 函数使这变得非常简单。

x <- c(0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1)
r <- rle(x)
## modify values appropriately
r$values[r$values==0 & r$lengths<=5] <- 1
## convert back to full vector
x_new <- rep(r$values, r$lengths)
## [1] 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1

然而,这仍然需要针对文字边缘情况进行一些调整 — 这已将 3 个零的初始 运行 转换为 1。也许

n <- length(r$values)
rv_int <- r$values[2:(n-1)]
rl_int <- r$lengths[2:(n-1)]
rv_int[rv_int == 0 &
       rl_int <= 5] <- 1
x_new <- rep(c(r$values[1],  rv_int, r$values[n]),
             c(r$lengths[1], rl_int, r$lengths[n]))

您可以使用 rle() 获取 运行。然后根据 运行 的长度更改它,通过查看 cumprod().

排除第一个 运行
x_rle <- rle(x)

x_0 <- cumprod(x_rle$values == 0)
x_rev_0 <- rev(cumprod(rev(x_rle$values) == 0))

x_rle$values <- ifelse(
  x_rle$lengths > 5 | x_0 | x_rev_0,
  x_rle$values,
  1
)

rep(x_rle$values, x_rle$lengths)
#>  [1] 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1

With data.table::rleid: rleid 创建 run-length 类型组 ID,在 ave 中用作分组因子。 ave 然后对 r.

定义的组执行函数
r <- data.table::rleid(x)
# [1] 1 1 1 2 2 2 3 3 3 4 4 5 5 5 5 5 5 6 6 6 6
sub <- !r %in% c(1, max(r)) 

x[sub] <- ave(x[sub], r[sub], FUN = function(x) ifelse(length(x) <= 3 & x == 0, 1, x))
# [1] 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1

rle 的可能解决方案,它不会改变 x 开头或结尾的零短序列:

# create the run length encoding
r <- rle(x)

# create an index of which zero's should be changed
i <- r$values == 0 & r$lengths < 5 & 
  c(tail(r$values, -1) == 1, FALSE) & 
  c(FALSE, head(r$values, -1) == 1)

# set the appropriate values to 1
r$values[i] <- 1

# use the inverse of rle to recreate the vector
inverse.rle(r)

给出:

[1] 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1

一种不同的方法,基于将 x 转换为字符串然后再转换回数字向量:

library(tidyverse)

x <- c(0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1)

x %>% str_c(collapse = "") %>% 
  str_replace_all("(?<=1)0{1,5}(?=1)", \(x) str_dup("1", nchar(x))) %>% 
  str_split("") %>% flatten %>% as.numeric

#>  [1] 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1

purrr::walkrle:

library(purrr)

x <- c(0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,1,1,1,1)

z <- rle(x)

walk(1:(length(z$values)-3), 
  ~ if (all(z$values[.x:(.x+2)] == c(1,0,1)) & z$lengths[.x+1] <= 5)
     z$values[.x+1] <<- 1)

inverse.rle(z)

#>  [1] 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1