FutureWarning: porównanie elementów nie powiodło się; zwracanie wartości skalarnej, ale w przyszłości będzie wykonywać porównanie elementarne

108

Używam Pandy 0.19.1w Pythonie 3. Otrzymuję ostrzeżenie o tych liniach kodu. Próbuję uzyskać listę zawierającą wszystkie numery wierszy, w których Peterw kolumnie występuje ciąg Unnamed: 5.

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

Generuje ostrzeżenie:

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

Co to jest FutureWarning i czy powinienem go zignorować, ponieważ wydaje się, że działa.

Eric Leschinski
źródło

Odpowiedzi:

172

To FutureWarning nie pochodzi z Pand, jest z numpy, a błąd dotyczy również matplotlib i innych, oto jak odtworzyć ostrzeżenie bliżej źródła problemu:

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

Inny sposób odtworzenia tego błędu przy użyciu operatora double równa się:

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

Przykład Matplotlib, na który wpłynęło to FutureWarning w ramach implementacji wątku kołczanowego: https://matplotlib.org/examples/pylab_examples/quiver_demo.html

Co tu się dzieje?

Między Numpy a natywnym Pythonem nie ma zgody co do tego, co powinno się stać, gdy porównujesz łańcuchy z typami liczbowymi numpy. Zwróć uwagę, że lewy operand to obszar Pythona, prymitywny łańcuch, a operacja środkowa to obszar Pythona, ale prawy operand to obszar numpy'ego. Czy należy zwrócić tablicę skalarną w stylu Pythona czy tablicę logiczną w stylu Numpy? Numpy mówi ndarray of bool, programiści Pythonic nie zgadzają się z tym. Klasyczny dystans.

Czy powinno to być porównanie elementarne czy skalarne, jeśli element istnieje w tablicy?

Jeśli Twój kod lub biblioteka używają operatorów inlub ==do porównywania ciągu znaków Pythona z numpy ndarrays, nie są one zgodne, więc jeśli spróbujesz, zwraca wartość skalarną, ale tylko na razie. Ostrzeżenie wskazuje, że w przyszłości to zachowanie może się zmienić, więc twój kod rzyga po całym dywanie, jeśli python / numpy zdecydują się na przyjęcie stylu Numpy.

Zgłoszone raporty o błędach:

Numpy i Python są w impasie, na razie operacja zwraca skalar, ale w przyszłości może się to zmienić.

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

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

Dwa rozwiązania obejścia:

Albo zablokuj swoją wersję języka Python i numpy, zignoruj ​​ostrzeżenia i spodziewaj się, że zachowanie się nie zmieni, lub przekonwertuj lewy i prawy operand z ==i inbędą z typu numpy lub prymitywnego typu liczbowego Python.

Pomiń ostrzeżenie globalnie:

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

Pomiń ostrzeżenie wiersz po wierszu.

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

Po prostu stłum ostrzeżenie według nazwy, a następnie umieść głośny komentarz obok niego, wspominając o aktualnej wersji Pythona i numpy, mówiąc, że ten kod jest kruchy i wymaga tych wersji, i umieść link do tutaj. Kopnij puszkę w dół drogi.

TLDR: pandas są Jedi; numpysą chaty; i pythonjest imperium galaktycznym. https://youtu.be/OZczsiCfQQk?t=3

Eric Leschinski
źródło
1
Fuj. Więc jeśli mam jakąś ilość thing(która może, ale nie musi być typem odrętwienia, nie wiem) i chcę zobaczyć, czy thing == 'some string'uzyskać prosty boolwynik, co powinienem zrobić? np.atleast_1d(thing)[0] == 'some string'? Ale to nie jest solidne dla jakiegoś jokera, który umieściłby 'some string'pierwszy element tablicy. Myślę, że muszę thingnajpierw przetestować typ, a następnie wykonać ==test tylko wtedy, gdy jest to ciąg znaków (lub nie obiekt numpy).
EL_DON Kwietnia
1
W rzeczywistości to przyszłe ostrzeżenie jest również zgłaszane za każdym razem, gdy spróbujesz porównać numpy.ndarray z pustą listą. Na przykład wykonanie np.array([1, 2]) == []również podniesie ostrzeżenie.
1313e
2
or babysit your left and right operands to be from a common turf
Przydałby mi
12
To niesamowity poziom jakości informacji na ten temat.
StephenBoesch
Więc pozbyłbym się ostrzeżenia o tym kodzie: df.loc [df.cName == '', 'cName'] = '10004'. Innymi słowy, co to jest pandy / numpy odpowiednik pythona '' (pusty ciąg)
Garet Jax
13

