【问题标题】:Split vector separated by n zeros into different group将由 n 个零分隔的向量拆分为不同的组
【发布时间】:2017-08-30 19:12:26
【问题描述】:

我有一个向量x

x = c(1, 1, 2.00005, 1, 1, 0, 0, 0, 0, 1, 2, 0, 3, 4, 0, 0, 0, 0, 1, 2, 3, 1, 3)

我需要将由n 分隔的值(在这种情况下,假设n3)或多个零分成不同的组。

期望的输出是

list(x1 = c(1, 1, 2.00005, 1, 1),
     x2 = c(1, 2, 0, 3, 4),
     x3 = c(1, 2, 3, 1, 3))
#$x1
#[1] 1.00000 1.00000 2.00005 1.00000 1.00000

#$x2
#[1] 1 2 0 3 4

#$x3
#[1] 1 2 3 1 3

以下内容不起作用,因为它会拆分 x,即使组中的零少于 n

temp = cumsum(x == 0)
split(x[x!=0], temp[x!=0])
#$`0`
#[1] 1.00000 1.00000 2.00005 1.00000 1.00000

#$`4`
#[1] 1 2

#$`5`
#[1] 3 4

#$`9`
#[1] 1 2 3 1 3

【问题讨论】:

  • lapply(strsplit(strsplit(gsub('(,0){3,},', ';', paste(x, collapse = ',')), ';')[[1L]], ','), as.numeric)

标签: r


【解决方案1】:

这是我的尝试。此方法将长度小于或等于 3 的零运行替换为 NA。由于在使用 split() 时移除了 NA,因此我们得到了所需的输出。

x <- c(1, 1, 2.00005, 1, 1, 0, 0, 0, 0, 1, 2, 0, 3, 4, 0, 0, 0, 0, 1, 2, 3, 1, 3)

ll <- with(rle(x == 0), {
  ifelse(x == 0 & (seq_along(x) != cumsum(lengths)[lengths <= 3 & values]), NA, x)
})

split(x, with(rle(is.na(ll)), rep(1:length(lengths), lengths) + ll * 0))
# $`1`
# [1] 1.00000 1.00000 2.00005 1.00000 1.00000
#
# $`3`
# [1] 1 2 0 3 4
#
# $`5`
# [1] 1 2 3 1 3

【讨论】:

  • 此代码似乎不适用于稍微不同的输入:x
【解决方案2】:

这是一个带有rlesplitlapply的方法

# get RLE
temp <- rle(x)
# replace values with grouping variables
temp$values <- cumsum(temp$values == 0 & temp$lengths > 2)

# split on group and lapply through, dropping 0s at beginning which are start of each group
lapply(split(x, inverse.rle(temp)), function(y) y[cummax(y) > 0])
$`0`
[1] 1.00000 1.00000 2.00005 1.00000 1.00000

$`1`
[1] 1 2 0 3 4

$`2`
[1] 1 2 3 1 3

没有lapply的第二种方法如下

# get RLE
temp <- rle(x)
# get positions of 0s that force grouping
changes <- which(temp$values == 0 & temp$lengths > 2)
# get group indicators
temp$values <- cumsum(temp$values == 0 & temp$lengths > 2)
# make 0s a new group
temp$values[changes] <- max(temp$values) + 1L

# create list
split(x, inverse.rle(temp))
$`0`
[1] 1.00000 1.00000 2.00005 1.00000 1.00000

$`1`
[1] 1 2 0 3 4

$`2`
[1] 1 2 3 1 3

$`3`
[1] 0 0 0 0 0 0 0 0

最后,您只需删除最后一个列表项,例如 head(split(x, inverse.rle(temp)), -1)

【讨论】:

    【解决方案3】:

    此方法与您已经提出的方法略有不同,它包括将所有 n 个或多个零的延伸替换为 x 中未找到的值的第一步,例如 max+1:

    r = rle(x)
    val = max(x,na.rm=T)+1
    r$values[r$values==0 & r$lengths>2] = val
    x2 = inverse.rle(r)
    temp = cumsum(x2 == val)
    split(x2[x2!=val], temp[x2!=val])
    
    $`0`
    [1] 1.00000 1.00000 2.00005 1.00000 1.00000
    
    $`4`
    [1] 1 2 0 3 4
    
    $`8`
    [1] 1 2 3 1 3
    

    【讨论】:

      【解决方案4】:

      另一个使用rle(两次)和inverse.rle的解决方案。

      n <- 3
      r <- rle(as.integer(x == 0))
      r$values[r$values == 1 & r$lengths < n] <- 0
      r <- rle(inverse.rle(r))
      
      group <- integer(length(x))
      start <- 1
      for(i in seq_along(r$values)){
          group[start:(start + r$lengths[i] - 1)] <- c(1L, rep(0L, r$lengths[i] - 1))
          start <- start + r$lengths[i]
      }
      

      与此同时,我意识到上面准备循环的代码和循环本身可以大大简化。为了使它完整,我将重复最初的代码行。

      r <- rle(as.integer(x == 0))
      r$values[r$values == 1 & r$lengths < n] <- 0
      
      # This is the simplification
      group <- c(1L, diff(inverse.rle(r)) != 0)
      
      res <- split(x, cumsum(group))
      res <- res[-which(sapply(res, function(y) all(y == 0)))]
      res
      #$`1`
      #[1] 1.00000 1.00000 2.00005 1.00000 1.00000
      #
      #$`3`
      #[1] 1 2 0 3 4
      #
      #$`5`
      #[1] 1 2 3 1 3
      

      【讨论】:

        【解决方案5】:

        这是一个使用rleinverse.rle 多次创建x (x_sub) 和组号(group_sub) 的子集的想法。最后使用split得到最终结果。

        x <- c(1, 1, 2.00005, 1, 1, 0, 0, 0, 0, 1, 2, 0, 3, 4, 0, 0, 0, 0, 1, 2, 3, 1, 3)
        
        ### Step 1: Filtet the index with values == 0 and length > 3
        x2 <- as.integer(x != 0)
        run <- rle(x2)
        index <- which(run$values == 0 & run$lengths > 3)
        
        ### Step 2: Replace the values in index to -1
        ### Create an intermediate index (x3)
        run2 <- run
        run2$values[index] <- -1
        run2$values[run2$values == 0] <- 1
        x3 <- inverse.rle(run2)
        
        ### Step 3: Create grouping variable (x4)
        run3 <- rle(x3)
        run3$values <- 1:length(run3$values)
        x4 <- inverse.rle(run3)
        
        ### Step 4: Subset x by x3 and x4 (x_sub) and create group number (group_sub)
        x_sub <- x[x3 != -1]
        group_sub <- x4[x3 != -1] %/% 2 + 1
        
        ### Step 5: Split x_sub to get the final output (final_list)
        final_list <- split(x_sub, f = group_sub)
        
        final_list
        $`1`
        [1] 1.00000 1.00000 2.00005 1.00000 1.00000
        
        $`2`
        [1] 1 2 0 3 4
        
        $`3`
        [1] 1 2 3 1 3
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-11-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-09-16
          • 1970-01-01
          • 2011-10-15
          相关资源
          最近更新 更多