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

时间:2014-02-26 17:38:32

标签: r

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

一些示例向量:

示例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)

5 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:3)

也许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 :(得分:3)

这是一个只有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

答案 3 :(得分:2)

你可以试试这个:

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. 从第一个元素的位置s开始构建一个序列x
  3. w==s=TRUE是第一次出现时连续出现的那些事件,因此您希望返回w!=s的第一个位置,即第一个位置不是第一个位置。

答案 4 :(得分:2)

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

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