R:将向量中的下两个值相互比较(如果可能,不循环)

R: compare the next two values in a vector with each other (without looping if possible)

我有一个这样的向量:

10  7  7 10  7 10  7 10 10  7 10 10  7  7 10 10  7 10  7  7 10  7 10

我想成对比较向量的条目:例如第一个条目与第二个条目,第三个条目与第四个条目,直到成对我有两个相等的条目。 在这个例子中,两个相等的值第一次出现在第六对中,或者换句话说,第 11 和第 12 个值相等。 重要的是现在我想要第 11 行的索引并继续比较第 12 行和第 13 行。

有没有好的方法(我宁愿不用循环)?

编辑: 我真的没有解释清楚自己。当一对值相等时,我想删除这两个值的第一个条目。因此,这些对的索引从一开始就不知道。 在上面的示例中,所需的输出将是:

10  7  7 10  7 10  7 10 10  7 10  7  7 10 10  7 10  7  7 10  7 10

以及被删除行的索引:

11

在这种情况下,只需删除一行,即所有对都由 7 和 10 组成。

如果为对创建索引,则可以使用 tapply。例如:

x=c(10,  7,  7, 10,  7, 10,  7, 10, 10,  7, 10, 10,  7,  7, 10, 10, 7, 10,  7,  7, 10,  7, 10,12) #note the addition of "12" to create an even number of pairs.
pair=(seq_along(v1)-1) %/%2 +1 #create an index for the pairs. Thanks to @akrun for this bit of code
tapply(x,pair,function(x) x[1]==x[2])
#    1     2     3     4     5     6     7     8     9    10    11    12 
#FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE FALSE 

结果 returns 一个 TRUE 或 FALSE 值,对应于该对的值是否匹配。

注意 如果向量中没有偶数(即不完整的对),索引将不起作用,所以我在你的例子中添加了一个数字)。

您可以提取向量中的奇数和偶数条目并进行比较:

x=c(10,  7,  7, 10,  7, 10,  7, 10, 10,  7, 10, 10,  7,  7, 10, 10, 7, 10,  7,  7, 10,  7, 10,12)
x[seq(1, length(x), 2)] == x[seq(2, length(x), 2)]
# [1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE FALSE

这比成对分组并单独进行每个比较要快得多:

# Slightly larger dataset
set.seed(144)
x <- sample(1:10, 1000, replace=T)

# Grouping solution from @user7598's post
josilber <- function(x) x[seq(1, length(x), 2)] == x[seq(2, length(x), 2)]
user7598 <- function(x) tapply(x, (seq_along(x)-1) %/%2 +1, function(y) y[1]==y[2])
all.equal(josilber(x), unname(as.vector(user7598(x))))
# [1] TRUE

# Compare speed on 1000-length vector
library(microbenchmark)
microbenchmark(josilber(x), user7598(x))
# Unit: microseconds
#         expr      min       lq     mean   median       uq        max neval
#  josilber(x)   74.350  109.319  223.102  164.961  242.236   2411.465   100
#  user7598(x) 2271.347 2440.235 5040.763 3119.307 5356.552 110777.522   100

我们在长度为 1000 的向量上看到了 20 倍的加速。这是因为将奇数索引与偶数索引进行比较利用了矢量化——它对 == 进行了一次调用,包含了所有需要的数据进行比较。同时,如果您分组然后比较每个较小的组,您将在较小的向量上多次调用 ==

你也可以试试

 f1 <- function(v){
  if(length(v)%%2!=0)
    v <- v[-length(v)]
   m1 <- matrix(v, nrow=2)
   m1[1,] == m1[2,]
 }

 f1(v1)
 #[1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE FALSE

基准

set.seed(144)
x <- sample(1:10, 1000, replace=T)
library(microbenchmark)
microbenchmark(josilber(x), akrun=f1(x), unit='relative', times=20L)
#Unit: relative
#    expr      min       lq     mean  median       uq      max neval cld
#josilber(x) 4.791352 4.768276 4.675041 4.64354 4.474515 5.340249    20   b
#      akrun 1.000000 1.000000 1.000000 1.00000 1.000000 1.000000    20  a 

identical(josilber(x), f1(x))
#[1] TRUE

数据

v1 <- c(10, 7, 7, 10, 7, 10, 7, 10, 10, 7, 10, 10, 7, 7, 10, 10, 7,
    10, 7, 7, 10, 7, 10)

根据问题的编辑版本,现在很明显您需要某种循环函数,因为您对先前索引的决定会影响您对后续索引的决定。我能想到的最有效的方法是填充一个逻辑向量,指示每个索引是否应该保留在向量中。之后您可以使用逻辑向量来获取剩余的值和被删除的索引。

x <- c(10,  7,  7, 10,  7, 10,  7, 10, 10,  7, 10, 10,  7,  7, 10, 10, 7, 10,  7,  7, 10,  7, 10)
keep <- rep(TRUE, length(x))
even <- TRUE
for (pos in 2:length(x)) {
  if (even & x[pos] == x[pos-1]) {
    keep[pos-1] <- FALSE
  } else {
    even <- !even
  }
}
x[keep]
# [1] 10  7  7 10  7 10  7 10 10  7 10  7  7 10 10  7 10  7  7 10  7 10
which(!keep)
# [1] 11

与任何循环函数一样,Rcpp 可用于加速:

library(Rcpp)
cppFunction(
"LogicalVector getBin(NumericVector x) {
  const int n = x.size();
  LogicalVector keep(n, true);
  bool even = true;
  for (int pos=1; pos < n; ++pos) {
    if (even && x[pos] == x[pos-1]) {
      keep[pos-1] = false;
    } else {
      even = !even;
    }
  }
  return keep;
}")

纯 R 和 Rcpp 方法的基准测试:

# Slightly larger dataset
set.seed(144)
x <- sample(1:10, 1000, replace=T)

# Functions to compare
pureR <- function(x) {
  keep <- rep(TRUE, length(x))
  even <- TRUE
  for (pos in 2:length(x)) {
    if (even & x[pos] == x[pos-1]) {
      keep[pos-1] <- FALSE
    } else {
      even <- !even
    }
  }
  list(x[keep], which(!keep))
}
with.Rcpp <- function(x) {
  keep <- getBin(x)
  list(x[keep], which(!keep))
}
all.equal(pureR(x), with.Rcpp(x))
# [1] TRUE
library(microbenchmark)
microbenchmark(pureR(x), with.Rcpp(x))
# Unit: microseconds
#          expr     min       lq       mean   median       uq       max neval
#      pureR(x) 855.318 1066.177 1806.67855 1140.656 1442.869 35379.369   100
#  with.Rcpp(x)  30.137   62.304   86.80656   78.132   94.771   348.598   100

对于长度为 1000 的向量,我们看到使用 Rcpp 的速度提高了 10 倍以上。显然,这种加速只适用于更大的向量。