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

Другой способ воспроизвести эту ошибку с помощью оператора двойного равенства:

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 и собственным питоном по поводу того, что должно произойти, когда вы сравниваете строки с числовыми типами numpy. Обратите внимание, что левый операнд - это область Python, примитивная строка, а средняя операция - это область Python, но правый операнд - это область numpy. Следует ли возвращать Scalar в стиле Python или ndarray в стиле Numpy для Boolean? Numpy говорит, что ndarray от bool, разработчики Pythonic не согласны. Классическое противостояние.

Должно ли это быть поэлементное сравнение или скалярное, если элемент существует в массиве?

Если в вашем коде или библиотеке используются операторы inили ==для сравнения строки 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, говоря, что этот код хрупкий и требует этих версий, и поместите ссылку здесь. Выбей консервную банку по дороге.

TL pandas ; DR: джедаи; numpyхижины; и pythonэто галактическая империя. https://youtu.be/OZczsiCfQQk?t=3


1
Тьфу. Итак, если у меня есть какое-то количество thing(которое может быть или не быть типом numpy; я не знаю), и я хочу посмотреть, 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
HaPsantran

10
Это потрясающий уровень качественной информации по этому вопросу.
Стивен Бош

Поэтому я бы избавился от предупреждения в этом коде: df.loc [df.cName == '', 'cName'] = '10004'. Другими словами, что такое pandas / numpy, эквивалентное python '' (пустая строка)
Гарет Джакс

12

Я получаю ту же ошибку, когда пытаюсь установить 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
Маркус Дучке

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.

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

5

Не могу победить потрясающе подробный ответ Эрика Лещинского, но вот быстрый способ решения исходного вопроса, который, я думаю, еще не упоминался - поместите строку в список и используйте .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"]

Интересно, могу ли я сделать то же самое с этим синтаксисом -> if "-" в 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

Я сравнил несколько возможных для этого методов, включая pandas, несколько методов 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, я попробую и посмотрю, к чему это приведет.
Ахаген,

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.