FutureWarning: error en la comparación de elementos; devolviendo escalar, pero en el futuro realizará una comparación de elementos

99

Estoy usando Pandas 0.19.1en Python 3. Recibo una advertencia sobre estas líneas de código. Estoy tratando de obtener una lista que contenga todos los números de fila donde la cadena Peterestá presente en la columna Unnamed: 5.

df = pd.read_excel(xls_path)
myRows = df[df['Unnamed: 5'] == 'Peter'].index.tolist()

Produce una Advertencia:

"\Python36\lib\site-packages\pandas\core\ops.py:792: FutureWarning: elementwise 
comparison failed; returning scalar, but in the future will perform 
elementwise comparison 
result = getattr(x, name)(y)"

¿Qué es este FutureWarning y debería ignorarlo ya que parece funcionar?

Eric Leschinski
fuente

Respuestas:

158

Este FutureWarning no es de Pandas, es de numpy y el error también afecta a matplotlib y a otros, aquí se explica cómo reproducir la advertencia más cerca de la fuente del problema:

import numpy as np
print(np.__version__)   # Numpy version '1.12.0'
'x' in np.arange(5)       #Future warning thrown here

FutureWarning: elementwise comparison failed; returning scalar instead, but in the 
future will perform elementwise comparison
False

Otra forma de reproducir este error usando el operador doble igual:

import numpy as np
np.arange(5) == np.arange(5).astype(str)    #FutureWarning thrown here

Un ejemplo de Matplotlib afectado por este FutureWarning bajo su implementación de diagrama de carcaj: https://matplotlib.org/examples/pylab_examples/quiver_demo.html

¿Que está pasando aqui?

Existe un desacuerdo entre Numpy y el pitón nativo sobre lo que debería suceder cuando se compara una cadena con los tipos numéricos de numpy. Observe que el operando de la izquierda es el territorio de Python, una cadena primitiva, y la operación del medio es el terreno de Python, pero el operando de la derecha es el terreno de Numpy. ¿Debería devolver un escalar de estilo Python o un ndarray de estilo Numpy de booleano? Numpy dice que ndarray de bool, los desarrolladores de Pythonic no están de acuerdo. Enfrentamiento clásico.

¿Debería ser una comparación por elementos o escalar si el elemento existe en la matriz?

Si su código o biblioteca usa los operadores ino ==para comparar la cadena de python con numerosos ndarrays, no son compatibles, por lo que cuando lo intenta, devuelve un escalar, pero solo por ahora. La Advertencia indica que en el futuro este comportamiento podría cambiar, por lo que su código vomita por toda la alfombra si python / numpy decide adoptar el estilo Numpy.

Informes de errores enviados:

Numpy y Python están en un punto muerto, por ahora la operación devuelve un escalar, pero en el futuro puede cambiar.

https://github.com/numpy/numpy/issues/6784

https://github.com/pandas-dev/pandas/issues/7830

Dos soluciones alternativas:

Bloquee su versión de python y numpy, ignore las advertencias y espere que el comportamiento no cambie, o convierta los operandos izquierdo y derecho de ==y insean de un tipo numpy o un tipo numérico python primitivo.

Suprima la advertencia a nivel mundial:

import warnings
import numpy as np
warnings.simplefilter(action='ignore', category=FutureWarning)
print('x' in np.arange(5))   #returns False, without Warning

Suprima la advertencia línea por línea.

import warnings
import numpy as np

with warnings.catch_warnings():
    warnings.simplefilter(action='ignore', category=FutureWarning)
    print('x' in np.arange(2))   #returns False, warning is suppressed

print('x' in np.arange(10))   #returns False, Throws FutureWarning

Simplemente suprima la advertencia por su nombre, luego coloque un comentario fuerte junto a ella mencionando la versión actual de python y numpy, diciendo que este código es frágil y requiere estas versiones y coloque un enlace aquí. Patea la lata por el camino.

TLDR: pandas son Jedi; numpyson las chozas; y pythones el imperio galáctico. https://youtu.be/OZczsiCfQQk?t=3

