FutureWarning: elementweiser Vergleich fehlgeschlagen; Rückgabe des Skalars, aber in Zukunft wird ein elementweiser Vergleich durchgeführt

99

Ich verwende Pandas 0.19.1unter Python 3. Ich erhalte eine Warnung in diesen Codezeilen. Ich versuche, eine Liste zu erhalten, die alle Zeilennummern enthält, in denen die Zeichenfolge Peterin der Spalte vorhanden ist Unnamed: 5.

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

Es wird eine Warnung ausgegeben:

"\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)"

Was ist das FutureWarning und sollte ich es ignorieren, da es zu funktionieren scheint.

Eric Leschinski
quelle

Antworten:

157

Diese FutureWarning stammt nicht von Pandas, sondern von Numpy. Der Fehler betrifft auch Matplotlib und andere. So reproduzieren Sie die Warnung näher an der Ursache des Problems:

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

Eine andere Möglichkeit, diesen Fehler mit dem Operator double equals zu reproduzieren:

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

Ein Beispiel für Matplotlib, das von dieser FutureWarning im Rahmen der Implementierung des Köcherplots betroffen ist: https://matplotlib.org/examples/pylab_examples/quiver_demo.html

Was ist hier los?

Es gibt eine Meinungsverschiedenheit zwischen Numpy und nativem Python darüber, was passieren soll, wenn Sie eine Zeichenfolge mit den numerischen Typen von numpy vergleichen. Beachten Sie, dass der linke Operand Pythons Rasen ist, eine primitive Zeichenfolge, und die mittlere Operation ist Pythons Rasen, aber der rechte Operand ist Numpys Rasen. Sollten Sie einen Skalar im Python-Stil oder einen Ndarray im Bumpean-Stil im Numpy-Stil zurückgeben? Numpy sagt ndarray von bool, Pythonic-Entwickler sind anderer Meinung. Klassischer Abstandshalter.

Sollte es ein elementweiser Vergleich oder ein Skalar sein, wenn ein Element im Array vorhanden ist?

Wenn Ihr Code oder Ihre Bibliothek die Operatoren inoder verwendet ==, um Python-Zeichenfolgen mit numpy ndarrays zu vergleichen, sind sie nicht kompatibel. Wenn Sie es also versuchen, wird ein Skalar zurückgegeben, jedoch nur für den Moment. Die Warnung weist darauf hin, dass sich dieses Verhalten in Zukunft möglicherweise ändern wird, sodass Ihr Code über den gesamten Teppich kotzt, wenn Python / Numpy sich für den Numpy-Stil entscheidet.

Eingereichte Fehlerberichte:

Numpy und Python befinden sich in einer Pattsituation. Derzeit gibt die Operation einen Skalar zurück, der sich jedoch in Zukunft möglicherweise ändern wird.

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

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

Zwei Problemumgehungslösungen:

Entweder Lockdown Ihre Version von Python und numpy, die Warnungen ignorieren und das Verhalten nicht ändern erwarten, oder konvertieren beide linken und rechten Operanden ==und invon einem numpy Typ oder primitive Python numerischen Typ zu sein.

Unterdrücken Sie die Warnung global:

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

Unterdrücken Sie die Warnung zeilenweise.

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

Unterdrücken Sie einfach die Warnung mit Namen und setzen Sie einen lauten Kommentar daneben, in dem die aktuelle Version von Python und Numpy erwähnt wird. Dieser Code ist spröde und erfordert diese Versionen. Fügen Sie hier einen Link hinzu. Treten Sie die Dose die Straße hinunter.

TLDR: pandas sind Jedi; numpysind die Hütten; und pythonist das galaktische Reich. https://youtu.be/OZczsiCfQQk?t=3

