【问题标题】:How to extract all rows between start signal and end signal?如何提取开始信号和结束信号之间的所有行?
【发布时间】:2015-12-28 19:35:04
【问题描述】:

我有以下df,我想根据以下开始和结束信号提取所有行。

启动信号:状态从 1 变为 0 时 结束信号:当状态从 0 变为 -1 时。

df <- data.frame(time = rep(1:14), status = c(0,1,1,0,0,0,-1,0,1,0,0,0,-1,0))

   time status
1     1      0
2     2      1
3     3      1
4     4      0
5     5      0
6     6      0
7     7     -1
8     8      0
9     9      1
10   10      0
11   11      0
12   12      0
13   13     -1
14   14      0

愿望:

   time status    
4     4      0
5     5      0
6     6      0
10   10      0
11   11      0
12   12      0

【问题讨论】:

  • 状态从 1 变为 0 时定义开始信号,结束信号为 0 到 -1 时定义。因此,在示例中,我想获取从 4 到 6 和 10 到 12 的所有行。

标签: r dataframe filtering


【解决方案1】:

我们计算开始和结束标记,然后使用这些值和 (start - end) 的累积和来过滤行。 (cumsum(start)-cumsum(end)&gt;1) 是一个小技巧,以避免累积计数被第 2 行打乱,第 2 行开始但没有结束;否则第 14 行会被包含在内。

require(dplyr)

df %>% mutate(start=(status==1), end=(status==-1)) %>%
       filter(!start & !end & (cumsum(start)-cumsum(end)>1) ) %>%
       select(-start, -end)

#   time status
# 1    4      0
# 2    5      0
# 3    6      0
# 4   10      0
# 5   11      0
# 6   12      0

【讨论】:

  • @David Arenburg:这与 “当状态 = 1 作为开始信号且状态 = -1 作为结束信号时提取所有行”的问题有关”。然后,OP 在问题下添加的评论中完全更改了问题。请少一些恶作剧。
  • OP 想要提取 1 到 -1 序列之间的所有行,对我来说看起来很清楚。
  • @DavidArenburg: “当状态 = 1 作为开始信号且状态 = -1 作为结束信号时,我想提取所有行” 字面意思是要求状态 = 的所有行= +1 或 -1。显然这不是他们的意思。正如我已经在上面发布的那样,我正在根据 OP 的重述问题修改我的代码。
  • 现在看起来好多了 (+1)
【解决方案2】:

有点难看,但您总是可以循环遍历这些值并保留一个标志来确定是否应该保留该元素。

keepers <- rep(FALSE, nrow(df))
flag <- FALSE
for(i in 1:(nrow(df)-1)) {
    if(df$status[i] == 1 && df$status[i+1] == 0) { 
        flag <- TRUE
        next  # keep signal index false
    }
    if(df$status[i] == -1 && df$status[i+1] == 0) {
        flag <- FALSE
        next  # keep signal index false
    }
    keepers[i] <- flag
}
keepers[nrow(df)] <- flag  # Set the last element to final flag value
newdf <- df[keepers, ]  # subset based on the T/F values determined

【讨论】:

    【解决方案3】:

    这是使用data.table 包的可能解决方案。我基本上首先按status == 1 出现分组,然后检查每个组是否还有status == -1,如果有,我将分组从第二个事件到-1 事件减去1

    library(data.table)
    setDT(df)[, indx := cumsum(status == 1)]
    df[, if(any(status == -1)) .SD[2:(which(status == -1) - 1)], by = indx]
    #    indx time status
    # 1:    2    4      0
    # 2:    2    5      0
    # 3:    2    6      0
    # 4:    3   10      0
    # 5:    3   11      0
    # 6:    3   12      0 
    

    【讨论】:

      【解决方案4】:

      您是否有更多数据(或者您可以生成更多您知道结果的数据)来看看这个/这些是否可以概括?

      两种类似的方法:

      library(stringr)
      
      df <- data.frame(time = rep(1:14), status = c(0,1,1,0,0,0,-1,0,1,0,0,0,-1,0))
      
      dfr <- rle(df$status)
      
      # first approach 
      
      find_seq_str <- function() {
        str_locate_all(paste(gsub("-1", "x", dfr$values), collapse=""), "10")[[1]][,2]
      }
      
      df[as.vector(sapply(find_seq_str(), 
        function(n) {
          i <- sum(dfr$lengths[1:(n-1)])
          tail(i:(i+dfr$lengths[n]), -1)
        })),]
      
      
      # second approach
      
      find_seq_ts <- function() {
        which(apply(embed(dfr$values, 2), 1, function(x) all(x == c(0, 1))))
      }
      
      df[as.vector(sapply(find_seq_ts(), 
        function(n) {
          i <- sum(dfr$lengths[1:(n)])+1
          head(i:(i+dfr$lengths[n+1]), -1)
        })),]
      

      这两种方法都需要对status 向量进行游程编码。

      第一个对-1 进行单个字符替换,因此我们可以创建一个明确的连续字符串,然后使用str_locate 找到告诉我们目标序列何时开始的对,然后从rle 重建零范围长度。

      如果它需要以 R 为基础,我可以尝试使用 regexpr 制作一些东西。

      第二个构建一个配对矩阵并比较相同的目标序列。

      注意事项:

      • 我没有进行基准测试
      • 如果status 很大,两者都会创造潜在的大事。
      • 我并不完全肯定它可以概括(因此我的初始 q)。
      • David 的代码更具可读性、可维护性和可转移性,但您可以处理使用 data.table 带来的所有“优点”;-)

      我将这些方法封装在函数中,因为它们可能会被参数化,但您也可以轻松地将值分配给变量或将其推入 sapply(呃,寿)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-16
        • 2023-03-22
        • 1970-01-01
        • 2021-04-14
        • 2018-03-26
        • 2020-12-19
        • 2014-04-10
        • 2019-02-13
        相关资源
        最近更新 更多