Фильтр 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)]