python - en operador, flotador("NaN") y np.nan
numpy containers (2)
Solía creer que in
operador en Python se comprueba la presencia del elemento en alguna colección que usa la verificación de igualdad ==
, por lo que el element in some_list
es aproximadamente equivalente a any(x == element for x in some_list)
. Por ejemplo:
True in [1, 2, 3]
# True because True == 1
o
1 in [1., 2., 3.]
# also True because 1 == 1.
Sin embargo, es bien sabido que NaN
no es igual a sí mismo. Así que esperaba que float("NaN") in [float("NaN")]
sea False
. Y es False
cierto.
Sin embargo, si usamos numpy.nan
lugar de float("NaN")
, la situación es bastante diferente:
import numpy as np
np.nan in [np.nan, 1, 2]
# True
Pero np.nan == np.nan
todavía da False
!
¿Como es posible? ¿Cuál es la diferencia entre np.nan
y float("NaN")
? ¿Cómo hacer frente a np.nan
?
Para verificar si el elemento está en la lista, Python prueba primero la identidad del objeto, y luego prueba la igualdad solo si los objetos son diferentes. 1
float("NaN") in [float("NaN")]
es False porque hay dos objetos NaN
diferentes involucrados en la comparación. Por lo tanto, la prueba de identidad devuelve Falso, y luego la prueba de igualdad también devuelve Falso desde NaN != NaN
.
np.nan in [np.nan, 1, 2]
sin embargo, es verdadero porque el mismo objeto NaN
está involucrado en la comparación. La prueba de identidad de objeto devuelve Verdadero, por lo que Python reconoce inmediatamente que el elemento está en la lista.
El método __contains__
(invocado usando in
) para muchos de los otros tipos de Contenedores incorporados de Python, como tuplas y conjuntos, se implementa usando la misma verificación.
1 Al menos esto es cierto en CPython. La identidad del objeto aquí significa que los objetos se encuentran en la misma dirección de memoria: el método que contiene las listas se realiza mediante PyObject_RichCompareBool
que compara rápidamente los punteros del objeto antes que una comparación de objetos potencialmente más complicada. Otras implementaciones de Python pueden diferir.
Una cosa que vale la pena mencionar es que las matrices numpy se comportan como se espera:
a = np.array((np.nan,))
a[0] in a
# False
Variaciones del tema:
[np.nan]==[np.nan]
# True
[float(''nan'')]==[float(''nan'')]
# False
{np.nan: 0}[np.nan]
# 0
{float(''nan''): 0}[float(''nan'')]
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# KeyError: nan
Todo lo demás está cubierto en la excelente respuesta de @AlexRiley.