Test d’égalité entre tous les éléments d’un vecteur unique

J’essaie de tester si tous les éléments d’un vecteur sont égaux. Les solutions que j’ai proposées semblent quelque peu détournées, toutes deux impliquant une vérification de la length() .

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

Avec unique() :

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

Avec rle() :

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

Une solution qui me permettrait d’inclure une valeur de tolérance pour évaluer «l’égalité» entre les éléments serait idéale pour éviter les problèmes liés à la FAQ 7.31 .

Existe-t-il une fonction intégrée pour le type de test que j’ai complètement ignoré? identical() et all.equal() comparent deux objects R, ils ne fonctionneront donc pas ici.

Modifier 1

Voici quelques résultats d’parsing comparative. En utilisant le code:

 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) 

Avec les résultats:

  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 

Donc, cela ressemble à diff(range(x)) < .Machine$double.eps ^ 0.5 est le plus rapide.

J’utilise cette méthode, qui compare le min et le max, après la division par la moyenne:

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

Si vous utilisiez cela plus sérieusement, vous voudriez probablement supprimer les valeurs manquantes avant de calculer la plage et la moyenne.

Si elles sont toutes des valeurs numériques, alors si tol est votre tolérance alors …

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

est la solution à votre problème.

MODIFIER:

Après avoir examiné cette question, ainsi que d'autres réponses, et analysé quelques points, les résultats suivants sont deux fois plus rapides que la réponse de DWin.

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

C'est un peu étonnamment plus rapide que diff(range(x)) car diff ne doit pas être très différent de - et abs avec deux nombres. Demander la plage devrait optimiser l'obtention du minimum et du maximum. diff et range sont des fonctions primitives. Mais le timing ne ment pas.

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

Un autre dans le même sens:

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

Pourquoi ne pas simplement utiliser la variance:

 var(x) == 0 

Si tous les éléments de x sont égaux, vous obtiendrez une variance de 0 .

Vous pouvez utiliser identical() et all.equal() en comparant le premier élément à tous les autres, en balayant efficacement la comparaison entre:

 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> 

De cette façon, vous pouvez append n’importe quel epsilon à identical() si nécessaire.

Étant donné que je reviens sans Rcpp à cette question, voici une solution Rcpp qui sera généralement beaucoup plus rapide que toutes les solutions R si la réponse est FALSE (car elle arrêtera le moment où elle rencontrera une incompatibilité) et aura la même vitesse que la solution R la plus rapide si la réponse est TRUE . Par exemple, pour le benchmark OP, system.time exactement à 0 en utilisant cette fonction.

 library(inline) library(Rcpp) fast_equal = cxxfunction(signature(x = 'numeric', y = 'numeric'), ' NumericVector var(x); double precision = as(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 

J’ai écrit une fonction spécifiquement pour cela, qui peut vérifier non seulement les éléments dans un vecteur, mais également capable de vérifier si tous les éléments d’une liste sont identiques . Bien sûr, il gère également bien les vecteurs de caractères et tous les autres types de vecteurs. Il a également une gestion des erreurs appropriée.

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

Maintenant, essayez quelques exemples.

 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 

Vous n’avez pas réellement besoin d’utiliser min, mean ou max. Basé sur la réponse de John:

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

Voici une alternative en utilisant le tour min, max mais pour un bloc de données. Dans l’exemple, je compare des colonnes, mais le paramètre margin de apply peut être modifié à 1 pour les lignes.

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

Si valid == 0 alors tous les éléments sont les mêmes