使用任意数量的条件过滤 Pandas 数据框架

5
我熟悉使用Pandas进行基本过滤和查询。例如,如果我有一个名为 df 的数据帧,我可以执行 df [df ['field1']<2] df [df ['field2']<3] 。我还可以将多个条件链接在一起,例如: df [(df ['field1']<3)&(df ['field2']<2)]
如果我不知道需要使用多少个条件怎么办?是否有一种方法可以将任意数量的这些操作“链接”在一起?我想传递一个过滤器列表,例如 [('field1',3),('field2',2),('field3',4)] ,这将导致将这3个条件链接在一起。
谢谢!

一个例子可能会有所帮助。 - kjmerf
mask = df['field1'] < 3; mask = mask & (df['field2'] < 2); mask = mask & df['field3'] < 4 等等。然后使用 df[mask] - user707650
6个回答

9
pandas Series对象有小于、大于等操作,可以作为方法调用。因此,df['field1'] < 3 可以改为 df['field1'].lt(3)。这不是非常重要,但它使代码更易读。
要实现您要求的内容,可以使用functools中的reduce函数和operator包中的and_(相当于&)函数。
from functools import reduce
from operator import and_

reduce(and_, (df.field1.lt(3), df.field2.lt(2), df.field3.lt(4)))

我认为这是最干净的方法,意图最直接,魔法最少。谢谢! - speedyturkey
2
这也是一个非常好的方法!但是,它缺乏动态性。此外,我会尝试对ndarrays进行评估,而不是series。还有...您仍然需要利用结果来过滤初始数据框。即使它稍微难以阅读,我也会将其用作实际解决方案。df[reduce(and_, (df[t[0]].values < t[1] for t in c))]其中c = [('field1', 3), ('field2', 2), ('field3', 4)] - piRSquared

6

方法一
使用pd.DataFrame.query

c = [('field1', 3), ('field2', 2), ('field3', 4)]
f = '{0[0]} < {0[1]}'.format

df.query(' & '.join(f(t) for t in c))

方法二


c = [('field1', 3), ('field2', 2), ('field3', 4)]

df[df[[t[0] for t in c]].lt([t[1] for t in c]).all(1)]

方法三
将c转换为 pd.Series,然后进行比较。

c = [('field1', 3), ('field2', 2), ('field3', 4)]

s = pd.Series(dict(c))

df[df[s.index].lt(s).all(1)]

2
令人惊叹的...:-) 哈哈 - BENY
感谢您深思熟虑的回复。我喜欢方法2,我认为这是詹姆斯提供的最佳解决方案之后的第二好选择。 - speedyturkey
太棒了的回答! - vagabond

1

类似于使用add,然后使用all,你可以得到所需的布尔值。

df1[['f1','f2','f3']].add([-2,-3,-4]).lt(0).all(1)

我喜欢这个概念。你需要展示如何对df进行子集操作并获取[-2, -3, -4] - piRSquared

1
这是另一种方法:

import pandas as pd
import numpy as np

df = pd.DataFrame([np.arange(4),np.arange(3,7),np.arange(5,9)],
                  columns = ["field1","field2","field3","field4"])

f = [('field1', 3), ('field2', 4), ('field3', 5)]

mask = np.array([(df[i[0]] == i[1]) for i in f])

# 1 True is enough:
df[mask.any(axis=0)]  # [False  True False] in this sample

# All must be true
df[mask.all(axis=0)]  # [False  True False] in this sample

Df看起来像这样:
   field1  field2  field3  field4
0       0       1       2       3
1       3       4       5       6
2       5       6       7       8

0

您可以循环遍历条件,并通过将布尔掩码 &(逻辑与)在一起来迭代地构建掩码

def chain_lt(df, conditions):
    for i, (field, val) in enumerate(conditions):
        res = df[field] < val
        if i == 0:
            msk = res
        else:
            msk &= res
    return df[msk]

0

我相信reduce( (lambda x, y: x & (df[y[0]]<y[1])), list_of_filters )可以实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接