【问题标题】:How to find the index of the second, non-sequential occurrence of a value in a vector using R?如何使用 R 在向量中找到第二个非连续出现的值的索引?
【发布时间】:2014-03-29 17:34:46
【问题描述】:

我需要找到向量中第二个非连续出现值的索引。

一些示例向量:

示例 a) 1 1 1 2 3 4 1 1 1 2 3 4

示例 b) 1 2 3 1 1 1 3 5

请注意,向量的每个值的出现次数可能不同,并且非常大(超过 100000 个条目)

因此,如果所讨论的值为 1,则在示例中 a) 结果应返回第 7 位,b) 应返回第 4 位。

提前感谢您提供的任何帮助或建议。

示例代码:

exampleA<-c(1, 1, 1, 2, 3, 4, 1, 1, 1, 2, 3, 4)
exampleB<-c(1, 2, 3, 1, 1, 1, 3, 5)

【问题讨论】:

  • 感谢大家快速而彻底的帮助。我最终选择了@josliber 的答案,因为我能够最轻松地遵循逻辑(其他人都贡献了一些有价值的东西,但有一些超出了我的技能),它向我介绍了我不熟悉和看起来的 rle 函数非常有用。

标签: r


【解决方案1】:

也许whichdiff 的组合可以使用:

x <- which(a == 1)
x[which(diff(x) != 1)[1] + 1]
# [1] 7
y <- which(b == 1)
y[which(diff(y) != 1)[1] + 1]
# [1] 4

这是一个函数:

findFirst <- function(invec, value, event) {
  x <- which(invec == value)
  if (event == 1) out <- x[1]
  else out <- x[which(diff(x) != 1)[event-1] + 1]
  out
}
  • invec 是输入向量。
  • value 是您要查找的值。
  • event 是位置(例如,第一、第二、第三序列)。

用法如下:

findFirst(a, 1, 2)   ## event is the occurrence you want to get

对目前可用的功能进行基准测试:

set.seed(1)
a <- sample(25, 1e7, replace = TRUE)
findFirst(a, 10, 2)
# [1] 14
find.index(a, 10)
# [1] 14
op(a, 10)
# [1] 14

library(microbenchmark)
microbenchmark(findFirst(a, 10, 2), find.index(a, 10), op(a, 10), times = 5)
# Unit: milliseconds
#                 expr       min        lq    median        uq       max neval
#  findFirst(a, 10, 2)  281.6979  284.3281  301.6595  380.9089  414.9640     5
#    find.index(a, 10) 3268.0227 3312.0002 3372.3713 3444.7334 3769.0176     5
#            op(a, 10)  272.7325  278.3369  280.3172  286.0758  293.6699     5

