pandas: filter rows of DataFrame with operator chaining
在
1 | df_filtered = df[df['column'] == value] |
这是不吸引人的,因为它要求我在能够过滤变量值之前将
1 | df_filtered = df.mask(lambda x: x['column'] == value) |
我不完全确定您想要什么,您的最后一行代码也没有帮助,但无论如何:
"链接"过滤是通过"链接"布尔索引中的条件来完成的。
1 2 3 4 5 6 7 8 9 10 11 12 | In [96]: df Out[96]: A B C D a 1 4 9 1 b 4 5 0 2 c 5 5 1 0 d 1 3 9 6 In [99]: df[(df.A == 1) & (df.D == 6)] Out[99]: A B C D d 1 3 9 6 |
如果你想链接方法,你可以添加你自己的蒙版方法并使用它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | In [90]: def mask(df, key, value): ....: return df[df[key] == value] ....: In [92]: pandas.DataFrame.mask = mask In [93]: df = pandas.DataFrame(np.random.randint(0, 10, (4,4)), index=list('abcd'), columns=list('ABCD')) In [95]: df.ix['d','A'] = df.ix['a', 'A'] In [96]: df Out[96]: A B C D a 1 4 9 1 b 4 5 0 2 c 5 5 1 0 d 1 3 9 6 In [97]: df.mask('A', 1) Out[97]: A B C D a 1 4 9 1 d 1 3 9 6 In [98]: df.mask('A', 1).mask('D', 6) Out[98]: A B C D d 1 3 9 6 |
可以使用pandas查询链接过滤器:
1 2 | df = pd.DataFrame( np.random.randn(30,3), columns = ['a','b','c']) df_filtered = df.query('a>0').query('0<b<2') |
也可以在单个查询中组合过滤器:
1 | df_filtered = df.query('a>0 and 0<b<2') |
@lodagro的回答很好。我将通过将mask函数归纳为:
1 2 | def mask(df, f): return df[f(df)] |
然后你可以做如下的事情:
1 | df.mask(lambda x: x[0] < 0).mask(lambda x: x[1] > 0) |
从0.18.1版开始,
1 2 3 4 5 6 7 | import numpy as np import pandas as pd df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD')) df.loc[lambda df: df.A == 80] # equivalent to df[df.A == 80] but chainable df.sort_values('A').loc[lambda df: df.A > 80].loc[lambda df: df.B > df.A] |
如果你所做的只是过滤,你也可以省略
我提供这个作为额外的例子。这与https://stackoverflow.com/a/28159296的答案相同。/
我将添加其他编辑以使此文章更有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import pandas as pd import numpy as np np.random.seed([3,1415]) df = pd.DataFrame( np.random.randint(10, size=(10, 5)), columns=list('ABCDE') ) df A B C D E 0 0 2 7 3 8 1 7 0 6 8 6 2 0 2 0 4 9 3 7 3 2 4 3 4 3 6 7 7 4 5 5 3 7 5 9 6 8 7 6 4 7 7 6 2 6 6 5 8 2 8 7 5 8 9 4 7 6 1 5 |
让我们用
1 2 3 4 5 6 7 8 9 10 | df.query('D > B') A B C D E 0 0 2 7 3 8 1 7 0 6 8 6 2 0 2 0 4 9 3 7 3 2 4 3 4 3 6 7 7 4 5 5 3 7 5 9 7 6 2 6 6 5 |
我们链
1 2 3 4 5 6 7 8 9 10 11 | df.query('D > B').query('C > B') # equivalent to # df.query('D > B and C > B') # but defeats the purpose of demonstrating chaining A B C D E 0 0 2 7 3 8 1 7 0 6 8 6 4 3 6 7 7 4 5 5 3 7 5 9 7 6 2 6 6 5 |
我的回答和其他人相似。如果您不想创建一个新的函数,您可以使用熊猫已经为您定义的函数。使用管道方法。
1 | df.pipe(lambda d: d[d['column'] == value]) |
我也有同样的问题,只是我想把标准合并成一个或条件。wouter overmeire给出的格式将标准合并为一个和条件,这样两个条件都必须满足:
1 2 3 4 5 6 7 8 9 10 11 12 | In [96]: df Out[96]: A B C D a 1 4 9 1 b 4 5 0 2 c 5 5 1 0 d 1 3 9 6 In [99]: df[(df.A == 1) & (df.D == 6)] Out[99]: A B C D d 1 3 9 6 |
但我发现,如果将每个条件包装在
1 | df[((df.A==1) == True) | ((df.D==6) == True)] |
如果要应用所有常见的布尔值遮罩以及通用遮罩,可以将以下内容放入一个文件中,然后简单地按如下方式分配它们:
1 | pd.DataFrame = apply_masks() |
用途:
1 2 | A = pd.DataFrame(np.random.randn(4, 4), columns=["A","B","C","D"]) A.le_mask("A", 0.7).ge_mask("B", 0.2)... (May be repeated as necessary |
它有点黑客,但如果你不断地根据过滤器来切碎和更改数据集,它可以让事情变得更干净一点。在gen_mask函数中还有一个通用过滤器,它是根据上面的daniel velkov改编的,您可以使用lambda函数,或者在需要时使用。
要保存的文件(我使用masks.py):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import pandas as pd def eq_mask(df, key, value): return df[df[key] == value] def ge_mask(df, key, value): return df[df[key] >= value] def gt_mask(df, key, value): return df[df[key] > value] def le_mask(df, key, value): return df[df[key] <= value] def lt_mask(df, key, value): return df[df[key] < value] def ne_mask(df, key, value): return df[df[key] != value] def gen_mask(df, f): return df[f(df)] def apply_masks(): pd.DataFrame.eq_mask = eq_mask pd.DataFrame.ge_mask = ge_mask pd.DataFrame.gt_mask = gt_mask pd.DataFrame.le_mask = le_mask pd.DataFrame.lt_mask = lt_mask pd.DataFrame.ne_mask = ne_mask pd.DataFrame.gen_mask = gen_mask return pd.DataFrame if __name__ == '__main__': pass |
大熊猫提供了两种替代方法来替代沃特·奥维米尔的答案,而不需要任何压倒性的回答。一个是带可调用的
1 | df_filtered = df.loc[lambda x: x['column'] == value] |
另一个是
1 | df_filtered = df.pipe(lambda x: x['column'] == value) |
这个解决方案在实现方面更为黑客,但我发现它在使用方面更为清晰,而且它肯定比其他建议的更为通用。
https://github.com/toobaz/generic_utils/blob/master/generic_utils/pandas/where.py
您不需要下载整个repo:保存文件并执行
1 | from where import where as W |
就够了。然后像这样使用它:
1 2 3 4 5 6 7 8 9 10 11 | df = pd.DataFrame([[1, 2, True], [3, 4, False], [5, 7, True]], index=range(3), columns=['a', 'b', 'c']) # On specific column: print(df.loc[W['a'] > 2]) print(df.loc[-W['a'] == W['b']]) print(df.loc[~W['c']]) # On entire - or subset of a - DataFrame: print(df.loc[W.sum(axis=1) > 3]) print(df.loc[W[['a', 'b']].diff(axis=1)['b'] > 1]) |
稍微不那么愚蠢的用法示例:
1 | data = pd.read_csv('ugly_db.csv').loc[~(W == '$null$').any(axis=1)] |
顺便说一句:即使在只使用布尔列的情况下,
1 | df.loc[W['cond1']].loc[W['cond2']] |
效率比
1 | df.loc[W['cond1'] & W['cond2']] |
因为它只在
免责声明:我第一次在别处给出这个答案是因为我没有看到这个。
This is unappealing as it requires I assign
df to a variable before being able to filter on its values.
1 | df[df["column_name"] != 5].groupby("other_column_name") |
似乎有效:您也可以链接
只想添加一个使用
下面的代码可以按值筛选行。
1 | df_filtered = df.loc[df['column'] == value] |
通过稍微修改它,您也可以过滤列。
1 | df_filtered = df.loc[df['column'] == value, ['year', 'column']] |
那么为什么我们要一个链式方法呢?答案是,如果您有许多操作,那么阅读起来很简单。例如,
1 2 3 4 | res = df\ .loc[df['station']=='USA', ['TEMP', 'RF']]\ .groupby('year')\ .agg(np.nanmean) |
如果将列设置为搜索索引,则可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import pandas as pd import numpy as np np.random.seed([3,1415]) df = pd.DataFrame( np.random.randint(3, size=(10, 5)), columns=list('ABCDE') ) df # Out[55]: # A B C D E # 0 0 2 2 2 2 # 1 1 1 2 0 2 # 2 0 2 0 0 2 # 3 0 2 2 0 1 # 4 0 1 1 2 0 # 5 0 0 0 1 2 # 6 1 0 1 1 1 # 7 0 0 2 0 2 # 8 2 2 2 2 2 # 9 1 2 0 2 1 df.set_index(['A', 'D']).xs([0, 2]).reset_index() # Out[57]: # A D B C E # 0 0 2 2 2 2 # 1 0 2 1 1 0 |
您还可以利用numpy库进行逻辑操作。速度很快。
1 | df[np.logical_and(df['A'] == 1 ,df['B'] == 6)] |