Eric Leschinski
quelle
1
Pfui. Was soll ich also tun, wenn ich eine bestimmte Menge habe thing(die ein numpy-Typ sein kann oder nicht; ich weiß es nicht) und sehen möchte, ob thing == 'some string'ich ein einfaches boolErgebnis erhalte ? np.atleast_1d(thing)[0] == 'some string'? Aber das ist nicht robust für einen Joker, der 'some string'das erste Element eines Arrays einfügt. Ich denke, ich muss zuerst den Typ testen thingund dann nur dann ==testen, wenn es sich um eine Zeichenfolge handelt (oder nicht um ein numpy-Objekt).
EL_DON
1
Tatsächlich wird diese zukünftige Warnung auch ausgelöst, wenn Sie versuchen, eine numpy.ndarray mit einer leeren Liste zu vergleichen. Wenn Sie beispielsweise ausführen, np.array([1, 2]) == []wird auch die Warnung ausgelöst.
1313e
2
Ich würde es hilfreich finden, ein Beispiel dafür zu sehen:or babysit your left and right operands to be from a common turf
HaPsantran
10
Dies ist ein erstaunliches Maß an Qualitätsinformationen zu diesem Thema.
StephenBoesch
Also würde ich die Warnung auf diesem Code loswerden: df.loc [df.cName == '', 'cName'] = '10004'. Mit anderen Worten, was ist das Pandas / Numpy-Äquivalent zu Pythons '' (leere Zeichenfolge)
Garet Jax
12

Ich erhalte den gleichen Fehler, wenn ich versuche, das index_colLesen einer Datei in den Datenrahmen eines zu setzen Panda:

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])

Ich bin noch nie auf einen solchen Fehler gestoßen. Ich versuche immer noch, den Grund dafür herauszufinden (unter Verwendung der Erklärung von @Eric Leschinski und anderer).

Wie auch immer, der folgende Ansatz löst das Problem vorerst, bis ich den Grund herausgefunden habe:

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

Ich werde dies aktualisieren, sobald ich den Grund für ein solches Verhalten herausgefunden habe.

Dataman
quelle
Ich habe das gleiche Problem mit read_csv(). Sieht für mich nach etwas aus, pandasdas repariert werden muss.
Konstantin
1
Vielen Dank! Hat mir viel Arbeit gespart - denke ich. pd__version__: 0.22.0;; np.__version__: 1.15.4
Markus Dutschke
1
Gleiches Problem hier, anscheinend ein numpy Aufruf im Inneren, read_csvwenn index_colParameter verwendet werden. Ich habe zwei Setups mit unterschiedlichen Ergebnissen getestet: 1. numpy Version 1.19.2, Pandas Version 1.1.2: FutureWarning: Elementweiser Vergleich fehlgeschlagen ... 2. numpy Version 1.19.2, Pandas Version 1.1.3: TypeError: ufunc ' isnan 'nicht unterstützt ...
Carlos
9

Meine Erfahrung mit derselben Warnmeldung wurde von TypeError verursacht.

TypeError: Ungültiger Typvergleich

Vielleicht möchten Sie den Datentyp des überprüfen Unnamed: 5

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

So kann ich die Warnmeldung replizieren:

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

Ich hoffe es hilft.

yhd.leung
quelle
1
Ihr Code enthält viele unnötige bewegliche Teile, um die Warnung zu veranschaulichen. Pandas ziert Sie mit diesem zusätzlichen TypeError, aber das ist Schadensbegrenzung durch Pandas. Die Quellwarnung ist eine Meinungsverschiedenheit zwischen Numpy und Python und tritt bei der Auswertung von auf df['num3'] == '3'.
Eric Leschinski
1
df.loc[df['num3'] == 3, 'num3'] = 4 # No ErrorDieser Teil hilft mir. Danke
Jameslem
5

Die unglaublich detaillierte Antwort von Eric Leschinski ist nicht zu übertreffen, aber hier ist eine kurze Lösung für die ursprüngliche Frage, von der ich glaube, dass sie noch nicht erwähnt wurde - setzen Sie die Zeichenfolge in eine Liste und verwenden Sie sie .isinstattdessen==

Zum Beispiel:

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
quelle
Ich frage mich, ob ich das gleiche mit dieser Syntax machen könnte -> wenn "-" in dfN ['Drate']. unique ()
lone_coder
3

Eine schnelle Problemumgehung hierfür ist die Verwendung numpy.core.defchararray. Ich hatte auch die gleiche Warnmeldung und konnte sie mit dem obigen Modul beheben.

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

Erics Antwort erklärt hilfreich, dass das Problem darin besteht, eine Pandas-Serie (die ein NumPy-Array enthält) mit einer Python-Zeichenfolge zu vergleichen. Leider unterdrücken seine beiden Problemumgehungen nur die Warnung.

