Test auf Gleichheit zwischen allen Elementen eines einzelnen Vektors

101

Ich versuche zu testen, ob alle Elemente eines Vektors gleich sind. Die Lösungen, die ich gefunden habe, scheinen etwas umständlich zu sein, beide beinhalten Überprüfungen length().

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

Mit unique():

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

Mit rle():

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

Eine Lösung, mit der ich einen Toleranzwert für die Bewertung der „Gleichheit“ zwischen Elementen angeben könnte, wäre ideal, um Probleme mit FAQ 7.31 zu vermeiden .

Gibt es eine eingebaute Funktion für die Art des Tests, die ich völlig übersehen habe? identical()und all.equal()vergleichen Sie zwei R-Objekte, damit sie hier nicht funktionieren.

Bearbeiten 1

Hier sind einige Benchmarking-Ergebnisse. Verwenden des Codes:

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)

Mit den Ergebnissen:

          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

Es sieht also so aus, als wäre diff(range(x)) < .Machine$double.eps ^ 0.5es am schnellsten.

kmm
quelle

Antworten:

37

Ich benutze diese Methode, die das Minimum und das Maximum vergleicht, nachdem ich durch den Mittelwert dividiert habe:

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

Wenn Sie dies ernsthafter verwenden, möchten Sie wahrscheinlich fehlende Werte entfernen, bevor Sie den Bereich und den Mittelwert berechnen.

Hadley
quelle
Ich habe diesen gewählt, weil er schneller ist als der von Dirk. Ich habe nicht Millionen von Elementen, aber das sollte für mich etwas schneller gehen.
kmm
@ Kevin: Was ist mit Johns Lösung? Es ist ~ 10x schneller als das von Hadley und ermöglicht es Ihnen, Toleranz einzustellen. Ist es auf andere Weise mangelhaft?
Joshua Ulrich
Bitte geben Sie ein Benchmarking an - ich habe gerade überprüft, ob mein Benchmarking für einen Vektor von einer Million Uniformen ungefähr gleich ist.
Hadley
@ Hadley: Ich rannte system.time(for(i in 1:1e4) zero_range(x)), wo xwar aus dem OP. Johns Lösung ist ~ 10x für x, ~ 3x schneller für yund etwas langsamer für runif(1e6).
Joshua Ulrich
10x Unterschied spielt keine große Rolle, wenn Sie den Unterschied zwischen 0,00023 und 0,000023 Sekunden betrachten - und DWin würde wahrscheinlich behaupten, dass sie bis zum angegebenen Toleranzgrad gleich sind;)
Hadley
46

Warum nicht einfach die Varianz verwenden:

var(x) == 0

Wenn alle Elemente von xgleich sind, erhalten Sie eine Varianz von 0.

Yohan Obadia
quelle
17
length(unique(x))=1am Ende ist es ungefähr doppelt so schnell, aber es varist knapp, was schön ist.
AdamO
YohanBadia, ich habe ein Array c (-5.532456e-09, 1.695298e-09) und erhalte die John test: TRUE ; DWin test: TRUE ; zero-range test: TRUE ; variance test: FALSEBedeutung, dass alle anderen Tests erkennen, dass die Werte in R identisch sind. Wie kann der Varianztest in diesem Kontext verwendet werden?
mjs
Die 2 Werte in Ihrem Array sind nicht identisch. Warum soll der Test zurückkehren TRUE? Bei Johns Antwort prüfen Sie, ob der Unterschied über einem bestimmten Schwellenwert liegt. In Ihrem Fall ist der Unterschied zwischen den beiden Werten sehr gering, was dazu führen kann, dass er unter dem von Ihnen definierten Schwellenwert liegt.
Yohan Obadia
41

Wenn sie alle numerische Werte sind, wenn tol Ihre Toleranz ist, dann ...

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

ist die Lösung für Ihr Problem.

BEARBEITEN:

Nachdem Sie sich diese und andere Antworten angesehen und einige Dinge verglichen haben, ist das Folgende doppelt so schnell wie die DWin-Antwort.

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

Dies ist ein bisschen überraschend schneller als diff(range(x))da diffsollte nicht viel anders sein als -und absmit zwei Zahlen. Das Anfordern des Bereichs sollte das Erhalten des Minimums und Maximums optimieren. Beide diffund rangesind primitive Funktionen. Aber das Timing lügt nicht.

John
quelle
Können Sie die relativen Vorteile des Subtrahierens des Mittelwerts im Vergleich zur Division durch diesen kommentieren?
Hadley
Es ist rechnerisch einfacher. Abhängig vom System und wie R kompiliert und vektorisiert wird, wird es schneller mit weniger Stromverbrauch erreicht. Wenn Sie durch den Mittelwert dividieren, ist Ihr getestetes Ergebnis relativ zu 1, während es bei Subtraktion 0 ist, was mir schöner erscheint. Auch die Toleranz ist einfacher zu interpretieren.
John
1
Die Division ist jedoch nicht einmal so komplex, da die zum Extrahieren des Bereichs erforderliche Suche und Sortierung viel rechenintensiver ist als eine einfache Subtraktion. Ich habe es getestet und der obige Code ist ungefähr 10x schneller als die Zero_Range-Funktion Hadley (und Ihre ist hier die schnellste richtige Antwort). Die Vergleichsfunktion von Dirk ist brutal langsam. Dies ist hier die schnellste Antwort.
John
Ich habe gerade Joshs Timing-Kommentare in Ihrer Antwort gesehen. Hadley ... Ich bekomme keine Situationen, in denen zero_range schneller ist. Die Diskrepanz liegt zwischen etwas schneller (vielleicht 20%) und 10x immer zugunsten dieser Antwort. Es wurden verschiedene Methoden ausprobiert.
John
24
> isTRUE(all.equal( max(y) ,min(y)) )
[1] TRUE
> isTRUE(all.equal( max(x) ,min(x)) )
[1] FALSE