Eric Leschinski
fuente
1
Ugh. Entonces, si tengo alguna cantidad thing(que puede o no ser un tipo numpy; no lo sé) y quiero ver si thing == 'some string'y obtener un boolresultado simple , ¿qué debo hacer? np.atleast_1d(thing)[0] == 'some string'? Pero eso no es robusto para que algún bromista ponga 'some string'el primer elemento de una matriz. Supongo que tengo que probar el tipo de thingprimero y luego solo hacer la ==prueba si es una cadena (o no un objeto numpy).
EL_DON
1
En realidad, esta advertencia futura también se genera cada vez que intenta comparar un numpy.ndarray con una lista vacía. Por ejemplo, la ejecución también np.array([1, 2]) == []generará la advertencia.
1313e
2
Me resultaría útil ver un ejemplo de cómo hacer esto:or babysit your left and right operands to be from a common turf
HaPsantran
10
Este es un nivel asombroso de información de calidad sobre este tema.
StephenBoesch
Entonces, me desharía de la advertencia en este código: df.loc [df.cName == '', 'cName'] = '10004'. En otras palabras, ¿cuál es el equivalente pandas / numpy a python? '' (Cadena vacía)
Garet Jax
12

Recibo el mismo error cuando intento configurar la index_collectura de un archivo en el Pandamarco de datos de a:

df = pd.read_csv('my_file.tsv', sep='\t', header=0, index_col=['0'])  ## or same with the following
df = pd.read_csv('my_file.tsv', sep='\t', header=0, index_col=[0])

Nunca me había encontrado con un error de este tipo anteriormente. Todavía estoy tratando de averiguar la razón detrás de esto (usando la explicación de @Eric Leschinski y otros).

De todos modos, el siguiente enfoque resuelve el problema por ahora hasta que averigüe la razón:

df = pd.read_csv('my_file.tsv', sep='\t', header=0)  ## not setting the index_col
df.set_index(['0'], inplace=True)

Actualizaré esto tan pronto como descubra el motivo de tal comportamiento.

Dataman
fuente
Tengo el mismo problema con read_csv(). Me parece que es algo que pandashay que arreglar.
Konstantin
1
¡Gracias! Me ahorró mucho trabajo, supongo. pd__version__: 0.22.0; np.__version__: 1.15.4
Markus Dutschke
1
El mismo problema aquí, aparentemente algunas llamadas internas read_csvcuando se usa el index_colparámetro. He probado dos configuraciones con resultados diferentes: 1. numpy versión 1.19.2, Pandas versión 1.1.2: FutureWarning: la comparación de elementos falló ... 2. numpy versión 1.19.2, Pandas versión 1.1.3: TypeError: ufunc ' isnan 'no es compatible ...
Carlos
9

Mi experiencia con el mismo mensaje de advertencia fue causada por TypeError.

TypeError: comparación de tipos no válidos

Por lo tanto, es posible que desee verificar el tipo de datos del Unnamed: 5

for x in df['Unnamed: 5']:
  print(type(x))  # are they 'str' ?

Así es como puedo replicar el mensaje de advertencia:

import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(3, 2), columns=['num1', 'num2'])
df['num3'] = 3
df.loc[df['num3'] == '3', 'num3'] = 4  # TypeError and the Warning
df.loc[df['num3'] == 3, 'num3'] = 4  # No Error

Espero eso ayude.

yhd.leung
fuente
1
Su código tiene muchas partes móviles innecesarias para ilustrar la advertencia. Pandas lo agracia con ese TypeError adicional, pero eso es control de daños de Pandas. La advertencia de origen es un desacuerdo entre Numpy y Python y ocurre en la evaluación de df['num3'] == '3'.
Eric Leschinski
1
df.loc[df['num3'] == 3, 'num3'] = 4 # No ErrorEsta parte me ayuda. Gracias
jameslem
5

No se puede superar la respuesta asombrosamente detallada de Eric Leschinski, pero aquí hay una solución rápida a la pregunta original que no creo que se haya mencionado todavía: coloque la cadena en una lista y use en .isinlugar de==

Por ejemplo:

import pandas as pd
import numpy as np

df = pd.DataFrame({"Name": ["Peter", "Joe"], "Number": [1, 2]})

# Raises warning using == to compare different types:
df.loc[df["Number"] == "2", "Number"]

# No warning using .isin:
df.loc[df["Number"].isin(["2"]), "Number"]
Toby Petty
fuente
Me pregunto si podría hacer lo mismo con esta sintaxis -> si "-" en dfN ['Drate']. unique ()
lone_coder
3

Una solución rápida para esto es usar numpy.core.defchararray. También enfrenté el mismo mensaje de advertencia y pude resolverlo usando el módulo anterior.

import numpy.core.defchararray as npd
resultdataset = npd.equal(dataset1, dataset2)
Jeet23
fuente
2

La respuesta de Eric explica de manera útil que el problema proviene de comparar una serie Pandas (que contiene una matriz NumPy) con una cadena de Python. Desafortunadamente, sus dos soluciones alternativas simplemente eliminan la advertencia.

