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 倍以上。显然,这种加速只适用于更大的向量。
我有一个这样的向量:
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 倍以上。显然,这种加速只适用于更大的向量。