【讨论】:

    【解决方案2】:

    向量的行程编码在这些类型的计算中很有帮助:

    find.index <- function(x, value) {
      r <- rle(x)
      match.pos <- which(r$value == value)
      if (length(match.pos) < 2) {
        return(NA)  # There weren't two sequential sets of observations
      }
      return(sum(r$length[1:(match.pos[2]-1)])+1)
    }
    
    # Test it out
    a <- c(1, 1, 1, 2, 3, 4, 1, 1, 1, 2, 3, 4)
    b <- c(1, 2, 3, 1, 1, 1, 3, 5)
    find.index(a, 1)
    # [1] 7
    find.index(b, 1)
    # [1] 4
    find.index(b, 5)
    # [1] NA
    

    【讨论】:

      【解决方案3】:

      你可以试试这个:

      op <- function(v, x){ # v=vector, x=value
          w <- which(v==x) # 1)
          s <- seq(w[1],length.out=length(w)) # 2)
          return(w[which(w!=s)[1]]) # 3)
      }
      
      > exampleA <- c(1, 1, 1, 2, 3, 4, 1, 1, 1, 2, 3, 4)
      > exampleB <- c(1, 2, 3, 1, 1, 1, 3, 5)
      > op(exampleA, 1)
      [1] 7
      > op(exampleB, 1)
      [1] 4
      
      1. 检查向量中的哪些元素等于x
      2. 从等于x的第一个元素的位置开始构建序列s
      3. w==s=TRUE 是与第一次出现连续的那些出现,因此您希望返回 w!=s 所在的第一个位置,即与第一个出现不连续的位置。

      【讨论】:

      • +1。似乎非常有效(根据我的回答中的基准)。稍微概括一下这个函数会很好。
      【解决方案4】:

      如果速度是这里的一个重要因素(并且,阅读原始帖子,它似乎可能是),那么使用 Rcpp 的自定义解决方案可能比迄今为止发布的任何纯 R 方法都更快:

      library(Rcpp)
      find.second = cppFunction(
      "int findSecond(NumericVector x, const int value) {
          bool startFirst = false;
          bool inFirst = false;
          for (int i=0; i < x.size(); ++i) {
              if (x[i] == value) {
                  if (!startFirst) {
                      startFirst = true;
                      inFirst = true;
                  } else if (!inFirst) {
                      return i+1;
                  }
              } else {
                  inFirst = false;
              }
          }
          return -1;
      }")
      

      这里是@AnandMahto 的基准,扩展到包括find.second

      set.seed(1)
      a <- sample(25, 1e7, replace = TRUE)
      findFirst(a, 10, 2)
      # [1] 14
      find.index(a, 10)
      # [1] 14
      op(a, 10)
      # [1] 14
      find.second(a, 10)
      # [1] 14
      
      microbenchmark(findFirst(a, 10, 2), find.index(a, 10), op(a, 10), find.second(a, 10), times = 5)
      # Unit: milliseconds
      #                 expr        min         lq     median         uq        max neval
      #  findFirst(a, 10, 2)   79.00000   93.85400   96.80120  118.32011  121.56636     5
      #    find.index(a, 10) 1620.83892 1673.72124 1689.06826 1747.42781 2145.90346     5
      #            op(a, 10)   78.54637   83.71081   94.20531   97.30813  195.78469     5
      #   find.second(a, 10)   14.57835   24.36220   25.24104   36.57584   47.45959     5
      

      【讨论】:

        【解决方案5】:

        这是一个仅 R 的实现,它比 Rcpp 快得惊人,尽管我们没有深入研究向量,所以我不知道这是否有意义。

        find.index.3 <- function(vec, val) {
          seq_val <- 0
          last_val <- NA
          for(i in seq_along(vec)) {
            if(identical(vec[[i]], val) & !identical(last_val, val_to_match))
              if(identical(seq_val <- seq_val + 1, 2)) break
            last_val <- vec[[i]]
          }
          i
        }
        library(microbenchmark)
        microbenchmark(find.index.3(a, 10L), find.second(a, 10))
        # Unit: milliseconds
        #                  expr       min        lq    median        uq      max neval
        #  find.index.3(a, 10L)  5.650716  5.877447  6.095766  8.003047 106.4033   100
        #    find.second(a, 10) 15.758154 18.143398 18.934030 20.247239 118.1735   100
        

        关键是避免使用查看整个向量的向量化函数。如果重复实例在向量中很深,这可能会更慢。请注意,identical() 应该非常快(编辑:实际上,使用 == 似乎更快),但这意味着您必须将值作为整数传递。

        编辑:

        如果你走得足够深,Rcpp 确实会变得更快。更改 a 使其从 10,000 个值中采样,而不是您得到的 25 个:

        # Unit: milliseconds
        #                  expr      min       lq   median       uq      max neval
        #  find.index.3(a, 10L) 80.50039 83.23213 84.27801 85.43654 186.4049   100
        #    find.second(a, 10) 17.06515 19.38969 20.52041 23.52533 125.8619   100
        

        【讨论】:

        • 这里有深度与不深度的酷分析
        • @josilber,谢谢,尽管很有趣,在您的第二个答案中遇到了所有麻烦之后,OP 与您最初的缓慢答案一起使用。
        猜你喜欢
        • 2011-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-13
        • 2018-10-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多