Test for equality among all elements of a single numeric vector

我试图测试一个向量的所有元素是否相等。我提出的解决方案似乎有些迂回,都涉及检查 length()

x <- c(1, 2, 3, 4, 5, 6, 1)  # FALSE
y <- rep(2, times = 7)       # TRUE

unique():

length(unique(x)) == 1
length(unique(y)) == 1

rle():

length(rle(x)$values) == 1
length(rle(y)$values) == 1

一个解决方案,将让我包括一个容忍值的评估’平等’之间的元素将是理想的,以避免 FAQ 7.31问题。

Is there a built-in function for type of test that I have completely overlooked? identical() and all.equal() compare two R objects, so they won't work here.

编辑1

下面是一些基准测试结果:

library(rbenchmark)


John <- function() all( abs(x - mean(x)) < .Machine$double.eps ^ 0.5 )
DWin <- function() {diff(range(x)) < .Machine$double.eps ^ 0.5}
zero_range <- function() {
if (length(x) == 1) return(TRUE)
x <- range(x) / mean(x)
isTRUE(all.equal(x[1], x[2], tolerance = .Machine$double.eps ^ 0.5))
}


x <- runif(500000);


benchmark(John(), DWin(), zero_range(),
columns=c("test", "replications", "elapsed", "relative"),
order="relative", replications = 10000)

结果是:

          test replications elapsed relative
2       DWin()        10000 109.415 1.000000
3 zero_range()        10000 126.912 1.159914
1       John()        10000 208.463 1.905251

看起来 diff(range(x)) < .Machine$double.eps ^ 0.5是最快的。

96905 次浏览

你可以通过比较第一个元素和所有其他元素来使用 identical()all.equal(),有效地将比较横跨:

R> compare <- function(v) all(sapply( as.list(v[-1]),
+                         FUN=function(z) {identical(z, v[1])}))
R> compare(x)
[1] FALSE
R> compare(y)
[1] TRUE
R>

这样你就可以根据需要在 identical()中添加任何 ε。

> isTRUE(all.equal( max(y) ,min(y)) )
[1] TRUE
> isTRUE(all.equal( max(x) ,min(x)) )
[1] FALSE

另一个也是如此:

> diff(range(x)) < .Machine$double.eps ^ 0.5
[1] FALSE
> diff(range(y)) < .Machine$double.eps ^ 0.5
[1] TRUE

我使用这种方法,比较最小值和最大值,除以平均值:

# Determine if range of vector is FP 0.
zero_range <- function(x, tol = .Machine$double.eps ^ 0.5) {
if (length(x) == 1) return(TRUE)
x <- range(x) / mean(x)
isTRUE(all.equal(x[1], x[2], tolerance = tol))
}

如果您更认真地使用它,您可能希望在计算范围和平均值之前删除缺失的值。

如果它们都是数值,那么如果 tol 是你的容差,那么..。

all( abs(y - mean(y)) < tol )

是解决你问题的办法。

编辑:

在看过这个和其他答案,并对一些事情进行基准测试之后,下面的结果比 DWin 的答案快一倍。

abs(max(x) - min(x)) < tol

This is a bit surprisingly faster than diff(range(x)) since diff shouldn't be much different than - and abs with two numbers. Requesting the range should optimize getting the minimum and maximum. Both diff and range are primitive functions. But the timing doesn't lie.

And, in addition, as @Waldi pointed out, abs is superfluous here.

因为我一遍又一遍地回到这个问题,这里有一个 Rcpp解决方案,如果答案实际上是 FALSE(因为一旦遇到不匹配它就会停止) ,它通常比任何 R解决方案都要快得多,如果答案是 TRUE,它的速度将与最快的 R 解决方案相同。例如,对于 OP 基准测试,system.time使用这个函数正好在0时进入。

library(inline)
library(Rcpp)


fast_equal = cxxfunction(signature(x = 'numeric', y = 'numeric'), '
NumericVector var(x);
double precision = as<double>(y);


for (int i = 0, size = var.size(); i < size; ++i) {
if (var[i] - var[0] > precision || var[0] - var[i] > precision)
return Rcpp::wrap(false);
}


return Rcpp::wrap(true);
', plugin = 'Rcpp')


fast_equal(c(1,2,3), 0.1)
#[1] FALSE
fast_equal(c(1,2,3), 2)
#[2] TRUE

您实际上不需要使用 min、 mean 或 max。 基于约翰的回答:

all(abs(x - x[[1]]) < tolerance)

我为此专门编写了一个函数,它不仅可以检查向量中的元素,还可以检查列表中的所有元素是否都是 一模一样。当然,它也能很好地处理字符向量和所有其他类型的向量。它还具有适当的错误处理。

all_identical <- function(x) {
if (length(x) == 1L) {
warning("'x' has a length of only 1")
return(TRUE)
} else if (length(x) == 0L) {
warning("'x' has a length of 0")
return(logical(0))
} else {
TF <- vapply(1:(length(x)-1),
function(n) identical(x[[n]], x[[n+1]]),
logical(1))
if (all(TF)) TRUE else FALSE
}
}

现在试着举几个例子。

x <- c(1, 1, 1, NA, 1, 1, 1)
all_identical(x)       ## Return FALSE
all_identical(x[-4])   ## Return TRUE
y <- list(fac1 = factor(c("A", "B")),
fac2 = factor(c("A", "B"), levels = c("B", "A"))
)
all_identical(y)     ## Return FALSE as fac1 and fac2 have different level order

这里有一个替代方案,使用最小,最大的技巧,但为一个数据帧。在这个示例中,我比较了列,但是对于行,来自 apply的页边距参数可以更改为1。

valid = sum(!apply(your_dataframe, 2, function(x) diff(c(min(x), max(x)))) == 0)

如果 valid == 0,则所有元素都是相同的

为什么不简单地使用方差:

var(x) == 0

如果 x的所有元素都相等,那么就会得到 0的方差。 但这只适用于 double 和整数。

根据以下评论进行编辑:
一个更通用的选项是检查向量中唯一元素的长度,在这种情况下必须是1。这样做的好处是,除了可以从中计算方差的双精度和整数之外,它还可以适用于所有类。

length(unique(x)) == 1

You can just check all(v==v[1])

另一个使用 data.table包的解决方案是 uniqueN(x) == 1,它与字符串和 NA兼容