varias - Prueba de igualdad entre todos los elementos de un solo vector
varias graficas en r (9)
Estoy tratando de probar si todos los elementos de un vector son iguales entre sí. Las soluciones que he encontrado parecen algo indirectas, ambas implican verificar la length()
.
x <- c(1, 2, 3, 4, 5, 6, 1) # FALSE
y <- rep(2, times = 7) # TRUE
Con unique()
:
length(unique(x)) == 1
length(unique(y)) == 1
Con rle()
:
length(rle(x)$values) == 1
length(rle(y)$values) == 1
Una solución que me permita incluir un valor de tolerancia para evaluar la "igualdad" entre los elementos sería ideal para evitar los problemas de preguntas frecuentes 7.31 .
¿Hay una función incorporada para el tipo de prueba que he pasado por alto por completo? identical()
y all.equal()
comparan dos objetos R, por lo que no funcionarán aquí.
Editar 1
Aquí hay algunos resultados de evaluación comparativa. Usando el código:
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)
Con los resultados:
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
Entonces parece que diff(range(x)) < .Machine$double.eps ^ 0.5
es el más rápido.
¿Por qué no simplemente usar la varianza?
var(x) == 0
Si todos los elementos de x
son iguales, obtendrás una varianza de 0
.
Aquí una alternativa usando el truco min, max pero para un marco de datos. En el ejemplo estoy comparando columnas, pero el parámetro de margen desde apply
puede cambiarse a 1 para filas.
valid = sum(!apply(your_dataframe, 2, function(x) diff(c(min(x), max(x)))) == 0)
Si es valid == 0
entonces todos los elementos son iguales
Como sigo volviendo a esta pregunta una y otra vez, aquí hay una solución Rcpp
que generalmente será mucho más rápida que cualquiera de las soluciones R
si la respuesta es realmente FALSE
(porque se detendrá en el momento en que encuentra una discrepancia) y tendrá la misma velocidad que la solución R más rápida si la respuesta es TRUE
. Por ejemplo, para el punto de referencia OP, system.time
exactamente a 0 usando esta función.
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
En realidad, no necesita usar min, mean o max. Basado en la respuesta de John:
all(abs(x - x[[1]]) < tolerance)
Escribí una función específicamente para esto, que puede verificar no solo los elementos en un vector, sino que también puede verificar si todos los elementos en una lista son idénticos . Por supuesto, también maneja bien los vectores de caracteres y todos los otros tipos de vectores. También tiene un manejo de error apropiado.
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
}
}
Ahora prueba algunos ejemplos.
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
Puede usar identical()
y all.equal()
comparando el primer elemento con todos los demás, barriendo efectivamente la comparación en:
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 esta forma, puede agregar cualquier épsilon a identical()
según sea necesario.
Si todos son valores numéricos, entonces si tol es su tolerancia, entonces ...
all( abs(y - mean(y)) < tol )
es la solución a tu problema
EDITAR:
Después de mirar esto y otras respuestas, y comparar algunas cosas, lo siguiente sale dos veces más rápido que la respuesta DWin.
abs(max(x) - min(x)) < tol
Esto es un poco sorprendentemente más rápido que diff(range(x))
ya que diff
no debería ser muy diferente de -
y abs
con dos números. Solicitar el rango debería optimizar obtener el mínimo y el máximo. Tanto la diff
como el range
son funciones primitivas. Pero el tiempo no miente.
Utilizo este método, que compara el mínimo y el máximo, después de dividir por el medio:
# 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 estuvieras usando esto más en serio, probablemente quieras eliminar los valores faltantes antes de calcular el rango y la media.
> isTRUE(all.equal( max(y) ,min(y)) )
[1] TRUE
> isTRUE(all.equal( max(x) ,min(x)) )
[1] FALSE
Otro en la misma línea:
> diff(range(x)) < .Machine$double.eps ^ 0.5
[1] FALSE
> diff(range(y)) < .Machine$double.eps ^ 0.5
[1] TRUE