TypeError: 'function' object is not iterable
Я продолжаю получать эту ошибку, когда чувствую, что функция filter должна достичь желаемого результата.
Чего мне не хватает, когда я решаю использовать «фильтр» (который мне кажется более аккуратным, чем использование «loc» для достижения того же результата), и почти всегда получаю эту ошибку.
Вот простой пример.
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(4,4), columns=['a', 'b', 'c', 'd'])
df.filter(lambda x:x['b'] > 0.5, axis=1)
Traceback (most recent call last):
Cell In[190], line 1
df.filter(lambda x:x['b'] > 0.5, axis=1)
File ~/.local/lib/python3.8/site-packages/pandas/core/generic.py:5454 in filter
return self.reindex(**{name: [r for r in items if r in labels]})
TypeError: 'function' object is not iterable
In [1]: df.apply(lambda x:x['b'] > 0.5, axis=1)
Out[1]:
0 True
1 True
2 False
3 False
dtype: bool
In [2]: df.loc[df['b']> 0.5]
Out[2]:
a b c d
0 0.578342 0.82178 0.414652 0.630859
1 0.181232 0.91806 0.772260 0.502921
Даже самая простая лямбда-функция создает ошибку, например:
In [5]: tru = lambda x: True
In [6]: df['b'].filter(tru)
Traceback (most recent call last):
Cell In[196], line 1
df['b'].filter(tru)
File ~/.local/lib/python3.8/site-packages/pandas/core/generic.py:5454 in filter
return self.reindex(**{name: [r for r in items if r in labels]})
TypeError: 'function' object is not iterable
# Once again
In [7]: df['b'].apply(tru)
Out[7]:
0 True
1 True
2 True
3 True
Name: b, dtype: bool
На мой взгляд, filter требует функции, возвращающей логическое значение, что, очевидно, и делает упрощенный пример, но filter терпит неудачу и снова apply работает. Что находится «под капотом», что влияет на результат?
Как объяснялось выше, я попробовал df.filter(function), но это не удалось.
df.loc[df.apply(function)] дает правильный результат.






df.filter используется для «[s]подстановки строк или столбцов фрейма данных в соответствии с указанными метками индекса».
Например, df.filter на индексных метках дает вам соответствующее подмножество:
df.filter([0,1], axis=0)
a b c d
0 0.550798 0.708148 0.290905 0.510828
1 0.892947 0.896293 0.125585 0.207243
То же самое с метками столбцов:
df.filter(['c', 'd'], axis=1)
c d
0 0.290905 0.510828
1 0.125585 0.207243
2 0.029876 0.456833
3 0.676255 0.590863
Чтобы выполнить фильтр того типа, который вы хотите получить, обычно используется логическое индексирование:
import pandas as pd
import numpy as np
np.random.seed(3) # for reproducibility
df = pd.DataFrame(np.random.rand(4,4), columns=['a', 'b', 'c', 'd'])
cond = df['b'] > 0.5
df[cond]
a b c d
0 0.550798 0.708148 0.290905 0.510828
1 0.892947 0.896293 0.125585 0.207243
По крайней мере, в вашем примере альтернативой может быть df.query:
df.query('b > 0.5')
a b c d
0 0.550798 0.708148 0.290905 0.510828
1 0.892947 0.896293 0.125585 0.207243
Возможные путаницы
Для использования встроенного filter в Python требуется итерация, а pd.DataFrame сам по себе не является итерацией. Например, вам придется использовать что-то вроде df.iterrows, а затем потом перестроить df:
pd.DataFrame([row[1] for row in filter(lambda x: x[1]['b'] > 0.5,
df.iterrows())],
columns=df.columns)
a b c d
0 0.550798 0.708148 0.290905 0.510828
1 0.892947 0.896293 0.125585 0.207243
Как видите, это довольно абсурдный поступок.
Несколько сбивает с толку то, что эта функция работает иначе, чем df.filter, и имеет другую цель, а именно фильтровать целые группы по одному или нескольким групповым условиям.
Например, если мы разделим наш df на две группы: индексы 0, 2, 3 и 1, мы сможем сделать что-то вроде этого:
df.groupby([1,2,1,1]).filter(lambda x: (x['b'] > 0.5).all())
a b c d
1 0.892947 0.896293 0.125585 0.207243
# row `0` will be filtered out, since other group members fail test
Имейте в виду, что groupby.filter зачастую не самый эффективный вариант, особенно при работе с простыми агрегатами. Например, рассмотрим следующее:
import pandas as pd
import numpy as np
np.random.seed(0) # for reproducibility
N = 10_000
data = {'a': np.random.choice(np.arange(500), size=N, replace=True),
'b': np.random.rand(N)}
df = pd.DataFrame(data)
gr_transform = lambda df: df[df.groupby('a')['b'].transform('count') > 20]
gr_filter = lambda df: df.groupby('a').filter(lambda x: x['b'].count() > 20)
gr_filter(df).equals(gr_transform(df))
# True
%timeit gr_transform(df)
# 746 µs ± 23.2 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
%timeit gr_filter(df)
# 30.5 ms ± 731 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Здесь groupby.transform с логическим индексированием легко превосходит groupby.filter.
Обратите внимание, что вы можете использовать df.loc[lambda x: x['b'] > 0.5], чтобы действовать как питон filter ;)
@ouroboros и @ Пол Уилсон, ааа. Да, ты прав. Но не путайте с «фильтром» Python, а с фильтром pandas gruopby. С группой я могу делать такие вещи, как grp.filter(lambda x: x['b'].count() > 2), предполагая, что группировка была создана. Но для этого всегда требуется наличие агрегатирующей функции.
Я приму ваш ответ, поскольку он охватывает как минимум 2 (правильных) альтернативы с использованием loc или запроса.
@JohnTweed: понятно. Да, это немного сбивает с толку. Немного реструктуризировал мою пятерку, чтобы убрать потенциальную путаницу в конце, добавив df.groupby.filter, а затем потенциальную проблему с производительностью. Надеюсь, это полезно.
filter не выполняет функцию. Вам следует использовать loc[]
filter имеет несколько опций в pandas, но они основаны на фильтрации имен столбцов (или индексов), а не значений. Он не может принимать функцию в качестве аргумента. Вместо этого потребуется итерация, например список (в документации есть и другие варианты, но они основаны на той же предпосылке).
Ссылка Документация Pandas
Это недопустимое использование функционального фильтра. Вы используете функцию фильтра класса Dataframe, и фильтр не принимает функцию в качестве своего параметра (например, лямбда-функцию, которую вы передаете). Посмотрите документацию о том, как фильтр работает в фрейме данных. обратите внимание, что это не функция фильтра типичного списка (встроенная функция фильтра, которая работает с любой итерацией), которую вы видите в Python, которая отличается. pandas.pydata.org/docs/reference/api/…