varias superponer studio lineas graficos graficas r floating-point floating-accuracy r-faq

studio - superponer graficas en r



¿Por qué estos números no son iguales? (4)

El siguiente código es obviamente incorrecto. ¿Cuál es el problema?

i <- 0.1 i <- i + 0.05 i ## [1] 0.15 if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15") ## i does not equal 0.15


Razón general (idioma agnóstico)

Como no todos los números pueden representarse exactamente en la aritmética de punto flotante IEEE (el estándar que casi todas las computadoras usan para representar números decimales y hacer cálculos matemáticos con ellos), no siempre obtendrá lo que esperaba. Esto es especialmente cierto porque algunos valores que son decimales finitos simples (como 0.1 y 0.05) no se representan exactamente en la computadora y, por lo tanto, los resultados de la aritmética en ellos pueden no dar un resultado que sea idéntico a una representación directa de " respuesta conocida.

Esta es una limitación bien conocida de la aritmética computacional y se discute en varios lugares:

Comparando los escalares

La solución estándar para esto en R es no usar == , sino la función all.equal . O mejor dicho, ya que all.equal da muchos detalles sobre las diferencias, si existen, isTRUE(all.equal(...)) .

if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")

rendimientos

i equals 0.15

Algunos ejemplos más del uso de all.equal lugar de == (se supone que el último ejemplo muestra que esto mostrará correctamente las diferencias).

0.1+0.05==0.15 #[1] FALSE isTRUE(all.equal(0.1+0.05, 0.15)) #[1] TRUE 1-0.1-0.1-0.1==0.7 #[1] FALSE isTRUE(all.equal(1-0.1-0.1-0.1, 0.7)) #[1] TRUE 0.3/0.1 == 3 #[1] FALSE isTRUE(all.equal(0.3/0.1, 3)) #[1] TRUE 0.1+0.1==0.15 #[1] FALSE isTRUE(all.equal(0.1+0.1, 0.15)) #[1] FALSE

Algunos detalles más, directamente copiados de una respuesta a una pregunta similar :

El problema que ha encontrado es que el punto flotante no puede representar fracciones decimales exactamente en la mayoría de los casos, lo que significa que con frecuencia encontrará que las coincidencias exactas fallan.

mientras que R miente ligeramente cuando dices:

1.1-0.2 #[1] 0.9 0.9 #[1] 0.9

Puedes averiguar lo que realmente piensa en decimal:

sprintf("%.54f",1.1-0.2) #[1] "0.900000000000000133226762955018784850835800170898437500" sprintf("%.54f",0.9) #[1] "0.900000000000000022204460492503130808472633361816406250"

Puedes ver que estos números son diferentes, pero la representación es un poco difícil de manejar. Si los vemos en binario (bueno, hexadecimal, que es equivalente) obtenemos una imagen más clara:

sprintf("%a",0.9) #[1] "0x1.ccccccccccccdp-1" sprintf("%a",1.1-0.2) #[1] "0x1.ccccccccccccep-1" sprintf("%a",1.1-0.2-0.9) #[1] "0x1p-53"

Puede ver que difieren en 2^-53 , lo cual es importante porque este número es la diferencia representable más pequeña entre dos números cuyo valor es cercano a 1, tal como es.

Podemos averiguar para cualquier computadora determinada cuál es el número representable más pequeño buscando en el campo de la machine de R:

?.Machine #.... #double.eps the smallest positive floating-point number x #such that 1 + x != 1. It equals base^ulp.digits if either #base is 2 or rounding is 0; otherwise, it is #(base^ulp.digits) / 2. Normally 2.220446e-16. #.... .Machine$double.eps #[1] 2.220446e-16 sprintf("%a",.Machine$double.eps) #[1] "0x1p-52"

Puede utilizar este hecho para crear una función "casi igual a" que verifica que la diferencia esté cerca del número más pequeño representable en punto flotante. De hecho esto ya existe: all.equal .

?all.equal #.... #all.equal(x,y) is a utility to compare R objects x and y testing ‘near equality’. #.... #all.equal(target, current, # tolerance = .Machine$double.eps ^ 0.5, # scale = NULL, check.attributes = TRUE, ...) #....

Así que la función all.equal en realidad está comprobando que la diferencia entre los números es la raíz cuadrada de la diferencia más pequeña entre dos mantisas.

Este algoritmo se vuelve un poco raro cerca de números extremadamente pequeños llamados denormales, pero no debes preocuparte por eso.

Comparando vectores

La discusión anterior asumió una comparación de dos valores únicos. En R, no hay escalares, solo vectores y la vectorización implícita es una fortaleza del lenguaje. Para comparar el valor de los vectores de forma elemental, los principios anteriores se mantienen, pero la implementación es ligeramente diferente. == se vectoriza (hace una comparación de elementos) mientras que all.equal compara los vectores completos como una entidad única.

Utilizando los ejemplos anteriores.

a <- c(0.1+0.05, 1-0.1-0.1-0.1, 0.3/0.1, 0.1+0.1) b <- c(0.15, 0.7, 3, 0.15)

== no da el resultado "esperado" y all.equal no se realiza de forma elemental

a==b #[1] FALSE FALSE FALSE FALSE all.equal(a,b) #[1] "Mean relative difference: 0.01234568" isTRUE(all.equal(a,b)) #[1] FALSE

Más bien, se debe usar una versión que recorra los dos vectores

mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b) #[1] TRUE TRUE TRUE FALSE

Si se desea una versión funcional de esto, se puede escribir

elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})

que puede ser llamado como justo

elementwise.all.equal(a, b) #[1] TRUE TRUE TRUE FALSE

Alternativamente, en lugar de envolver all.equal en aún más llamadas a funciones, puede simplemente replicar las all.equal.numeric internas relevantes de all.equal.numeric y usar vectorización implícita:

tolerance = .Machine$double.eps^0.5 # this is the default tolerance used in all.equal, # but you can pick a different tolerance to match your needs abs(a - b) < tolerance #[1] TRUE TRUE TRUE FALSE


Agregando al comentario de Brian (que es la razón) puede superar esto utilizando all.equal en all.equal lugar:

# i <- 0.1 # i <- i + 0.05 # i #if(all.equal(i, .15)) cat("i equals 0.15/n") else cat("i does not equal 0.15/n") #i equals 0.15

La advertencia de Per Joshua aquí es el código actualizado (Gracias Joshua):

i <- 0.1 i <- i + 0.05 i if(isTRUE(all.equal(i, .15))) { #code was getting sloppy &went to multiple lines cat("i equals 0.15/n") } else { cat("i does not equal 0.15/n") } #i equals 0.15


Esto es hackish, pero rápido:

if(round(i, 10)==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")


dplyr::near() es una opción para probar si dos vectores de números de punto flotante son iguales. Este es el ejemplo de la docs :

sqrt(2) ^ 2 == 2 #> [1] FALSE library(dplyr) near(sqrt(2) ^ 2, 2) #> [1] TRUE

La función tiene un parámetro de tolerancia incorporado: tol = .Machine$double.eps^0.5 que se puede ajustar. El parámetro predeterminado es el mismo que el predeterminado para all.equal() .