Otrzymuję ten sam błąd, gdy próbuję ustawić index_colodczyt pliku do Pandaramki danych:

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

Nigdy wcześniej nie spotkałem się z takim błędem. Nadal próbuję znaleźć przyczynę tego (używając wyjaśnienia @Eric Leschinski i innych).

W każdym razie następujące podejście na razie rozwiązuje problem, dopóki nie znajdę przyczyny:

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

Zaktualizuję to, gdy tylko znajdę przyczynę takiego zachowania.

Dataman
źródło
Mam ten sam problem z read_csv(). Wydaje mi się, że coś pandastrzeba naprawić.
Konstantin
1
Dzięki! Zaoszczędził mi dużo pracy - chyba. pd__version__: 0.22.0; np.__version__: 1.15.4
Markus Dutschke
1
Ten sam problem, najwyraźniej jakieś wywołanie numpy w środku read_csvpodczas używania index_colparametru. Przetestowałem dwie konfiguracje z różnymi wynikami: 1. numpy wersja 1.19.2, Pandas wersja 1.1.2: FutureWarning: porównanie elementwise nie powiodło się ... 2. numpy wersja 1.19.2, Pandas wersja 1.1.3: TypeError: ufunc ' isnan 'nie jest obsługiwany ...
Carlos
9

Moje doświadczenie z tym samym komunikatem ostrzegawczym zostało spowodowane przez TypeError.

TypeError: nieprawidłowe porównanie typów

Więc możesz chcieć sprawdzić typ danych Unnamed: 5

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

Oto, jak mogę powtórzyć komunikat ostrzegawczy:

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

Mam nadzieję, że to pomoże.

yhd.leung
źródło
1
Twój kod zawiera wiele niepotrzebnych ruchomych części, które ilustrują ostrzeżenie. Pandy zaszczycają cię tym dodatkowym TypeError, ale to jest kontrola szkód od Pandas, ostrzeżenie o źródle jest niezgodnością między Numpy i Pythonem i pojawia się przy ocenie df['num3'] == '3'.
Eric Leschinski
1
df.loc[df['num3'] == 3, 'num3'] = 4 # No ErrorTa część mi pomaga. Dzięki
jameslem
9

Nie mogę przebić niesamowicie szczegółowej odpowiedzi Erica Leschinskiego, ale oto szybkie obejście pierwotnego pytania, o którym nie sądzę, że zostało jeszcze wspomniane - umieść ciąg na liście i użyj .isinzamiast==

Na przykład:

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
źródło
zastanawiam się, czy mógłbym zrobić to samo z tą składnią -> if "-" w dfN ['Drate']. unique ()
lone_coder
3

Szybkim obejściem tego jest użycie numpy.core.defchararray. Napotkałem również ten sam komunikat ostrzegawczy i udało mi się go rozwiązać za pomocą powyższego modułu.

import numpy.core.defchararray as npd
resultdataset = npd.equal(dataset1, dataset2)
Jeet23
źródło
2

Odpowiedź Erica w pomocny sposób wyjaśnia, że ​​problem wynika z porównania serii Pandas (zawierającej tablicę NumPy) z ciągiem znaków Pythona. Niestety, oba jego obejścia tylko pomijają ostrzeżenie.

