如何使用 R 在向量中找到第二个非连续出现的值的索引?

Posted

技术标签:

【中文标题】如何使用 R 在向量中找到第二个非连续出现的值的索引?【英文标题】:How to find the index of the second, non-sequential occurrence of a value in a vector using 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 函数非常有用。 【参考方案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
    检查向量中的哪些元素等于x。 从等于x的第一个元素的位置开始构建序列sw==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 与您最初的缓慢答案一起使用。

以上是关于如何使用 R 在向量中找到第二个非连续出现的值的索引?的主要内容,如果未能解决你的问题,请参考以下文章

问,R语言中如何判断两个向量是不是有相同值,并返回

在数据表或连续表单视图中的表单上,我们如何将第二个组合框中的可能值基于第一个组合框中选择的值?

如何在 r 的范围内返回 .csv 列中的值的数量

如何查找一行数据中倒数第二个非空单元格的内容呢?

如何使用 plotly 在散点图中显示第三个变量的第二个图例?

使用第二个&在子集向量中时R函数错误