使用字典中的值过滤 pandas 数据框
- 2025-03-20 08:46:00
- admin 原创
- 42
问题描述:
我需要用字典过滤数据框,该字典以列名作为键,以我想要过滤的值作为值:
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]
但这会多次过滤数据框,每次过滤一个值,而不是同时应用所有过滤器。有没有办法通过编程来实现?
编辑:一个例子:
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:
对于传递过滤器值数组而不是单个值的情况,对上述内容进行抽象(类似于 pandas.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
解决方案 3:
对于 python2,@primer 的答案中没有问题。但是,由于dict_keys ,在 Python3 中你应该小心。例如,
>> 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), :]
解决方案 4:
以下是一种方法:
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 的方法。
解决方案 5:
您还可以创建查询
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)
解决方案 6:
这是另一种方法:
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
解决方案 7:
为了跟进 DSM 的答案,您还可以使用any()
将查询转变为 OR 运算(而不是 AND):
df1.loc[(df1[list(filter_v)] == pd.Series(filter_v)).any(axis=1)]
解决方案 8:
结合前面的答案,这里有一个可以提供给 的函数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)]
解决方案 9:
由于我的字典中同一个键有多个值,所以我遇到了一个问题。
我能够将 DSM 的查询更改为:
df1.loc[df1[list(filter_v)].isin(filter_v).all(axis=1), :]
相关推荐
热门文章
项目管理软件有哪些?
热门标签
曾咪二维码
扫码咨询,免费领取项目管理大礼包!
云禅道AD