【问题标题】:Test for equality among all elements of a single numeric vector测试单个数值向量的所有元素之间的相等性
【发布时间】:2011-06-12 17:50:10
【问题描述】:

我正在尝试测试向量的所有元素是否彼此相等。我提出的解决方案似乎有些迂回,都涉及检查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)) &lt; .Machine$double.eps ^ 0.5 是最快的。

【问题讨论】:

  • 对于没有容忍度的相等,max(x) == min(x)diff(range(x)) 快一个数量级,并且可以处理字符和数字

标签: r vector equality


【解决方案1】:

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

var(x) == 0

如果x 的所有元素都相等,您将得到0 的方差。

【讨论】:

  • length(unique(x))=1 的速度大约是原来的两倍,但 var 很简洁,这很好。
  • YohanBadia,我有一个数组 c(-5.532456e-09, 1.695298e-09),并得到 John test: TRUE ; DWin test: TRUE ; zero-range test: TRUE ; variance test: FALSE,这意味着所有其他测试都认识到 R 中的值是相同的。如何进行方差测试在那种情况下使用?
  • 数组中的 2 个值不相同。为什么您希望测试返回 TRUE ?在约翰的回答中,您检查差异是否高于某个阈值。在您的情况下,这两个值之间的差异非常小,这可能导致它低于您定义的阈值。
  • “为什么不简单地使用方差”?因为var(x)NA 对于x &lt;- c("a", "b")
【解决方案2】:

如果它们都是数值,那么如果 tol 是您的容忍度,那么...

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

是您问题的解决方案。

编辑:

在查看了这个和其他答案,并对一些事情进行基准测试后,以下结果的速度是 DWin 答案的两倍。

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

这比diff(range(x)) 快了一点,因为diff-abs 的两个数字应该没有太大区别。请求范围应该优化获得最小值和最大值。 diffrange 都是原始函数。但时间不会说谎。

此外,正如@Waldi 指出的那样,abs 在这里是多余的。

【讨论】:

  • 您能评论一下减去均值与除以均值相比的相对优点吗?
  • 计算更简单。根据系统以及 R 的编译和矢量化方式,它将以更少的功耗更快地完成。此外,当您除以平均值时,您的测试结果相对于 1,而减法为 0,这对我来说似乎更好。此外,容差有更直接的解释。
  • 但是除法并不复杂,因为提取范围所需的搜索和排序比简单的减法计算成本高得多。我对其进行了测试,上面的代码比 zero_range 函数 Hadley 快了大约 10 倍(你的代码大约是这里最快的正确答案)。 Dirk 的比较功能非常缓慢。这是这里最快的答案。
  • 刚刚在您的回答 Hadley 中看到了 Josh 的计时 cmets...我没有遇到 zero_range 更快的情况。如果这个答案,差异在稍微快一点(可能是 20%)到 10 倍之间总是有利的。它尝试了多种方法。
  • @Waldi 好点。虽然,需要保留减法周围的括号。
【解决方案3】:

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

# 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 的快。我没有数百万个元素,但这对我来说应该运行得更快。
  • @Kevin:John 的解决方案怎么样?它比 Hadley 的速度快约 10 倍,并且允许您设置容差。是否有其他方面的不足?
  • 请提供一些基准 - 我刚刚检查过我的对于一百万个制服的向量大致相同。
  • @hadley:我正在运行 system.time(for(i in 1:1e4) zero_range(x)),其中 x 来自 OP。 John 的解决方案是 x 约 10 倍,y 快约 3 倍,runif(1e6) 稍慢。
  • 当您查看 0.00023 和 0.000023 秒之间的差异时,10 倍的差异并不重要 - DWin 可能会声称它们在指定的容差程度上是相同的 ;)
【解决方案4】:

您可以查看all(v==v[1])

【讨论】:

  • 这个很棒,因为它也适用于字符串!谢谢
  • 此方法有效,除非您的向量中有 NAx &lt;- c(1,1,NA); all(x == x[1]) 返回 NA,而不是 FALSE。在这种情况下,length(unique(x)) == 1 有效。
【解决方案5】:
> 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 &lt;- seq(1, 10) / 1e10
  • @Hadley:OP 要求提供一种允许指定公差的解决方案,大概是因为他不关心非常小的差异。 all.equal 可以与其他公差一起使用,并且 OP 似乎理解这一点。
  • 我表达的不是很清楚——在我的例子中,最大和最小数字之间有十倍的相对差异。这可能是您要注意的事情!我认为数值公差需要相对于数据的范围进行计算——我过去没有这样做过,它已经引起了问题。
  • 我想我一点也没有误解你。我只是认为提问者正在寻求一种解决方案,该解决方案将忽略实际上为零的数字的十倍相对差异。我听到他要求提供一种忽略 1e-11 和 1e-13 之间差异的解决方案。
  • 我试着给人们他们需要的东西,而不是他们想要的东西;)但重点是。
【解决方案6】:

您可以通过将第一个元素与所有其他元素进行比较来使用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> 

这样您就可以根据需要将任何 epsilon 添加到 identical()

【讨论】:

  • 虽然效率极低...(在我的电脑上,一百万个数字大约需要 10 秒)
  • 毫无疑问。然而,OP 质疑这是否可以完全完成。做好是第二步。你知道我在循环中的立场...... ;-)
  • 循环很棒吗? ;)
  • 我喜欢这个方法的地方是它可以与非数字对象一起使用。
  • 比较
【解决方案7】:

由于我一遍又一遍地回到这个问题,这里有一个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

【讨论】:

  • 这对速度来说很好并且 +1,但我不相信将所有元素与第一个元素进行比较是完全正确的。一个向量可以通过这个测试,但是 max(x) 和 min(x) 之间的差异大于精度。例如fast_equal(c(2,1,3), 1.5)
  • @dww 您要指出的是,当您遇到精度问题时,比较不是传递性的 - 即 a == bb == c 如果您正在使用浮点数,则不一定意味着 a == c比较。您可以将精度除以元素数以避免此问题,或者修改算法以计算 minmax 并将其用作停止条件。
【解决方案8】:

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

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

【讨论】:

    【解决方案9】:

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

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

    【讨论】:

      【解决方案10】:

      这里是使用 min, max 技巧的替代方法,但用于数据帧。在示例中,我正在比较列,但 apply 的边距参数可以更改为 1 用于行。

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

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

      【讨论】:

        【解决方案11】:

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

        【讨论】:

          最近更新 更多