FutureWarning: не вдалося порівняти по елементах; повертається скаляр, але в майбутньому буде виконувати поелементне порівняння


100

Я використовую Pandas 0.19.1на Python 3. Я отримую попередження щодо цих рядків коду. Я намагаюся отримати список, який містить усі номери рядків, де рядок Peterприсутній у стовпці Unnamed: 5.

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

Він видає попередження:

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

Що це за FutureWarning, і чи варто його ігнорувати, оскільки це, здається, працює.

Відповіді:


159

Це FutureWarning не від Pandas, воно від numpy, а помилка також впливає на matplotlib та інші, ось як відтворити попередження ближче до джерела проблеми:

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

Інший спосіб відтворити цю помилку за допомогою оператора double equals:

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

Приклад Matplotlib, на який впливає це FutureWarning під реалізацією їхнього сагайдака: https://matplotlib.org/examples/pylab_examples/quiver_demo.html

Що тут відбувається?

Існує розбіжність між Numpy та рідним python щодо того, що має статися, коли ви порівнюєте рядки з числовими типами numpy. Зверніть увагу, що лівий операнд - це дерн python, примітивний рядок, а середня операція - торф python, але правий операнд - торф numpy. Чи слід повертати скаляр стилю Python або ndarray стилю Numpy булевого значення? Numpy каже, що ndarray з bool, розробники Pythonic не погоджуються. Класичний протистояння.

Це має бути елементне порівняння або Скаляр, якщо елемент існує в масиві?

Якщо ваш код або бібліотека використовує оператори inor ==для порівняння рядка python із numpy ndarrays, вони несумісні, тому при спробі він повертає скаляр, але поки що. Попередження вказує на те, що в майбутньому ця поведінка може змінитися, тому ваш код пульсує по всьому килиму, якщо python / numpy вирішить прийняти стиль Numpy.

Надіслані звіти про помилки:

Numpy та Python перебувають у протистоянні, наразі операція повертає скаляр, але в майбутньому це може змінитися.

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

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

Два обхідних рішення:

Або заблокуйте свою версію python і numpy, ігноруйте попередження та очікуйте, що поведінка не зміниться, або перетворіть як лівий, так і правий операнди ==та inз типу numpy або примітивного числового типу python.

Приглушити попередження глобально:

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

Придушіть попередження по черзі.

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

Просто придушіть попередження за іменем, а потім поставте гучний коментар поруч із цим, згадуючи поточну версію python та numpy, сказавши, що цей код є крихким і вимагає цих версій, і розмістіть тут посилання. Збийте банку по дорозі.

TLDR: pandas є джедаями; numpyє хатини; і pythonє галактичною імперією. https://youtu.be/OZczsiCfQQk?t=3


1
Тьфу. Отже, якщо у мене є якась кількість thing(яка може бути або не бути нумізовим типом; я не знаю), і я хочу перевірити, чи thing == 'some string'отримаю простий boolрезультат, що мені робити? np.atleast_1d(thing)[0] == 'some string'? Але це не надійно для деяких жартівників, які вводять 'some string'перший елемент масиву. Я думаю, мені доведеться перевірити тип thingпершого, а потім робити ==тест, лише якщо це рядок (або не numpy-об'єкт).
EL_DON

1
Власне, це майбутнє попередження також піднімається щоразу, коли ви намагаєтесь порівняти numpy.ndarray з порожнім списком. Наприклад, при виконанні також np.array([1, 2]) == []буде піднято попередження.
1313e,

2
Мені було б корисно побачити приклад цього:or babysit your left and right operands to be from a common turf
ГаПсантран,

10
Це дивовижний рівень якості інформації з цього питання.
StephenBoesch

Тому я б позбувся застереження за цим кодом: df.loc [df.cName == '', 'cName'] = '10004'. Іншими словами, що pandas / numpy еквівалентно python '' (порожній рядок)
Garet Jax

13

Я отримую ту ж помилку, коли намагаюся встановити index_colчитання файлу в 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])

Я ніколи раніше не стикався з такою помилкою. Я все ще намагаюся з’ясувати причину цього (використовуючи пояснення @Eric Leschinski та інші).

У будь-якому випадку, наступний підхід вирішує проблему поки що, поки я не з’ясую причину:

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

Я оновлю це, як тільки з’ясую причину такої поведінки.


У мене така сама проблема з read_csv(). Мені здається, це щось, що pandasпотрібно виправити.
Костянтин

1
Дякую! Врятував мені багато роботи - мабуть. pd__version__: 0.22.0; np.__version__: 1.15.4
Markus

1
Тут та сама проблема, мабуть, якийсь виклик numpy всередині read_csvпри використанні index_colпараметра. Я протестував дві установки з різними результатами: 1. numpy версії 1.19.2, версії Pandas 1.1.2: FutureWarning: не вдалося порівняти елементне порівняння ... 2. numpy версії 1.19.2, версії Pandas 1.1.3: TypeError: ufunc ' isnan 'не підтримується ...
Карлос,

