使用具有各种函数的字典筛选熊猫数据帧



假设我有一个具有任意列数的数据帧df。举个例子,假设我们有

a    b    c
0  5    foo  2
1  5    bar  3
2  4    foo  2
3  5    test 1
4  4    bar  7

假设我想要一个过滤器,例如

df[(df['a'] == 5) & (~df['b'].isin(['foo','bar'])) & (df['c'].isin(range(5)))]

或者也许是这样的df[(df['a'] == 5) & (~df['b'].isin(['test','bar'])) | (df['c'].isin(range(5)))]

但我想要一些可以轻松插入作为输入的东西,例如:

def filter_df(filter_kwargs, df):
# do the filtering here

我知道如何处理==运算符,但是我对如何执行更复杂的操作感到困惑,例如.isin|。最好的方法是什么?

我这里有三个解决方案。在我看来,最优雅的是前两个。第三个感觉更像是一个"黑客",但可以用作其他东西的灵感。

import pandas as pd
df = pd.DataFrame({'a': [5,5,4,5,4], 'b': ['foo','bar','foo','test','bar'],'c': [2,3,2,1,7]})

示例 1 - 与您提供的相同内容,但已拆分,因此更具可读性:

mask_1 = (df['a'] == 5) & 
(~df['b'].isin(['foo','bar'])) & 
(df['c'].isin(range(5)))
print(df.loc[mask_1])

示例 2 - 使用 lambda 函数,因此它是标准的,因为条件看起来像其他地方(==,而不是在、在、>、<等...(:>

mask_2 = (df['a'].apply(lambda x: x == 5)) & 
(df['b'].apply(lambda x: x not in ['foo', 'bar'])) & 
(df['c'].apply(lambda x: x in range(5)))
print(df.loc[mask_2])

示例 3 - 灵感来自 B. Hel 的答案和更一般的

答案
def filter_df(filter_kwargs, df):
l = len(filter_kwargs)
for i, cond in enumerate(filter_kwargs):
eval_cond = df[cond[0]].apply(lambda x: eval("x " + cond[1]))
if i == 0:
mask = eval_cond
elif i+1 == l:
break
else:
mask = eval('mask' + filter_kwargs[i-1][2] + 'eval_cond')
return df.loc[mask]
# Format for each condition [[column_name, condition, AND_OR],...]
filter_kwargs = [['a', '==5', '&'],['b', 'not in ["foo", "bar"]','&'], ['c', 'in range(5)','|']]    
print(filter_df(filter_kwargs,df))

假设你有这个前导码

import pandas as pd
df = pd.DataFrame({'a': [5,5,4,5,4], 'b': ['foo','bar','foo','test','bar'],'c': [2,3,2,1,7]})

和这个函数

def helper_function(df,d):
x = True
for (i,k) in enumerate(d):
y = getattr(df[k['key']],k['function'])(k['values'])
if k['isnot']:
y = getattr(getattr(y,'__ne__'),'__self__')
if i == 0:
x = y
else:
x = getattr(x,k['left_connector'])(y)
return x

现在,您可以创建字典列表

di = [
{
'key': 'a',
'function': 'isin',
'isnot': False,
'values': [5],
'left_connector': "__and__"
},
{
'key': 'b',
'function': 'isin',
'isnot': True,
'values': ['test','bar'],
'left_connector': "__and__"
},
{
'key': 'c',
'function': 'isin',
'isnot': False,
'values': [0,1,2,3],
'left_connector': "__or__"
},
]

并使用此代码进行过滤

df[helper_function(df,di)]

由于您只使用熊猫的功能,因此您可以保持熊猫的性能。

这是一个解决方案的想法

import pandas as pd
df = pd.DataFrame({'a': [5,5,4,5,4], 'b': ['foo','bar','foo','test','bar'],'c': [2,3,2,1,7]})

def helper_function(df, *argv):
x = True
y = "and"
for (i,arg) in enumerate(argv):
if (i % 2 == 1):
y = arg
else:
if (y == "and"):
x = x & df[arg[0]].isin(arg[1])
else:
x = x | df[arg[0]].isin(arg[1])
return df[x]
print(helper_function(df, ['a',[5]],"and",['b',['test','bar']],"and",['c',[0,1,2]]))

最新更新