Um Code zu schreiben, der die Warnung überhaupt nicht verursacht, vergleichen Sie Ihre Zeichenfolge explizit mit jedem Element der Serie und erhalten Sie für jedes Element einen eigenen Bool. Zum Beispiel könnten Sie mapeine anonyme Funktion verwenden.

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

Wenn Ihre Arrays nicht zu groß sind oder Sie nicht zu viele davon haben, können Sie möglicherweise davonkommen, indem Sie die linke Seite ==dazu zwingen , eine Zeichenfolge zu sein:

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

Dies ist jedoch ~ 1,5-mal langsamer, wenn df['Unnamed: 5']es sich um eine Zeichenfolge handelt, 25-30-mal langsamer, wenn df['Unnamed: 5']es sich um ein kleines Numpy-Array handelt (Länge = 10), und 150-160-mal langsamer, wenn es sich um ein Numpy-Array mit einer Länge von 100 handelt (Zeiten gemittelt über 500 Versuche). .

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)))

Ergebnis:

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
quelle
1
Das Präfix links von ==mit strwar für mich eine gute Lösung, die die Leistung in 1,5 Millionen Zeilen kaum beeinträchtigte, die in Zukunft nicht größer werden.
David Erickson
0

Ich habe diese Warnung erhalten, weil ich dachte, meine Spalte enthält Nullzeichenfolgen, aber beim Überprüfen enthielt sie np.nan!

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

Das Ändern meiner Spalte in leere Zeichenfolgen hat geholfen :)

intecho
quelle
0

Ich habe einige der dafür möglichen Methoden verglichen, darunter Pandas, mehrere Numpy-Methoden und eine Listenverständnismethode.

Beginnen wir zunächst mit einer Grundlinie:

>>> 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

Unsere Grundlinie ist also, dass die Zählung korrekt sein 2sollte und wir ungefähr nehmen sollten 50 us.

Nun versuchen wir die naive Methode:

>>> 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.

Und hier bekommen wir die falsche Antwort ( NotImplemented != 2), es dauert lange und es wird die Warnung ausgegeben.

Also werden wir eine andere naive Methode ausprobieren:

>>> %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 ==

Wieder die falsche Antwort ( 0 != 2). Dies ist umso heimtückischer, als es keine nachfolgenden Warnungen gibt ( 0kann genauso weitergegeben werden 2).

Versuchen wir nun ein Listenverständnis:

>>> %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

Wir bekommen hier die richtige Antwort und es geht ziemlich schnell!

Eine andere Möglichkeit 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 ==

Langsam aber richtig!

Und schließlich die Option, die ich verwenden werde: Umwandeln des numpyArrays in den objectTyp:

>>> 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

Schnell und richtig!

ahagen
quelle
Um dies zu beheben 'x' in np.arange(5), schlagen Sie IIUC vor, einfach zu tun 'x' in np.arange(5).astype(object)(oder ähnlich :) 'x' == np.arange(5).astype(object). Richtig? IMHO, dies ist die eleganteste Problemumgehung, die hier gezeigt wird, daher bin ich durch das Fehlen von Upvotes verwirrt. Bearbeiten Sie möglicherweise Ihre Antwort, um mit dem Endergebnis zu beginnen, und fahren Sie dann mit der netten Leistungsanalyse fort?
Oren Milman
Danke @Oren, ich werde das versuchen und sehen, wo es mich hinbringt.
Ahagen
0

Ich hatte diesen Code, der den Fehler verursachte:

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

Ich habe es so geändert:

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

um den Vergleich zu vermeiden, der die Warnung auslöst - wie oben angegeben. Ich musste die Ausnahme nur wegen dfObj.locder for-Schleife vermeiden. Vielleicht gibt es eine Möglichkeit, sie anzuweisen, die bereits geänderten Zeilen nicht zu überprüfen.

ewizard
quelle
0

In meinem Fall trat die Warnung nur aufgrund der regulären Art der booleschen Indizierung auf - weil die Serie nur np.nan hatte. Demonstration (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

Ich denke, mit pandas 1.0 möchten sie wirklich, dass Sie den neuen 'string'Datentyp verwenden, der pd.NAWerte zulässt :

>>> 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

Lieben Sie nicht, an welchem ​​Punkt sie an alltäglichen Funktionen wie der booleschen Indizierung herumgebastelt haben.

Jeyes Unterwegs
quelle