J'essaie de tester si tous les éléments d'un vecteur sont égaux les uns aux autres. Les solutions que j'ai trouvées semblent quelque peu détournées, toutes deux impliquant la vérification de 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 FAQ 7.31 problèmes.
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 objets R, donc ils ne fonctionneront pas ici.
Modifier 1
Voici quelques résultats d'analyse 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
Il semble donc que diff(range(x)) < .Machine$double.eps ^ 0.5
soit le plus rapide.
J'utilise cette méthode, qui compare le min et le max, après avoir divisé 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 l'utilisiez plus sérieusement, vous voudriez probablement supprimer les valeurs manquantes avant de calculer la plage et la moyenne.
Si ce 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é cela et d'autres réponses, et comparé certaines choses, les éléments suivants sont deux fois plus rapides que la réponse DWin.
abs(max(x) - min(x)) < tol
C'est un peu plus rapide que diff(range(x))
puisque diff
ne devrait pas être très différent de -
Et abs
avec deux nombres. La demande de la plage doit optimiser l'obtention du minimum et du maximum. diff
et range
sont des fonctions primitives. Mais le timing ne ment pas.
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
.
> 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
Vous pouvez utiliser identical()
et all.equal()
en comparant le premier élément à tous les autres, en balayant efficacement la comparaison:
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 ajouter n'importe quel epsilon à identical()
au besoin.
Puisque je reviens à cette question encore et encore, voici une solution Rcpp
qui sera généralement beaucoup plus rapide que n'importe laquelle des solutions R
si la réponse est en fait FALSE
(car il s'arrêtera au moment où il rencontrera un décalage) et aura la même vitesse que la solution R la plus rapide si la réponse est TRUE
. Par exemple pour la référence OP, system.time
horloge à exactement 0 en utilisant cette fonction.
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
J'ai écrit une fonction spécialement pour cela, qui peut vérifier non seulement les éléments d'un vecteur, mais aussi capable de vérifier si tous les éléments d'une liste sont identiques. Bien sûr, il gère aussi 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
}
}
Essayez maintenant 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 en fait pas besoin d'utiliser min, moyenne ou max. D'après la réponse de John:
all(abs(x - x[[1]]) < tolerance)
Voici une alternative utilisant l'astuce min, max mais pour une trame de données. Dans l'exemple, je compare les colonnes, mais le paramètre de marge 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