9

Мій досвід щодо того самого попереджувального повідомлення був спричинений TypeError.

Помилка типу: недійсне порівняння типів

Отже, ви можете перевірити тип даних Unnamed: 5

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

Ось як я можу повторити попереджувальне повідомлення:

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

Сподіваюся, це допоможе.


1
У вашому коді багато непотрібних рухомих частин для ілюстрації попередження. Pandas надає вам додаткової помилки TypeError, але це контроль збитків від Pandas. Попередження про джерело - це розбіжності між Numpy та Python і виникає при оцінці df['num3'] == '3'.
Ерік Лещинський

1
df.loc[df['num3'] == 3, 'num3'] = 4 # No ErrorЦя частина мені допомагає. Дякую
jameslem

6

Не можу перемогти надзвичайно детальну відповідь Еріка Лещинського, але ось короткий обхідний шлях до оригінального питання, яке, на мою думку, ще не згадувалося - додайте рядок у список і використовуйте .isinзамість==

Наприклад:

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

цікаво, чи міг би я зробити те саме з цим синтаксисом -> якщо "-" у dfN ['Drate']. unique ()
lone_coder

3

Швидким вирішенням цього є використання numpy.core.defchararray. Я також зіткнувся з тим самим попереджувальним повідомленням і зміг вирішити його за допомогою наведеного вище модуля.

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

2

Відповідь Еріка корисно пояснює, що проблема полягає в порівнянні серії Pandas (що містить масив NumPy) із рядком Python. На жаль, обидва його обхідні шляхи просто придушують попередження.

Щоб написати код, який спочатку не викликає попередження, явно порівняйте свій рядок із кожним елементом серії та отримайте окремий bool для кожного. Наприклад, ви можете використовувати mapі анонімну функцію.

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

1

Якщо ваші масиви не надто великі або у вас їх не так багато, можливо, вам вдасться піти, примусивши ліву частину ==бути рядком:

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

Але це в ~ 1,5 рази повільніше, якщо df['Unnamed: 5']це рядок, у 25-30 разів повільніше, якщо df['Unnamed: 5']це малий масив numpy (довжина = 10), і в 150-160 разів повільніше, якщо це масив numpy довжиною 100 (в середньому за 500 випробувань) .

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

Результат:

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

1
Префікс лівої частини ==з strбуло для мене хорошим рішенням, яке ледь погіршило продуктивність 1,5 мільйона рядків, які в майбутньому не стануть більшими, ніж це.
Девід Еріксон,

0

Я отримав це попередження, тому що вважав, що мій стовпець містить нульові рядки, але при перевірці він містив np.nan!

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

Зміна мого стовпця на порожні рядки допомогло :)


0

Я порівняв декілька можливих методів для цього, включаючи панд, кілька методів numpy та метод розуміння списку.

Спочатку почнемо з базової лінії:

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

Отже, наше базове значення полягає в тому, що підрахунок повинен бути правильним 2, і ми повинні взяти приблизно50 us .

Тепер ми спробуємо наївний метод:

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

І ось, ми отримуємо неправильну відповідь ( NotImplemented != 2), це займає у нас багато часу і видає попередження.

Тож ми спробуємо інший наївний метод:

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

Знову ж, неправильна відповідь ( 0 != 2). Це ще більш підступно, оскільки подальших попереджень немає ( 0їх можна передавати, як і 2).

Тепер спробуємо зрозуміти список:

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

Тут ми отримуємо правильну відповідь, і це досить швидко!

Ще одна можливість 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 ==

Повільно, але правильно!

І нарешті, варіант, який я збираюся використовувати: приведення numpyмасиву до objectтипу:

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

Швидко і правильно!


Отже, IIUC, щоб виправити 'x' in np.arange(5), ви пропонуєте просто зробити 'x' in np.arange(5).astype(object)(або подібним чином:) 'x' == np.arange(5).astype(object). Правда? ІМХО, це найелегантніший обхідний шлях, показаний тут, тому мене бентежить відсутність голосів. Можливо, відредагуйте свою відповідь, щоб почати з нижнього рядка, а потім перейти до приємного аналізу ефективності?
Орен Мілман,

Дякую @Oren, я спробую це і подивлюсь, куди це мене дійде.
ahagen

0

У мене був цей код, який спричинив помилку:

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

Я змінив його на це:

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

щоб уникнути порівняння, яке викликає попередження - як зазначено вище. Мені довелося уникати виключення лише через dfObj.locцикл for, можливо, є спосіб сказати йому не перевіряти рядки, які він уже змінив.


0

У моєму випадку застереження відбулось лише через звичайний тип булевого індексування - оскільки серія мала лише np.nan. Демонстрація (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

Я думаю, що з pandas 1.0 вони дійсно хочуть, щоб ви використовували новий 'string'тип даних, який дозволяє використовувати pd.NAзначення:

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

Не любіть, коли вони повозилися з повсякденними функціоналами, такими як логічне індексування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.