【发布时间】:2021-05-10 19:52:48
【问题描述】:
问题陈述:
我有一个必须用多个条件过滤的 DataFrame。
每个条件都是可选的,这意味着如果用户为某个条件输入了无效值,则可以完全跳过该条件,默认返回原始 DataFrame(没有该特定条件)。
虽然我可以使用多个 if 条件很容易地实现这一点,按顺序修改 DataFrame,但我正在寻找更优雅和可扩展的东西(随着输入参数的增加),并且最好使用内置的 pandas 功能
可重现的示例
虚拟数据框 -
df = pd.DataFrame({'One':['a','a','a','b'],
'Two':['x','y','y','y'],
'Three':['l','m','m','l']})
print(df)
One Two Three
0 a x l
1 a y m
2 a y m
3 b y l
假设无效值是不属于相应列的值。因此,对于“一”列,除“a”和“b”外,所有其他值均无效。如果用户输入'a',那么我应该能够过滤 DataFrame df[df['One']=='a'],但是,如果用户输入任何无效值,则不应应用此类过滤器,并返回原始数据帧 df。
我的尝试(带有多个参数):
def valid_filtering(df, inp):
if inp[0] in df['One'].values:
df = df[df['One']==inp[0]]
if inp[1] in df['Two'].values:
df = df[df['Two']==inp[1]]
if inp[2] in df['Three'].values:
df = df[df['Three']==inp[2]]
return df
所有有效输入 -
inp = ['a','y','m'] #<- all filters valid so df is filtered before returning
print(valid_filtering(df, inp))
One Two Three
1 a y m
2 a y m
只有很少的无效输入 -
inp = ['a','NA','NA'] #<- only first filter is valid, so other 2 filters are ignored
print(valid_filtering(df, inp))
One Two Three
0 a x l
1 a y m
2 a y m
P.S. 附加问题 - 有没有办法让 DataFrame 索引表现得像 -
df[df['One']=='valid'] -> returns filtered df
df[df['One']=='invalid'] -> returns original df
因为这将帮助我重写我的过滤 -
df[(df['One']=='valid') & (df['Two']=='invalid') & (df['Three']=='valid')] -> Filtered by col One and Three
编辑:解决方案 -
受@corralien 和@Ben.T 提供的代码和逻辑启发的更新解决方案
df.loc[(df.eq(inp)|~df.eq(inp).any(0)).all(1)]
【问题讨论】:
-
在您的第二种情况下,
'valid'和'invalid'实际上是那些字符串,或者只是一个有效或无效的值?但似乎您的问题可以通过将字典作为参数来解决,例如d = {'One': 'a'}或d = {'One': 'a', 'Two': 'y', 'Three': 'm'},这样您就可以更灵活地指定您需要过滤的部分或全部列,迭代dict,而不是将基本相同的逻辑复制 3 倍 -
这只是一个例子,我指的是有效值和无效值。
-
基于字典的过滤器绝对是我可以使用的东西,使用
df.loc[df[filter_v.keys()].isin(filter_v.values()).all(axis=1), :]但是,问题是我必须先收集有效和无效值的列表,然后才能创建过滤器字典. link for reference