Ein anderer in der gleichen Richtung:

> diff(range(x)) < .Machine$double.eps ^ 0.5
[1] FALSE
> diff(range(y)) < .Machine$double.eps ^ 0.5
[1] TRUE
IRTFM
quelle
Ich denke nicht, dass dies für sehr kleine Zahlen so gut funktioniert:x <- seq(1, 10) / 1e10
Hadley
2
@ Hadley: Das OP bat um eine Lösung, die die Angabe einer Toleranz ermöglicht, vermutlich weil ihm sehr kleine Unterschiede egal waren. all.equal kann mit anderen Toleranzen verwendet werden, und das OP scheint dies zu verstehen.
IRTFM
2
Ich habe mich nicht sehr klar ausgedrückt - in meinem Beispiel gibt es einen zehnfachen relativen Unterschied zwischen der größten und der kleinsten Zahl. Das möchten Sie wahrscheinlich bemerken! Ich denke, die numerische Toleranz muss relativ zum Datenbereich berechnet werden - ich habe dies in der Vergangenheit nicht getan und es hat Probleme verursacht.
Hadley
2
Ich glaube nicht, dass ich dich im geringsten missverstanden habe. Ich dachte nur, der Fragesteller würde nach einer Lösung fragen, die einen zehnfachen relativen Unterschied für Zahlen, die effektiv Null sind, ignoriert. Ich hörte ihn nach einer Lösung fragen, die den Unterschied zwischen 1e-11 und 1e-13 ignorieren würde.
IRTFM
5
Ich versuche den Leuten zu geben, was sie brauchen, nicht was sie wollen;) Aber Punkt genommen.
Hadley
16

Sie können das erste Element verwenden identical()und all.equal()mit allen anderen vergleichen, um den Vergleich effektiv zu vertiefen:

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> 

Auf diese Weise können Sie nach Bedarf jedes Epsilon hinzufügen identical().

Dirk Eddelbuettel
quelle
2
Schrecklich ineffizient ... (auf meinem Computer dauert es ungefähr 10 Sekunden für eine Million Zahlen)
Hadley
2
Kein Zweifel. Die OP wurde jedoch in Frage , ob dies geschehen könnte überhaupt . Es gut zu machen ist ein zweiter Schritt. Und du weißt wo ich mit Loops stehe ... ;-)
Dirk Eddelbuettel
10
Dass Loops großartig sind? ;)
Hadley
4
Was ich an diesem Ansatz mag, ist, dass er mit nicht numerischen Objekten verwendet werden kann.
Luciano Selzer
vergleiche <- Funktion (v) alle (sapply (as.list (v [-1]), FUN = Funktion (z) {isTRUE (all.equal (z, v [1]))})
N. McA .
15

Sie können nur überprüfen all(v==v[1])

Maya Levy
quelle
Dieser ist großartig, weil er auch mit Streichern funktioniert! Danke
arvi1000
Dies funktioniert nur, wenn Sie NAin Ihrem Vektor x <- c(1,1,NA); all(x == x[1])Folgendes haben : Gibt NAnicht zurück FALSE. In solchen Fällen length(unique(x)) == 1funktioniert.
HBat
11

Da ich immer wieder auf diese Frage zurückkomme, ist hier eine RcppLösung, die im Allgemeinen viel schneller ist als jede der RLösungen, wenn die Antwort tatsächlich lautet FALSE(weil sie in dem Moment stoppt, in dem sie auf eine Nichtübereinstimmung stößt) und dieselbe Geschwindigkeit aufweist als schnellste R-Lösung, wenn die Antwort lautet TRUE. Beispiel: Für den OP-Benchmark wird system.timemit dieser Funktion genau 0 eingegeben.

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
eddi
quelle
1
Das ist schön & +1 für die Geschwindigkeit, aber ich bin nicht davon überzeugt, dass der Vergleich aller Elemente mit dem 1. Element ganz richtig ist. Ein Vektor kann diesen Test bestehen, der Unterschied zwischen max (x) und min (x) ist jedoch größer als die Genauigkeit. Zum Beispielfast_equal(c(2,1,3), 1.5)
Dww
@dww Was Sie weist darauf hin , dass der Vergleich ist nicht transitiv , wenn Sie Präzision Probleme haben - das heißt a == b, b == cnicht unbedingt bedeuten , a == cwenn Sie Floating - Point - Vergleiche zu tun. Sie können entweder teilen Sie Ihre Präzision durch die Anzahl der Elemente um dieses Problem zu vermeiden, oder den Algorithmus ändern zu berechnen minund maxund die Verwendung dieser als Stoppbedingung.
Eddi
10

Ich habe speziell dafür eine Funktion geschrieben, mit der nicht nur Elemente in einem Vektor überprüft werden können, sondern auch überprüft werden kann, ob alle Elemente in einer Liste identisch sind . Natürlich kann es auch Zeichenvektoren und alle anderen Arten von Vektoren gut verarbeiten. Es hat auch eine angemessene Fehlerbehandlung.

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

Versuchen Sie nun einige Beispiele.

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
Lawrence Lee
quelle
4

Sie müssen nicht unbedingt min, mean oder max verwenden. Basierend auf Johns Antwort:

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

quelle
3

Hier eine Alternative mit dem Min, Max-Trick aber für einen Datenrahmen. Im Beispiel vergleiche ich Spalten, aber der Randparameter von applykann für Zeilen in 1 geändert werden.

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

Wenn valid == 0dann alle Elemente gleich sind

pedrosaurio
quelle