Aby napisać kod, który nie powoduje ostrzeżenia w pierwszej kolejności, jawnie porównaj swój ciąg z każdym elementem Series i uzyskaj oddzielną wartość bool dla każdego. Na przykład możesz użyć mapi anonimowej funkcji.

myRows = df[df['Unnamed: 5'].map( lambda x: x == 'Peter' )].index.tolist()
Nathan
źródło
1

Jeśli twoje tablice nie są zbyt duże lub nie masz ich zbyt wiele, możesz być w stanie wymusić na lewej stronie ==ciąg znaków:

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

Ale to jest ~ 1,5 raza wolniej, jeśli df['Unnamed: 5']jest ciągiem, 25-30 razy wolniej, jeśli df['Unnamed: 5']jest to mała tablica numpy (długość = 10) i 150-160 razy wolniej, jeśli jest to tablica numpy o długości 100 (razy uśredniona z 500 prób) .

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

Wynik:

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
źródło
1
Prefiks po lewej stronie ==z strbył dla mnie dobrym rozwiązaniem, które ledwo wpłynęło na wydajność 1,5 miliona wierszy, które nie będą większe niż w przyszłości.
David Erickson
1

W moim przypadku ostrzeżenie wystąpiło właśnie z powodu zwykłego typu indeksowania boolowskiego - bo seria miała tylko np.nan. Demonstracja (pandy 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

Myślę, że w pandach 1.0 naprawdę chcą, abyś używał nowego 'string'typu danych, który pozwala na pd.NAwartości:

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

Nie podoba mi się, w którym momencie majstrowali przy codziennych funkcjach, takich jak indeksowanie logiczne.

Jeyes Unterwegs
źródło
0

Otrzymałem to ostrzeżenie, ponieważ myślałem, że moja kolumna zawiera ciągi zerowe, ale po sprawdzeniu zawierała np.nan!

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

Pomogła zmiana mojej kolumny na puste ciągi :)

intotecho
źródło
0

Porównałem kilka możliwych metod, w tym pandy, kilka metod numpy i metodę rozumienia listy.

Najpierw zacznijmy od linii bazowej:

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

Tak więc naszym punktem odniesienia jest to, że liczba powinna być poprawna 2i powinniśmy się zająć 50 us.

Teraz próbujemy naiwnej metody:

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

I tutaj otrzymujemy złą odpowiedź ( NotImplemented != 2), zajmuje nam to dużo czasu i rzuca ostrzeżenie.

Spróbujemy więc innej naiwnej metody:

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

Znowu zła odpowiedź ( 0 != 2). Jest to jeszcze bardziej podstępne, ponieważ nie ma kolejnych ostrzeżeń ( 0można je przekazać tak samo 2).

Teraz spróbujmy zrozumieć listę:

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

Mamy tutaj właściwą odpowiedź i jest to dość szybkie!

Inna możliwość 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 ==

Powolne, ale poprawne!

I na koniec opcja, której zamierzam użyć: rzutowanie numpytablicy na 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

Szybko i poprawnie!

ahagen
źródło
Więc IIUC, aby naprawić 'x' in np.arange(5), sugerujesz po prostu zrobienie 'x' in np.arange(5).astype(object)(lub podobnie :) 'x' == np.arange(5).astype(object). Dobrze? IMHO, jest to najbardziej eleganckie obejście pokazane tutaj, więc jestem zdezorientowany brakiem głosów za. Może edytuj swoją odpowiedź, aby rozpocząć od dolnej linii, a następnie przejdź do ładnej analizy wydajności?
Oren Milman
Dzięki @Oren, spróbuję tego i zobaczę, dokąd mnie to doprowadzi.
ahagen
0

Miałem ten kod, który powodował błąd:

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

Zmieniłem to na to:

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

aby uniknąć porównania, które rzuca ostrzeżenie - jak wspomniano powyżej. Musiałem tylko uniknąć wyjątku z powodu dfObj.locpętli for, być może jest sposób, aby powiedzieć mu, aby nie sprawdzał wierszy, które już zmienił.

ewizard
źródło