Python 忽略数据帧上多个筛选器中的无效筛选器 问题陈述:
我有一个数据帧,必须用多个条件进行过滤 每个条件都是可选的,这意味着如果用户为某个条件输入了无效值,则可以完全跳过该条件,默认为原始数据帧(没有该特定条件) 虽然我可以在多个if条件下很容易地实现这一点,以顺序方式修改数据帧,但我正在寻找更优雅和可伸缩的东西(增加输入参数),最好使用内置功能 可复制示例 虚拟数据帧-Python 忽略数据帧上多个筛选器中的无效筛选器 问题陈述:,python,pandas,dataframe,Python,Pandas,Dataframe,我有一个数据帧,必须用多个条件进行过滤 每个条件都是可选的,这意味着如果用户为某个条件输入了无效值,则可以完全跳过该条件,默认为原始数据帧(没有该特定条件) 虽然我可以在多个if条件下很容易地实现这一点,以顺序方式修改数据帧,但我正在寻找更优雅和可伸缩的东西(增加输入参数),最好使用内置功能 可复制示例 虚拟数据帧- df = pd.DataFrame({'One':['a','a','a','b'], 'Two':['x','y','y','y'],
df = pd.DataFrame({'One':['a','a','a','b'],
'Two':['x','y','y','y'],
'Three':['l','m','m','l']})
print(df)
假设无效值是不属于相应列的值。因此,对于列“One”,除了“a”和“b”之外,所有其他值都无效。如果用户输入的是'a',那么我应该能够过滤数据帧df[df['One']='a']
,但是,如果用户输入任何无效值,则不应应用此类过滤器,并返回原始数据帧df
我的尝试(使用多个参数):
使用所有有效输入-
inp = ['a','y','m'] #<- all filters valid so df is filtered before returning
print(valid_filtering(df, inp))
inp = ['a','NA','NA'] #<- only first filter is valid, so other 2 filters are ignored
print(valid_filtering(df, inp))
几乎没有无效的输入-
inp = ['a','y','m'] #<- all filters valid so df is filtered before returning
print(valid_filtering(df, inp))
inp = ['a','NA','NA'] #<- only first filter is valid, so other 2 filters are ignored
print(valid_filtering(df, inp))
p.S.另外一个问题-有没有办法让数据帧索引表现为-
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
编辑:解决方案- 受@correlian和@Ben.T提供的代码和逻辑启发的更新解决方案
df.loc[(df.eq(inp)|~df.eq(inp).any(0)).all(1)]
下面是一种根据每列中inp的每个值创建布尔数据帧的方法。然后沿行使用
any
以获得至少有一个True的列,并在选择至少有一个True的列后沿列使用all
def valid_filtering(df, inp):
# check where inp values are same than in df
m = (df==pd.DataFrame(data=[inp] , index=df.index, columns=df.columns))
# select the columns with at least one True
cols = m.columns[m.any()]
# select the rows that all True amongst wanted columns
rows = m[cols].all(axis=1)
# return df with selected rows
return df.loc[rows]
请注意,如果您没有与原始df中的列数相同的筛选器,那么您可以使用字典,它也可以工作,如下面的示例所示,列3将被忽略,因为allFalse
d = {'One': 'a', 'Two': 'y'}
m = (df==pd.DataFrame(d, index=df.index).reindex(columns=df.columns))
关键是如果列返回all False(
~b.any
,无效筛选器),则返回True以接受此列的所有值:
mask=df.eq(inp).apply(lambda b:np.where(~b.any(),True,b))
out=df.loc[屏蔽所有(axis=“columns”)]
案例1:inp=['a','y','m']
(包含所有有效输入)
>>输出
一二三
1 a y m
上午2点
案例2:inp=['a','NA','NA']
(很少有无效输入)
>>输出
一二三
0 a x l
1 a y m
上午2点
案例3:inp=['NA','NA','NA']
(无无效输入)
>>输出
一二三
0 a x l
1 a y m
上午2点
3 b y l
案例4:inp=['b','x','m']
(包含所有有效输入,但不包含结果)
当然,您可以增加输入参数:
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
df[“四”]=['i','j','k','k']
inp=['a','NA','m','k']
列表理解的另一种方式:
def valid_filtering(df, inp):
series = [df[column] == inp[i]
for i, column in enumerate(df.columns) if len(df[df[column] == inp[i]].values) > 0]
for s in series: df = df[s]
return df
打印输出(有效过滤(df、['a'、'NA'、'NA'])
:
相关:在第二种情况下,
是否为“有效”
和是否为“无效”
实际上是那些字符串,还是仅仅是一个有效或无效的值?但是似乎你的问题可以通过把dict作为参数来解决,比如d={'One':'a'}
或者d={'One':'a','Two':'y','Three':'m'}
,这样你就可以更灵活地指定你需要过滤的一些或所有列,在dict上迭代,我指的是有效值和无效值,而不是基本相同的逻辑。基于字典的筛选器肯定是我可以使用的,使用df.loc[df[filter\u v.keys()].isin(filter\u v.values()).all(axis=1),:]
但是,问题是,在创建过滤器字典之前,我必须收集有效值和无效值的列表。伟大的解决方案,我从你的解决方案和Ben.T的解决方案中得到灵感,提出了一个不使用apply方法的逻辑。我更新的答案中的详细信息。太糟糕了,我只能将一个答案标记为正确。非常感谢,我将您的解决方案与Corrarien的解决方案结合起来,得出了我最终使用的代码。更新我的答案,谢谢@AkshaySehgal非常棒的解决方案,我有点惊讶在完整数据帧df.eq(inp)和系列~df.eq(inp)之间执行or操作。任何(0)都是这样工作的,但我稍后会尝试记住这一点:)
>>> out
Empty DataFrame
Columns: [One, Two, Three]
Index: []
>>> out
One Two Three Four
2 a y m k
def valid_filtering(df, inp):
series = [df[column] == inp[i]
for i, column in enumerate(df.columns) if len(df[df[column] == inp[i]].values) > 0]
for s in series: df = df[s]
return df
One Two Three
0 a x l
1 a y m
2 a y m