这里有另一种方法。它更清洁,性能更好,并且具有一个优点,即columns
可以为空(在这种情况下,整个数据框将被返回)。
def filter(df, value, *columns):
return df.loc[df.loc[:, columns].eq(value).all(axis=1)]
说明
values = df.loc[:, columns]
只选择我们感兴趣的列。
masks = values.eq(value)
给出一个布尔数据框,指示与目标值相等。
mask = masks.all(axis=1)
对列应用AND(返回索引掩码)。请注意,您可以使用masks.any(axis=1)
进行OR运算。
return df.loc[mask]
将索引掩码应用于数据框。
演示
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randint(0, 2, (100, 3)), columns=list('ABC'))
assert np.all(filter(df, 1, 'A', 'B') == df[(df.A == 1) & (df.B == 1)])
assert np.all(filter(df, 1) == df)
assert np.all(filter(df, [1, 0], 'A', 'B') == df[(df.A == 1) & (df.B == 0)])
备选方案
对于少量列(< 5),以下解决方案基于 steven's answer,比上述方案更具性能,但灵活性较差。原样使用时,它将无法处理空的columns
集,并且不能针对每个列使用不同的值。
from operator import and_
def filter(df, value, *columns):
return df.loc[reduce(and_, (df[column] == value for column in columns))]
通过键检索 Series
对象(df[column]
)比在列子集周围构建 DataFrame
对象(df.loc[:, columns]
)要快得多。
In [4]: %timeit df['A'] == 1
100 loops, best of 3: 17.3 ms per loop
In [5]: %timeit df.loc[:, ['A']] == 1
10 loops, best of 3: 48.6 ms per loop
然而,当处理更多的列时,这种加速变得微不足道。瓶颈变成了将掩码进行AND运算,对于此操作,reduce(and_, ...)
比Pandas内置的all(axis=1)
要慢得多。