Las expresiones con “== True” y “is True” dan resultados diferentes

Tengo el siguiente MCVE :

#!/usr/bin/env python3 import pandas as pd df = pd.DataFrame([True, False, True]) print("Whole DataFrame:") print(df) print("\nFiltered DataFrame:") print(df[df[0] == True]) 

La salida es la siguiente, que esperaba:

 Whole DataFrame: 0 0 True 1 False 2 True Filtered DataFrame: 0 0 True 2 True 

Está bien, pero el estilo PEP8 parece estar equivocado, dice: La comparación de E712 con True debería ser if cond is True o if cond . Así que lo cambié a is True lugar de == True pero ahora falla, la salida es:

 Whole DataFrame: 0 0 True 1 False 2 True Filtered DataFrame: 0 True 1 False 2 True Name: 0, dtype: bool 

Que esta pasando?

El problema aquí es que en df[df[0] == True] , no estás comparando objetos con True .

Como dicen las otras respuestas, == está sobrecargado en pandas para producir una Series lugar de un bool como normalmente lo hace. [] está sobrecargado para interpretar la Series y dar el resultado filtrado. El código es esencialmente equivalente a:

 series = df[0].__eq__(True) df.__getitem__(series) 

Entonces, no estás violando PEP8 dejando == aquí.


Esencialmente, los pandas dan una syntax familiar, una semántica inusual – eso es lo que causó la confusión.

Según Stroustroup (sec.3.3.3), la sobrecarga del operador ha estado causando problemas debido a esto desde su invención (y tuvo que pensar mucho para incluirlo en C ++). Al ver aún más abuso en C ++ , Gosling corrió al otro extremo en Java, lo prohibió por completo, y eso resultó ser exactamente eso, un extremo.

Como conclusión, los lenguajes y códigos modernos tienden a sobrecargar a los operadores, pero deben vigilarse de cerca para no usarlos en exceso y para que la semántica se mantenga constante.

En python, is comprueba si un objeto es igual a otro. == está definido por una pandas.Series para actuar de forma elemental, no lo es.

Debido a eso, df[0] is True compara si df[0] y True son el mismo objeto. El resultado es False , que a su vez es igual a 0 , así que obtienes las 0 columnas cuando haces df[df[0] is True]

Creo que en la comparación de pandas solo funciona con == y el resultado es una boolean Series . Con is salida es False . Más información sobre es .

 print df[0] == True 0 True 1 False 2 True Name: 0, dtype: bool print df[df[0]] 0 0 True 2 True print df[df[0] == True] 0 0 True 2 True print df[0] is True False print df[df[0] is True] 0 True 1 False 2 True Name: 0, dtype: bool 

Esta es una explicación de la respuesta de MaxNoe, ya que fue demasiado extensa para incluirla en los comentarios.

Como indicó, df[0] is True evalúa como False , que luego se fuerza a 0 que corresponde a un nombre de columna. Lo interesante de esto es que si corres

 >>>df = pd.DataFrame([True, False, True]) >>>df[False] KeyError Traceback (most recent call last)  in () ----> 1 df[False] >>>df[0] 0 True 1 False 2 True Name: 0, dtype: bool >>>df[False] 0 True 1 False 2 True Name: 0, dtype: bool 

Esto parece un poco desconcertante al principio (al menos para mí), pero tiene que ver con cómo los pandas utilizan el almacenamiento en caché. Si nos fijamos en cómo se resuelve df[False] , parece que

  /home/matthew/anaconda/lib/python2.7/site-packages/pandas/core/frame.py(1975)__getitem__() -> return self._getitem_column(key) /home/matthew/anaconda/lib/python2.7/site-packages/pandas/core/frame.py(1999)_getitem_column() -> return self._get_item_cache(key) > /home/matthew/anaconda/lib/python2.7/site-packages/pandas/core/generic.py(1343)_get_item_cache() -> res = cache.get(item) 

Dado que el cache es solo un dict Python regular, después de ejecutar df[0] el cache parece

 >>>cache {0: 0 True 1 False 2 True Name: 0, dtype: bool} 

de modo que cuando buscamos False , python lo obliga a 0 . Si aún no hemos cebado el caché usando df[0] , res es None que activa un KeyError en la línea 1345 de generic.py

 def _get_item_cache(self, item): 1341 """Return the cached item, item represents a label indexer.""" 1342 cache = self._item_cache 1343 -> res = cache.get(item) 1344 if res is None: 1345 values = self._data.get(item) 

Una solución alternativa para no tener quejas de los linters pero aún así una syntax razonable para la sub-configuración podría ser:

 s = pd.Series([True] * 10 + [False]) s.loc[s == True] # bad comparison in Python's eyes s.loc[s.isin([True])] # valid comparison, not as ugly as s.__eq__(True) 

Ambos también toman el mismo tiempo.

Además, para los marcos de datos se puede usar la query :

 df = pd.DataFrame([ [True] * 10 + [False], list(range(11))], index=['T', 'N']).T df.query("T == True") # also okay