使用字典中的值过滤Pandas Dataframe

xytpbqjk  于 2023-03-16  发布在  其他
关注(0)|答案(9)|浏览(129)

我需要用一个dict来过滤一个数据框,这个dict的key是列名,value是我想要过滤的值:

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]

但是这将过滤数据框几次,一次一个值,并且不能同时应用所有的过滤器。2有没有一种方法可以通过编程来实现呢?
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

则应当仅选择最后一个。

e1xvtsh3

e1xvtsh31#

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

3lxsmp7m2#

上述内容的抽象,用于传递过滤器值数组而不是单个值的情况(类似于panda.core.series.series.isin())。使用相同的示例:

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':[1,0], 'C':['right']}
##Start with array of all True
ind = [True] * len(df1)

##Loop through filters, updating index
for col, vals in filter_v.items():
    ind = ind & (df1[col].isin(vals))

##Return filtered dataframe
df1[ind]

##Returns

    A   B    C      D
0   1.0 1   right   1
3   1.0 0   right   3
hjzp0vay

hjzp0vay3#

下面是一个方法:

df.loc[df[filter_v.keys()].isin(filter_v.values()).all(axis=1), :]

更新日期:

如果各列的值相同,则可以执行以下操作:

# 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)), axis=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的方法。

xpszyzbs

xpszyzbs4#

对于python 2,@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), :]
flseospp

flseospp5#

这里有另一种方法:

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
byqmnocz

byqmnocz6#

您还可以创建查询

query_string = ' and '.join(
    [f'({key} == "{val}")' if type(val) == str else f'({key} == {val})' for key, val in filter_v.items()]
)

df1.query(query_string)
yuvru6vn

yuvru6vn7#

要跟进DSM的答案,还可以使用any()将查询转换为OR操作(而不是AND):
df1.loc[(df1[list(filter_v)] == pd.Series(filter_v)).any(axis=1)]

bxjv4tth

bxjv4tth8#

结合前面的答案,这里有一个函数可以提供给df1.loc。它允许AND/OR(使用how='all'/'any'),如果需要,它还允许使用op关键字进行==以外的比较。

import operator

def quick_mask(df, filters, how='all', op=operator.eq) -> pd.Series:
    if how == 'all':
        comb = pd.Series.all
    elif how == 'any':
        comb = pd.Series.any
    return comb(op(df[[*filters]], pd.Series(filters)), axis=1)

# Usage
df1.loc[quick_mask(df1, filter_v)]
brc7rcf0

brc7rcf09#

我遇到了一个问题,因为我的字典有多个值的同一个关键字。
我可以将DSM的查询更改为:

df1.loc[df1[list(filter_v)].isin(filter_v).all(axis=1), :]

相关问题