如何通过多个列过滤 pandas dataframes

为了通过单列过滤 dataframe (df) ,如果我们有包含男性和女性的数据,我们可以:

males = df[df[Gender]=='Male']

问题1-但是如果数据跨越多年,而我只想在2014年看到男性呢?

在其他语言中,我可能会这样做:

if A = "Male" and if B = "2014" then

(除非我想这样做,并在一个新的数据框架对象中获得原始数据框架的子集)

问题2。我如何在循环中做到这一点,并为每个独特的年份和性别集创建一个数据框对象(即 df: 2013-男性,2013-女性,2014-男性,和2014-女性

for y in year:


for g in gender:


df = .....
313672 次浏览

使用 &操作符,不要忘记用 ()包装子语句:

males = df[(df[Gender]=='Male') & (df[Year]==2014)]

使用 for 循环将数据帧存储在 dict中:

from collections import defaultdict
dic={}
for g in ['male', 'female']:
dic[g]=defaultdict(dict)
for y in [2013, 2014]:
dic[g][y]=df[(df[Gender]==g) & (df[Year]==y)] #store the DataFrames to a dict of dict

编辑:

getDF的演示:

def getDF(dic, gender, year):
return dic[gender][year]


print genDF(dic, 'male', 2014)

对于希望用作过滤器并依赖于多个列的更一般的布尔函数,可以使用:

df = df[df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)]

其中 f 是一个函数,应用于 col1和 col2中的每一对元素(x1,x2) ,并根据需要的任何条件(x1,x2)返回 True 或 False。

熊猫0.13开始,这是最有效的方法。

df.query('Gender=="Male" & Year=="2014" ')

通过使用 np.logical_and运算符替换 &(或者使用 np.logical_or替换 |) ,可以按多列(多于两列)进行过滤

如果您为多个字段提供目标值,这里有一个执行此任务的示例函数。你可以把它用于不同类型的过滤和诸如此类的东西:

def filter_df(df, filter_values):
"""Filter df by matching targets for multiple columns.


Args:
df (pd.DataFrame): dataframe
filter_values (None or dict): Dictionary of the form:
`{<field>: <target_values_list>}`
used to filter columns data.
"""
import numpy as np
if filter_values is None or not filter_values:
return df
return df[
np.logical_and.reduce([
df[column].isin(target_values)
for column, target_values in filter_values.items()
])
]

用法:

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [1, 2, 3, 4]})


filter_df(df, {
'a': [1, 2, 3],
'b': [1, 2, 4]
})

如果有人想知道什么是最快的过滤方法(被接受的答案还是@redream) :

import pandas as pd
import numpy as np


length = 100_000
df = pd.DataFrame()
df['Year'] = np.random.randint(1950, 2019, size=length)
df['Gender'] = np.random.choice(['Male', 'Female'], length)


%timeit df.query('Gender=="Male" & Year=="2014" ')
%timeit df[(df['Gender']=='Male') & (df['Year']==2014)]

100000行的结果:

6.67 ms ± 557 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5.54 ms ± 536 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

10,000,000行的结果:

326 ms ± 6.52 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
472 ms ± 25.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

所以结果取决于规模和数据。在我的笔记本电脑上,50万行后 query()会更快。而且,Year=="2014"中的字符串搜索有不必要的开销(Year==2014更快)。

您可以使用 pandas中的 query创建自己的过滤器函数。在这里,您可以通过所有 kwargs参数过滤 df结果。不要忘记添加一些验证器(kwargs过滤) ,以获得自己的 df过滤器功能。

def filter(df, **kwargs):
query_list = []
for key in kwargs.keys():
query_list.append(f'{key}=="{kwargs[key]}"')
query = ' & '.join(query_list)
return df.query(query)

因为您要寻找的行基本上满足 Column _ A = ‘ Value _ A’和 Column _ B = ‘ Value _ B’的条件

你可以使用 loc

df = df.loc[df['Column_A'].eq('Value_A') & df['Column_B'].eq('Value_B')]

你可以在这里找到完整的文件 熊猫 Loc

几年后,我又回到了这个问题上,并且可以提出另一个解决方案,当你有很多过滤器包括在内的时候,这是特别好的。我们可以创建几个过滤掩码,然后对这些过滤器进行操作:

>>> df = pd.DataFrame({'gender': ['Male', 'Female', 'Male'],
...                    'married': [True, False, False]})
>>> gender_mask = df['gender'] == 'Male'
>>> married_mask = df['married']
>>> filtered_df = df.loc[gender_mask & married_mask]
>>> filtered_df
gender  married
0   Male     True

也许它不是最短的解决方案,但是它是可读的,并且对于组织代码有很大的帮助。

比亚历克斯的答案好多了

def df_filter(df, **kwargs):
query_list = []
for key, value in kwargs.items():
if value is not None:
query_list.append(f"{key}==@kwargs['{str(key)}']")
query = ' & '.join(query_list)
return df.query(query)

将删除 Nothing 值,这样就可以直接操作一些默认值为 Nothing 的函数 如果值不是字符串,前一个也不起作用,这将对任何类型的参数起作用

我的数据框架有25列,我希望将来可以自由选择任何类型的过滤器(参数数量,条件数量)。 我用这个:

def flex_query(params):
res = load_dataframe()
if type(params) is not list:
return None
for el in params:
res = res.query(f"{el[0]} {el[1]} {el[2]}")
return res

并称之为:

res = flex_query([['DATE','==', '"2022-09-26"'],['LEVEL','>=',2], ['PERCENT','>',10.2]])

Where 'DATE', 'LEVEL', 'PERCENT' - column names. As you can see, here are very flexible query method with several params and different type of conditions. This method gives me possibility to compare int, float, string - 'all in one'