Pandas 数据选择与过滤:全面指南与实战技巧

在数据分析和处理过程中,高效地选择和过滤数据是最基础也是最重要的操作之一。Pandas 作为 Python 数据分析的核心库,提供了丰富而灵活的数据选择与过滤方法。本文将全面介绍 Pandas 中的数据选择与过滤技巧,从基础操作到高级应用,帮助您掌握这一关键技能。

一、Pandas 数据结构回顾

在深入探讨数据选择与过滤之前,让我们先简要回顾一下 Pandas 的两种核心数据结构:

  1. Series:一维数组,带有标签的轴(索引)

  2. DataFrame:二维表格型数据结构,包含行索引和列标签

理解这两种数据结构的特点对于掌握数据选择方法至关重要,因为不同的选择方法可能返回不同类型的对象。

二、基础选择方法

1. 列选择

选择列是 Pandas 中最简单的操作之一,有以下几种方式:

# 选择单列(返回Series)
single_column = df['column_name']

# 选择多列(返回DataFrame)
multiple_columns = df[['col1', 'col2', 'col3']]

# 使用点记号(仅适用于列名是有效的Python标识符时)
single_column_dot = df.column_name

注意:点记号方法虽然简洁,但不推荐在生产代码中使用,因为它无法处理列名中有空格或与Python关键字冲突的情况。

2. 行选择

Pandas 提供了多种行选择方法,主要分为基于标签和基于整数位置两种方式。

基于标签的选择 (loc)

# 选择单行
single_row = df.loc['row_label']

# 选择多行
multiple_rows = df.loc[['row1', 'row2', 'row3']]

# 选择行范围
row_range = df.loc['row1':'row4']  # 包含结束行

基于整数位置的选择 (iloc)

# 选择单行
single_row = df.iloc[0]

# 选择多行
multiple_rows = df.iloc[[0, 2, 4]]

# 选择行范围
row_range = df.iloc[1:4]  # 不包含结束位置

三、布尔索引与条件过滤

布尔索引是 Pandas 中最强大且常用的数据过滤方法,它允许我们根据条件表达式选择数据。

1. 基本布尔索引

# 单条件过滤
filtered_df = df[df['column'] > value]

# 示例:选择年龄大于30的记录
older_than_30 = df[df['Age'] > 30]

2. 多条件组合

# AND条件(使用&)
filtered_df = df[(df['col1'] > value1) & (df['col2'] < value2)]

# OR条件(使用|)
filtered_df = df[(df['col1'] > value1) | (df['col2'] < value2)]

# NOT条件(使用~)
filtered_df = df[~(df['column'] == value)]

# 示例:选择年龄大于30且来自NY的记录
ny_adults = df[(df['Age'] > 30) & (df['City'] == 'NY')]

重要提示:在使用多条件组合时,必须用括号将每个条件括起来,否则会因为运算符优先级问题导致错误。

3. isin() 方法

当需要检查值是否在一组可能的值中时,isin() 方法非常有用:

# 选择城市为NY或LA的记录
cities = ['NY', 'LA']
filtered_df = df[df['City'].isin(cities)]

# 也可以用于排除某些值
excluded_cities = ['Chicago']
filtered_df = df[~df['City'].isin(excluded_cities)]

4. between() 方法

对于范围检查,between() 方法比组合两个不等式更简洁:

# 选择年龄在25到35之间的记录
filtered_df = df[df['Age'].between(25, 35)]

# 包含参数可以控制是否包含边界
filtered_df = df[df['Age'].between(25, 35, inclusive='neither')]

四、高级选择技巧

1. loc 和 iloc 的行列混合选择

loc 和 iloc 不仅可以用于行选择,还可以同时指定行和列:

# 使用loc选择特定行和列
subset = df.loc[rows_selection, columns_selection]

# 示例:选择年龄>30的记录的姓名和城市列
result = df.loc[df['Age'] > 30, ['Name', 'City']]

# 使用iloc基于位置选择
result = df.iloc[1:4, 0:2]  # 选择第2-4行,第1-2列

2. query() 方法

query() 方法提供了一种更直观的字符串表达式方式来过滤数据:

# 基本查询
result = df.query('Age > 30')

# 多条件查询
result = df.query('Age > 30 and City == "NY"')

# 使用变量
min_age = 30
result = df.query('Age > @min_age')  # 注意使用@符号引用变量

query() 方法的优势在于语法简洁,特别是对于复杂条件,而且通常性能也更好。

3. where() 和 mask() 方法

where() 和 mask() 方法提供了条件替换的功能:

# where(): 不满足条件的值替换为NaN(或其他指定值)
result = df.where(df > 0)  # 保留大于0的值,其他为NaN

# mask(): 满足条件的值替换为NaN
result = df.mask(df > 0)  # 大于0的值变为NaN,其他保留

这两个方法不会过滤掉数据,而是保留原始DataFrame的形状,只是替换不符合条件的值。

