【问题标题】:Ignoring an invalid filter among multiple filters on a DataFrame忽略 DataFrame 上多个过滤器中的无效过滤器
【发布时间】: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

标签: python pandas dataframe


【解决方案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 中的列数不同,那么您可以使用字典,它也可以像下面的示例一样工作,第三列将被忽略,因为所有 False .

d = {'One': 'a', 'Two': 'y'}
m = (df==pd.DataFrame(d, index=df.index).reindex(columns=df.columns))

【讨论】:

  • 非常感谢,我将您的解决方案与 Corralien 的解决方案结合起来,得出了我最终使用的代码。更新了我的答案,谢谢!
  • @AkshaySehgal 你得到了很棒的解决方案,我有点惊讶在完整数据帧 df.eq(inp) 和系列 ~df.eq(inp).any(0 ) 以这种方式工作,但我稍后会尝试记住这一点:)
【解决方案2】:

关键是如果一列返回全部 False(~b.any,无效过滤器)则返回 True 以接受该列的所有值:

mask = df.eq(inp).apply(lambda b: np.where(~b.any(), True, b))
out = df.loc[mask.all(axis="columns")]

案例 1inp = ['a','y','m'](所有有效输入)

>>> out
  One Two Three
1   a   y     m
2   a   y     m

案例 2inp = ['a','NA','NA'](有少量无效输入)

>>> out
  One Two Three
0   a   x     l
1   a   y     m
2   a   y     m

案例 3inp = ['NA','NA','NA'](没有无效输入)

>>> out
  One Two Three
0   a   x     l
1   a   y     m
2   a   y     m
3   b   y     l

案例 4inp = ['b','x','m'](包含所有有效输入但不包含结果)

>>> out
Empty DataFrame
Columns: [One, Two, Three]
Index: []

当然可以增加输入参数:

df["Four"] = ['i','j','k','k']
inp = ['a','NA','m','k']
>>> out
  One Two Three Four
2   a   y     m    k

【讨论】:

  • 很好的解决方案,我从您的解决方案和 Ben.T 的解决方案中汲取灵感,在不使用 apply 方法的情况下提出了一个逻辑。我更新的答案中的详细信息。
  • 太糟糕了,我只能将一个答案标记为正确
【解决方案3】:

列表理解的另一种方式:

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

print(valid_filtering(df, ['a','NA','NA']))的输出:

  One Two Three
0   a   x     l
1   a   y     m
2   a   y     m

相关:applying lambda row on multiple columns pandas

【讨论】:

    猜你喜欢
    • 2019-10-07
    • 2019-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-25
    • 1970-01-01
    相关资源
    最近更新 更多