测试单个向量的所有元素之间的相等性


101

我正在尝试测试向量的所有元素是否彼此相等。我想出的解决方案似乎有些round回,都涉及检查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问题的理想选择。

我完全忽略了针对测试类型的内置函数吗?identical()all.equal()比较两个R对象,因此它们在这里不起作用。

编辑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最快。

Answers:


37

我使用此方法,将平均值除以最小值和最大值:

# 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))
}

如果您更认真地使用此功能,则可能需要在计算范围和均值之前删除缺失的值。


我之所以选择该产品是因为它比Dirk的速度更快。我没有数百万个元素,但这对我来说应该运行得更快一些。
KMM

@Kevin:约翰的解决方案如何?它比Hadley的快10倍,并且可以设置公差。它以其他方式缺乏吗?
约书亚·乌尔里希

请提供一些基准测试-我刚刚检查了一百万套制服的向量是否相同。
哈德利2011年

@hadley:我正在跑步system.time(for(i in 1:1e4) zero_range(x)),那x是来自OP。John的解决方案是的〜10倍x,的〜3倍快,的y稍慢runif(1e6)
约书亚·乌尔里希

当您查看0.00023和0.000023秒之间的差异时,10倍差异并不重要-DWin可能会声称它们在指定的容忍度上是相同的;)
hadley 2011年

46

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

var(x) == 0

如果所有元素x相等,则将得到方差0


17
length(unique(x))=1最终速度大约var是原来的两倍,但简洁明了,很好。
AdamO

YohanBadia,我有一个数组c(-5.532456e-09,1.695298e-09),并John test: TRUE ; DWin test: TRUE ; zero-range test: TRUE ; variance test: FALSE表示所有其他测试都认识到R中的值相同。如何在这种情况下使用方差测试?
mjs

数组中的2个值不相同。您为什么要返回测试TRUE?在约翰回答的情况下,您检查差异是否超过某个阈值。在您的情况下,两个值之间的差异非常小,这可能会导致其低于您定义的阈值。
Yohan Obadia

41

如果它们都是数值,那么如果tol是您的公差,那么...

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

是您解决问题的方法。

编辑:

在查看了此答案和其他答案并进行了一些基准测试之后,得出的结果是DWin答案的两倍。

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

这是一个令人惊讶的一点速度比diff(range(x)),因为diff不应该是远远不同-,并abs用两个数字。请求范围应该优化获取最小值和最大值。这两个diffrange是原始的功能。但是时机并没有到来。


您能否评论减去均值与除以均值的相对优点?
哈德利2011年

它在计算上更简单。根据系统以及R的编译和向量化方式,它会以更少的功耗更快地完成。同样,当您用均值除以您的测试结果时,结果相对于1,而减去时为0,这对我来说似乎更好。同样,公差具有更直接的解释。
约翰

1
但是,除法并不复杂,因为提取范围所需的搜索和排序比简单的减法运算要昂贵得多。我对其进行了测试,上面的代码比zero_range函数Hadley快了10倍(您的代码在这里是最快的正确答案)。Dirk的比较功能非常慢。这是最快的答案。
约翰

刚在您的答案Hadley中看到了Josh的时间注释...我没有发现zero_range更快的情况。如果回答此问题,差异会稍快一些(也许20%)至10倍之间。它尝试了多种方法。
约翰

24
> 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

我认为这对于少数人来说效果x <- seq(1, 10) / 1e10
不佳

2
@Hadley:OP要求一种允许指定公差的解决方案,大概是因为他并不关心很小的差异。all.equal可以与其他公差一起使用,并且OP似乎可以理解这一点。
IRTFM 2011年

2
我没有很清楚地表达自己-在我的示例中,最大和最小数字之间存在十倍的相对差异。这可能是您要注意的事情!我认为需要计算相对于数据范围的数字公差-过去我没有这样做过,这已经引起了问题。
哈德利2011年

2
我想我不会误会你的意思。我只是以为发问者正在寻求一种解决方案,该解决方案将忽略有效数字为零的十倍的相对差异。我听说他在寻求解决方案,而忽略了1e-11和1e-13之间的区别。
IRTFM 2011年

5
我试着给人们他们所需要的,而不是他们所需要的;)但是要点。
哈德利2011年

16

您可以使用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()根据需要添加任何epsilon 。


2
不过效率低下……(在我的计算机上,一百万个数字大约需要10秒)
hadley 2011年

2
毫无疑问。然而,OP被质疑是否可以这样做,在所有。做好是第二步。而且您知道我在循环中所处的位置... ;-)
Dirk Eddelbuettel 2011年

10
循环很棒吗?;)
hadley 2011年

4
我喜欢这种方法,因为它可以与非数字对象一起使用。
Luciano Selzer

比较<-function(v)all(sapply(as.list(v [-1]),FUN = function(z){isTRUE(all.equal(z,v [1]))})))
N. McA 。

16

你可以检查一下 all(v==v[1])


这是一个很棒的公元前,它也适用于字符串!谢谢
arvi1000

除非您NA的向量中有此方法,否则该方法有效: x <- c(1,1,NA); all(x == x[1])return NA,而不是FALSE。在这种情况下length(unique(x)) == 1有效。
HBat

11

由于我不断Rcpp重复讨论这个问题,因此,R如果实际答案是正确的,这是一个比任何解决方案都要快得多的解决方案FALSE(因为它会在遇到不匹配时立即停止)并具有相同的速度如果答案是,则为最快的R解决方案TRUE。例如,对于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

1
这是不错的选择,并且可以提高+1的速度,但是我不相信将所有元素与第一个元素进行比较是正确的。向量可以通过此测试,但是max(x)和min(x)之间的差大于精度。例如fast_equal(c(2,1,3), 1.5)
dww

@dww您要指出的是,当您遇到精度问题时,比较不是可传递的-即a == bb == c不一定a == c表示您在进行浮点比较。您可以将精度除以元素数来避免此问题,也可以修改算法以进行计算min并将max其用作停止条件。
埃迪(EDDI)'17

10

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

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

4

您实际上不需要使用最小值,平均值或最大值。根据约翰的回答:

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

3

这是使用最小,最大技巧的替代方法,但用于数据帧。在示例中,我正在比较列,但apply可以将行的margin参数从更改为1。

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

如果valid == 0所有元素都相同

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.