4. 字符串方法过滤

对于文本数据,Pandas 通过 str 访问器提供了丰富的字符串操作:

# 以特定前缀开头
filtered_df = df[df['Name'].str.startswith('A')]

# 包含特定子串
filtered_df = df[df['Name'].str.contains('lie')]

# 正则表达式匹配
filtered_df = df[df['Name'].str.match('^C.*e$')]

# 字符串长度过滤
filtered_df = df[df['Name'].str.len() > 4]

5. 按数据类型选择

有时我们需要根据列的数据类型进行选择:

# 选择数值型列
numeric_cols = df.select_dtypes(include=['number'])

# 排除数值型列
non_numeric_cols = df.select_dtypes(exclude=['number'])

五、性能优化技巧

在处理大型数据集时,选择方法的性能变得尤为重要。以下是一些优化建议:

1. 避免链式索引

链式索引是指连续使用多个索引操作,如:

# 不推荐的链式索引
result = df[df['Age'] > 30]['Name']

# 推荐的做法
result = df.loc[df['Age'] > 30, 'Name']

链式索引可能会导致不可预测的行为,并且通常性能较差。

2. 使用query()提高性能

对于复杂条件,query() 方法通常比布尔索引更快,特别是对于大型DataFrame:

# 比传统的布尔索引更快
result = df.query('Age > 30 & City == "NY"')

3. 使用numpy.where进行条件选择

对于非常大型的数据集,可以考虑使用numpy的where函数:

import numpy as np

condition = df['Age'] > 30
result = df[np.where(condition, True, False)]

4. 使用eval()进行表达式求值

对于复杂的数值运算,eval() 可以提供性能提升:

result = df.eval('(A + B) / (C - D)')

六、实际应用案例

让我们通过一个完整的示例来演示这些技巧的综合应用:

import pandas as pd
import numpy as np

# 创建示例数据集
data = {
    'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace', 'Heidi'],
    'Age': [25, 32, 18, 47, 29, 31, 23, 50],
    'City': ['NY', 'LA', 'Chicago', 'NY', 'LA', 'Chicago', 'NY', 'LA'],
    'Salary': [70000, 85000, 45000, 120000, 65000, 95000, 55000, 110000],
    'Department': ['HR', 'Engineering', 'Marketing', 'Engineering', 
                  'HR', 'Engineering', 'Marketing', 'Engineering']
}
df = pd.DataFrame(data)

# 案例1:选择年龄在25-40之间且工资高于平均工资的工程师
avg_salary = df['Salary'].mean()
case1 = df[
    (df['Age'].between(25, 40)) & 
    (df['Salary'] > avg_salary) & 
    (df['Department'] == 'Engineering')
]

# 案例2:使用query方法实现相同功能
case2 = df.query('25 <= Age <= 40 and Salary > @avg_salary and Department == "Engineering"')

# 案例3:选择名字长度大于4且不以元音字母开头的员工
case3 = df[
    df['Name'].str.len() > 4
].loc[
    ~df['Name'].str.lower().str.startswith(('a', 'e', 'i', 'o', 'u'))
]

# 案例4:按部门和城市分组统计平均工资
summary = df.groupby(['Department', 'City'])['Salary'].mean().unstack()

七、常见问题与解决方案

1. SettingWithCopyWarning 警告

这是一个常见的警告,通常发生在修改链式索引的结果时。解决方案是明确使用loc或iloc:

# 可能引发警告的方式
df[df['Age'] > 30]['Salary'] = 0

# 正确的方式
df.loc[df['Age'] > 30, 'Salary'] = 0

2. 处理缺失值的选择

# 选择非空值
non_null = df[df['column'].notna()]

# 选择空值
null_values = df[df['column'].isna()]

3. 多条件过滤时的括号问题

# 错误:缺少括号
df[df['A'] > 0 & df['B'] < 0]  # 会报错

# 正确
df[(df['A'] > 0) & (df['B'] < 0)]

4. 分类数据的过滤

对于分类数据(categorical data),使用isin()通常比直接比较更高效:

# 对于分类变量
df[df['Category'].isin(['A', 'B'])]  # 比 == 更高效

八、总结

Pandas 提供了丰富多样的数据选择与过滤方法,从简单的列选择到复杂的多条件过滤,可以满足各种数据分析需求。掌握这些技巧的关键在于:

  1. 理解不同方法适用的场景

  2. 熟悉布尔索引和多条件组合

  3. 了解性能优化的方法

  4. 避免常见的陷阱和错误

在实际工作中,建议:

  • 对于简单选择,直接使用列索引或loc/iloc

  • 对于复杂条件过滤,考虑使用query()方法

  • 对于大型数据集,关注性能优化技巧

  • 始终注意避免链式索引

通过灵活运用这些方法,您可以高效地从复杂的数据集中提取所需信息,为后续的分析和建模打下坚实基础。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值