tipos - ¿Cómo extender el comportamiento `==` a los vectores que incluyen NA?
tipos de comportamiento en los niños (4)
¿Qué hay de usar identical()
envuelto en mapply()
a <- c( 1 , 2 , 3 )
b <- c( 1 , 2 , 4 )
mapply(identical,a,b)
#[1] TRUE TRUE FALSE
a <- c( 1 , NA , 3 )
b <- c( 1 , NA , 4 )
mapply(identical,a,b)
#[1] TRUE TRUE FALSE
a <- c( 1 , NA , 3 )
b <- c( 1 , 2 , 4 )
mapply(identical,a,b)
#[1] TRUE FALSE FALSE
Además, si necesita comparar los resultados de los cálculos, podría deshacerse de identical()
e ir con isTRUE(all.equal())
como tal
mapply(FUN=function(x,y){isTRUE(all.equal(x,y))}, a, b)
lo que da los mismos resultados, pero puede tratar mejor los problemas de redondeo. Como
a<-.3/3
b<-.1
mapply(FUN=function(x,y){isTRUE(all.equal(x,y))}, a, b)
#[1] TRUE
mapply(identical,a,b)
#[1] FALSE
Creo que este último ejemplo all.equal
muchas de las soluciones propuestas, pero cambiar a all.equal
lugar de ==
probablemente funcionaría para todas ellas.
He fallado completamente en la búsqueda de otra discusión r-help o Stack Overflow sobre este problema específico. Lo siento si está en algún lugar obvio. Creo que estoy buscando la forma más fácil de obtener el signo == de R para no devolver nunca NA.
# Example #
# Say I have two vectors
a <- c( 1 , 2 , 3 )
b <- c( 1 , 2 , 4 )
# And want to test if each element in the first
# is identical to each element in the second:
a == b
# It does what I want perfectly:
# TRUE TRUE FALSE
# But if either vector contains a missing,
# the `==` operator returns an incorrect result:
a <- c( 1 , NA , 3 )
b <- c( 1 , NA , 4 )
# Here I''d want TRUE TRUE FALSE
a == b
# But I get TRUE NA FALSE
a <- c( 1 , NA , 3 )
b <- c( 1 , 2 , 4 )
# Here I''d want TRUE FALSE FALSE
a == b
# But I get TRUE NA FALSE again.
Obtengo el resultado que quiero con:
mapply( `%in%` , a , b )
Pero mapply
parece pesado.
¿Hay una solución más intuitiva para esto?
Otra opción, pero ¿es mejor que mapply(''%in%'', a , b)
?
(!is.na(a) & !is.na(b) & a==b) | (is.na(a) & is.na(b))
Siguiendo la sugerencia de @AnthonyDamico, creación del operador "mutt":
"%==%" <- function(a, b) (!is.na(a) & !is.na(b) & a==b) | (is.na(a) & is.na(b))
Edición: o, versión ligeramente diferente y más corta de @Frank (que también es más eficiente)
"%==%" <- function(a, b) (is.na(a) & is.na(b)) | (!is.na(eq <- a==b) & eq)
Con los diferentes ejemplos:
a <- c( 1 , 2 , 3 )
b <- c( 1 , 2 , 4 )
a %==% b
# [1] TRUE TRUE FALSE
a <- c( 1 , NA , 3 )
b <- c( 1 , NA , 4 )
a %==% b
# [1] TRUE TRUE FALSE
a <- c( 1 , NA , 3 )
b <- c( 1 , 2 , 4 )
a %==% b
#[1] TRUE FALSE FALSE
a <- c( 1 , NA , 3 )
b <- c( 3 , NA , 1 )
a %==% b
#[1] FALSE TRUE FALSE
Suponiendo que no tenemos un número relativo grande de NA
, la solución vectorizada propuesta desperdicia algunos recursos comparando valores que ya han sido resueltos por a==b
.
Por lo general, podemos asumir que las NAs
son pocas, por lo que vale la pena calcular a==b
primero y luego tratar las NAs
separado, a pesar de los pasos adicionales y las variables temporales:
`%==%` <- function(a,b){
x <- a==b
na_x <- which(is.na(x))
x[na_x] <- is.na(a[na_x]) & is.na(b[na_x])
x
}
Comprobar salida
a <- c( 1 , 2 , 3 )
b <- c( 1 , 2 , 4 )
a %==% b
# [1] TRUE TRUE FALSE
a <- c( 1 , NA , 3 )
b <- c( 1 , NA , 4 )
a %==% b
# [1] TRUE TRUE FALSE
a <- c( 1 , NA , 3 )
b <- c( 1 , 2 , 4 )
a %==% b
# [1] TRUE FALSE FALSE
Puntos de referencia
Estoy reproduciendo a continuación el punto de referencia de @ akrun con las soluciones más rápidas y n = 100.
set.seed(24)
a <- sample(c(1:10, NA), 1e6, replace=TRUE)
b <- sample(c(1:20, NA), 1e6, replace=TRUE)
mm <- function(){
x <- a==b
na_x <- which(is.na(x))
x[na_x] <- is.na(a[na_x]) & is.na(b[na_x])
x
}
akrun1 <- function() {replace(a, is.na(a), Inf)==replace(b, is.na(b), Inf)}
cathG <- function() {(!is.na(a) & !is.na(b) & a==b) | (is.na(a) & is.na(b))}
docend <- function() {replace(a, which(is.na(a)), Inf)==replace(b, which(is.na(b)), Inf)}
library(microbenchmark)
microbenchmark(mm(),akrun1(),cathG(),docend(),
unit=''relative'', times=100L)
# Unit: relative
# expr min lq mean median uq max neval
# mm() 1.000000 1.000000 1.000000 1.000000 1.000000 1.0000000 100
# akrun1() 1.667242 1.884185 1.815392 1.642581 1.765238 0.9973017 100
# cathG() 2.447168 2.449597 2.118306 2.201346 2.358105 1.1421577 100
# docend() 1.683817 1.950970 1.756481 1.745400 2.007889 1.2264461 100
Extendiendo ==
Como la pregunta original es realmente encontrar:
la forma más fácil de obtener el signo
==
R
para no devolver nuncaNAs
Aquí hay una manera, donde definimos una nueva clase na_comparable
. Solo uno de los vectores debe ser de esta clase ya que el otro será obligado a ello.
na_comparable <- setClass("na_comparable", contains = "numeric")
`==.na_comparable` <- function(a,b){
x <- unclass(a) == unclass(b) # inefficient but I don''t know how to force the default `==`
na_x <- which(is.na(x))
x[na_x] <- is.na(a[na_x]) & is.na(b[na_x])
x
}
`!=.na_comparable` <- Negate(`==.na_comparable`)
a <- na_comparable(a)
a == b
# [1] TRUE TRUE FALSE
b == a
# [1] TRUE TRUE FALSE
a != b
# [1] FALSE FALSE TRUE
b != a
# [1] FALSE FALSE TRUE
En una cadena dplyr se podría usar convenientemente de esta manera:
data.frame(a=c(1,NA,3),b=c(1,NA,4)) %>%
mutate(a = na_comparable(a),
c = a==b,
d= a!=b)
# a b c d
# 1 1 1 TRUE FALSE
# 2 NA NA TRUE FALSE
# 3 3 4 FALSE TRUE
Con este enfoque, en caso de que necesite actualizar el código para tener en cuenta las NAs
que estuvieron ausentes antes, puede configurarlo con una sola llamada na_comparable
lugar de transformar sus datos iniciales o reemplazar todos sus ==
con %==%
en la línea.
Tu podrías intentar
replace(a, is.na(a), Inf)==replace(b, is.na(b), Inf)
O una variación más rápida sugerida por @docendo discimus
replace(a, which(is.na(a)), Inf)==replace(b, which(is.na(b)), Inf)
Basado en los diferentes escenarios.
1.
a <- c( 1 , 2 , 3 )
b <- c( 1 , 2 , 4 )
akrun1()
#[1] TRUE TRUE FALSE
2.
a <- c( 1 , NA , 3 )
b <- c( 1 , NA , 4 )
akrun1()
#[1] TRUE TRUE FALSE
3.
a <- c( 1 , NA , 3 )
b <- c( 1 , 2 , 4 )
akrun1()
#[1] TRUE FALSE FALSE
Puntos de referencia
set.seed(24)
a <- sample(c(1:10, NA), 1e6, replace=TRUE)
b <- sample(c(1:20, NA), 1e6, replace=TRUE)
akrun1 <- function() {replace(a, is.na(a), Inf)==replace(b, is.na(b), Inf)}
cathG <- function() {(!is.na(a) & !is.na(b) & a==b) | (is.na(a) & is.na(b))}
anthony <- function() {mapply(`%in%`, a, b)}
webb <- function() {ifelse(is.na(a),is.na(b),a==b)}
docend <- function() {replace(a, which(is.na(a)), Inf)==replace(b,
which(is.na(b)), Inf)}
library(microbenchmark)
microbenchmark(akrun1(), cathG(), anthony(), webb(),docend(),
unit=''relative'', times=20L)
#Unit: relative
# expr min lq mean median uq max
# akrun1() 3.050200 3.035625 3.007196 2.963916 2.977490 3.083658
# cathG() 4.829972 4.893266 4.843585 4.790466 4.816472 4.939316
# anthony() 190.499027 224.389971 215.792965 217.647702 215.503308 212.356051
# webb() 14.000363 14.366572 15.412527 14.095947 14.671741 19.735746
# docend() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000
# neval cld
# 20 a
# 20 a
# 20 c
# 20 b
# 20 a