Para escribir código que no cause la advertencia en primer lugar, compare explícitamente su cadena con cada elemento de la Serie y obtenga un bool separado para cada uno. Por ejemplo, podría utilizar mapy una función anónima.

myRows = df[df['Unnamed: 5'].map( lambda x: x == 'Peter' )].index.tolist()
Nathan
fuente
1

Si sus matrices no son demasiado grandes o no tiene demasiadas, es posible que pueda salirse con la suya forzando el lado izquierdo de ==a ser una cadena:

myRows = df[str(df['Unnamed: 5']) == 'Peter'].index.tolist()

Pero esto es ~ 1.5 veces más lento si df['Unnamed: 5']es una cadena, 25-30 veces más lento si df['Unnamed: 5']es una pequeña matriz numpy (longitud = 10) y 150-160 veces más lenta si es una matriz numpy con longitud 100 (tiempos promediados en 500 intentos) .

a = linspace(0, 5, 10)
b = linspace(0, 50, 100)
n = 500
string1 = 'Peter'
string2 = 'blargh'
times_a = zeros(n)
times_str_a = zeros(n)
times_s = zeros(n)
times_str_s = zeros(n)
times_b = zeros(n)
times_str_b = zeros(n)
for i in range(n):
    t0 = time.time()
    tmp1 = a == string1
    t1 = time.time()
    tmp2 = str(a) == string1
    t2 = time.time()
    tmp3 = string2 == string1
    t3 = time.time()
    tmp4 = str(string2) == string1
    t4 = time.time()
    tmp5 = b == string1
    t5 = time.time()
    tmp6 = str(b) == string1
    t6 = time.time()
    times_a[i] = t1 - t0
    times_str_a[i] = t2 - t1
    times_s[i] = t3 - t2
    times_str_s[i] = t4 - t3
    times_b[i] = t5 - t4
    times_str_b[i] = t6 - t5
print('Small array:')
print('Time to compare without str conversion: {} s. With str conversion: {} s'.format(mean(times_a), mean(times_str_a)))
print('Ratio of time with/without string conversion: {}'.format(mean(times_str_a)/mean(times_a)))

print('\nBig array')
print('Time to compare without str conversion: {} s. With str conversion: {} s'.format(mean(times_b), mean(times_str_b)))
print(mean(times_str_b)/mean(times_b))

print('\nString')
print('Time to compare without str conversion: {} s. With str conversion: {} s'.format(mean(times_s), mean(times_str_s)))
print('Ratio of time with/without string conversion: {}'.format(mean(times_str_s)/mean(times_s)))

Resultado:

Small array:
Time to compare without str conversion: 6.58464431763e-06 s. With str conversion: 0.000173756599426 s
Ratio of time with/without string conversion: 26.3881526541

Big array
Time to compare without str conversion: 5.44309616089e-06 s. With str conversion: 0.000870866775513 s
159.99474375821288

String
Time to compare without str conversion: 5.89370727539e-07 s. With str conversion: 8.30173492432e-07 s
Ratio of time with/without string conversion: 1.40857605178
EL_DON
fuente
1
Prefijar la izquierda de ==con strfue una buena solución para mí que apenas afectó el rendimiento en 1,5 millones de filas que no serán más grandes que eso en el futuro.
David Erickson
0

Recibí esta advertencia porque pensé que mi columna contenía cadenas nulas, pero al verificar, ¡contenía np.nan!

if df['column'] == '':

Cambiar mi columna a cadenas vacías ayudó :)

intotecho
fuente
0

He comparado algunos de los métodos posibles para hacer esto, incluidos los pandas, varios métodos numpy y un método de comprensión de listas.

Primero, comencemos con una línea de base:

>>> import numpy as np
>>> import operator
>>> import pandas as pd

>>> x = [1, 2, 1, 2]
>>> %time count = np.sum(np.equal(1, x))
>>> print("Count {} using numpy equal with ints".format(count))
CPU times: user 52 µs, sys: 0 ns, total: 52 µs
Wall time: 56 µs
Count 2 using numpy equal with ints

Por lo tanto, nuestra línea de base es que el recuento debe ser correcto 2y debemos tomar aproximadamente50 us .

Ahora, probamos el método ingenuo:

>>> x = ['s', 'b', 's', 'b']
>>> %time count = np.sum(np.equal('s', x))
>>> print("Count {} using numpy equal".format(count))
CPU times: user 145 µs, sys: 24 µs, total: 169 µs
Wall time: 158 µs
Count NotImplemented using numpy equal
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/ipykernel_launcher.py:1: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
  """Entry point for launching an IPython kernel.

Y aquí, obtenemos la respuesta incorrecta ( NotImplemented != 2), nos lleva mucho tiempo y arroja la advertencia.

Entonces probaremos otro método ingenuo:

>>> %time count = np.sum(x == 's')
>>> print("Count {} using ==".format(count))
CPU times: user 46 µs, sys: 1 µs, total: 47 µs
Wall time: 50.1 µs
Count 0 using ==

Nuevamente, la respuesta incorrecta ( 0 != 2). Esto es aún más insidioso porque no hay advertencias posteriores ( 0se pueden transmitir de la misma manera 2).

Ahora, intentemos una lista de comprensión:

>>> %time count = np.sum([operator.eq(_x, 's') for _x in x])
>>> print("Count {} using list comprehension".format(count))
CPU times: user 55 µs, sys: 1 µs, total: 56 µs
Wall time: 60.3 µs
Count 2 using list comprehension

Aquí obtenemos la respuesta correcta, ¡y es bastante rápido!

Otra posibilidad pandas,:

>>> y = pd.Series(x)
>>> %time count = np.sum(y == 's')
>>> print("Count {} using pandas ==".format(count))
CPU times: user 453 µs, sys: 31 µs, total: 484 µs
Wall time: 463 µs
Count 2 using pandas ==

¡Lento, pero correcto!

Y finalmente, la opción que voy a usar: convertir la numpymatriz al objecttipo:

>>> x = np.array(['s', 'b', 's', 'b']).astype(object)
>>> %time count = np.sum(np.equal('s', x))
>>> print("Count {} using numpy equal".format(count))
CPU times: user 50 µs, sys: 1 µs, total: 51 µs
Wall time: 55.1 µs
Count 2 using numpy equal

¡Rápido y correcto!

ahagen
fuente
Entonces, IIUC, para solucionarlo 'x' in np.arange(5), sugiere simplemente hacer 'x' in np.arange(5).astype(object)(o de manera similar :) 'x' == np.arange(5).astype(object). ¿Derecho? En mi humilde opinión, esta es la solución más elegante que se muestra aquí, así que estoy confundido por la falta de votos a favor. ¿Quizás editar su respuesta para comenzar con el resultado final y luego pasar al análisis de rendimiento agradable?
Oren Milman
Gracias @Oren, intentaré eso y veré a dónde me lleva.
ahagen
0

Tenía este código que estaba causando el error:

for t in dfObj['time']:
  if type(t) == str:
    the_date = dateutil.parser.parse(t)
    loc_dt_int = int(the_date.timestamp())
    dfObj.loc[t == dfObj.time, 'time'] = loc_dt_int

Lo cambié a esto:

for t in dfObj['time']:
  try:
    the_date = dateutil.parser.parse(t)
    loc_dt_int = int(the_date.timestamp())
    dfObj.loc[t == dfObj.time, 'time'] = loc_dt_int
  except Exception as e:
    print(e)
    continue

para evitar la comparación, que arroja la advertencia, como se indicó anteriormente. Solo tuve que evitar la excepción debido a que dfObj.locen el bucle for, tal vez haya una manera de decirle que no verifique las filas que ya ha cambiado.

ewizard
fuente
0

En mi caso, la advertencia se produjo solo por el tipo regular de indexación booleana, porque la serie solo tenía np.nan. Demostración (pandas 1.0.3):

>>> import pandas as pd
>>> import numpy as np
>>> pd.Series([np.nan, 'Hi']) == 'Hi'
0    False
1     True
>>> pd.Series([np.nan, np.nan]) == 'Hi'
~/anaconda3/envs/ms3/lib/python3.7/site-packages/pandas/core/ops/array_ops.py:255: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
  res_values = method(rvalues)
0    False
1    False

Creo que con pandas 1.0 realmente quieren que uses el nuevo 'string'tipo de datos que permite pd.NAvalores:

>>> pd.Series([pd.NA, pd.NA]) == 'Hi'
0    False
1    False
>>> pd.Series([np.nan, np.nan], dtype='string') == 'Hi'
0    <NA>
1    <NA>
>>> (pd.Series([np.nan, np.nan], dtype='string') == 'Hi').fillna(False)
0    False
1    False

No me encanta el momento en que jugaron con la funcionalidad diaria, como la indexación booleana.

Jeyes Unterwegs
fuente