如何使用 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】:也许which
和diff
的组合可以使用:
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
的第一个元素的位置开始构建序列s
。
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 与您最初的缓慢答案一起使用。以上是关于如何使用 R 在向量中找到第二个非连续出现的值的索引?的主要内容,如果未能解决你的问题,请参考以下文章
在数据表或连续表单视图中的表单上,我们如何将第二个组合框中的可能值基于第一个组合框中选择的值?