Фильтр a pandas с использованием значений из dict
Мне нужно отфильтровать фрейм данных с помощью dict, с ключом, являющимся именем столбца, и значением, являющимся значением, которое я хочу фильтровать:
filter_v = {'A':1, 'B':0, 'C':'This is right'}
# this would be the normal approach
df[(df['A'] == 1) & (df['B'] ==0)& (df['C'] == 'This is right')]
Но я хочу сделать что-то в строках
for column, value in filter_v.items():
df[df[column] == value]
но это будет фильтровать кадр данных несколько раз, по одному значению за раз и не применять все фильтры одновременно. Есть ли способ сделать это программно?
EDIT: пример:
df1 = pd.DataFrame({'A':[1,0,1,1, np.nan], 'B':[1,1,1,0,1], 'C':['right','right','wrong','right', 'right'],'D':[1,2,2,3,4]})
filter_v = {'A':1, 'B':0, 'C':'right'}
df1.loc[df1[filter_v.keys()].isin(filter_v.values()).all(axis=1), :]
дает
A B C D
0 1 1 right 1
1 0 1 right 2
3 1 0 right 3
но ожидаемый результат был
A B C D
3 1 0 right 3
следует выбрать только последний.
Ответы
Ответ 1
IIUC, вы должны сделать что-то вроде этого:
>>> df1.loc[(df1[list(filter_v)] == pd.Series(filter_v)).all(axis=1)]
A B C D
3 1 0 right 3
Это работает, делая серию для сравнения:
>>> pd.Series(filter_v)
A 1
B 0
C right
dtype: object
Выбор соответствующей части df1
:
>>> df1[list(filter_v)]
A C B
0 1 right 1
1 0 right 1
2 1 wrong 1
3 1 right 0
4 NaN right 1
Поиск того, где они совпадают:
>>> df1[list(filter_v)] == pd.Series(filter_v)
A B C
0 True False True
1 False False True
2 True False False
3 True True True
4 False False True
Поиск того, где они все совпадают:
>>> (df1[list(filter_v)] == pd.Series(filter_v)).all(axis=1)
0 False
1 False
2 False
3 True
4 False
dtype: bool
И, наконец, используя это для индексации в df1:
>>> df1.loc[(df1[list(filter_v)] == pd.Series(filter_v)).all(axis=1)]
A B C D
3 1 0 right 3
Ответ 2
Вот как это сделать:
df.loc[df[filter_v.keys()].isin(filter_v.values()).all(axis=1), :]
UPDATE:
При значениях, одинаковых по столбцам, вы можете сделать что-то вроде этого:
# Create your filtering function:
def filter_dict(df, dic):
return df[df[dic.keys()].apply(
lambda x: x.equals(pd.Series(dic.values(), index=x.index, name=x.name)), asix=1)]
# Use it on your DataFrame:
filter_dict(df1, filter_v)
Что дает:
A B C D
3 1 0 right 3
Если вы что-то часто делаете, вы можете зайти так далеко, чтобы установить DataFrame для легкого доступа к этому фильтру:
pd.DataFrame.filter_dict_ = filter_dict
И затем используйте этот фильтр следующим образом:
df1.filter_dict_(filter_v)
Что даст тот же результат.
НО, это неправильный способ сделать это, ясно.
Я бы использовал подход DSM.
Ответ 3
Здесь другой способ:
filterSeries = pd.Series(np.ones(df.shape[0],dtype=bool))
for column, value in filter_v.items():
filterSeries = ((df[column] == value) & filterSeries)
Это дает:
>>> df[filterSeries]
A B C D
3 1 0 right 3
Ответ 4
Для python2 это нормально в ответе @primer. Но вы должны быть осторожны в Python3 из-за dict_keys. Например,
>> df.loc[df[filter_v.keys()].isin(filter_v.values()).all(axis=1), :]
>> TypeError: unhashable type: 'dict_keys'
Правильный путь к Python3:
df.loc[df[list(filter_v.keys())].isin(list(filter_v.values())).all(axis=1), :]
Ответ 5
Чтобы отслеживать ответ DSM, вы также можете использовать any()
чтобы превратить ваш запрос в операцию OR (вместо AND):
df1.loc[(df1[list(filter_v)] == pd.Series(filter_v)).any(axis=1)]