Pandas DataFrame数据选取:从基础到高级的实战指南298

好的,作为一名专业的程序员,我深知数据选取在数据分析和处理中的核心地位。Pandas DataFrame作为Python数据科学的基石,其强大的数据选取能力是高效工作的关键。下面,我将为您撰写一篇关于Pandas DataFrame数据选取从基础到高级的全面指南,字数控制在1500字左右。
---


在当今数据驱动的世界中,Python凭借其强大的生态系统,特别是Pandas库,已成为数据科学家和分析师的首选工具。Pandas DataFrame是处理结构化数据的核心,而对DataFrame进行高效、精确地数据选取(或称作数据过滤、数据切片)是数据探索、清洗、转换和建模的基础。本文旨在为读者提供一份从入门到精通的Pandas DataFrame数据选取指南,涵盖各种常用的方法、高级技巧以及最佳实践,助您在数据处理的道路上如虎添翼。


我们将从最基础的列选择和行选择开始,逐步深入到强大的布尔索引、基于标签(`loc`)和基于位置(`iloc`)的选取,以及其他实用方法如`query()`等。通过丰富的代码示例,您将能够透彻理解并熟练运用这些技术。

1. 准备工作:创建示例DataFrame


在深入探讨数据选取方法之前,我们首先创建一个用于演示的Pandas DataFrame。这将帮助我们更好地理解各种操作。
```python
import pandas as pd
import numpy as np
# 创建一个示例DataFrame
data = {
'姓名': ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十', '郑十一', '冯十二'],
'年龄': [25, 30, 22, 35, 28, 40, 29, 33, 27, 31],
'城市': ['北京', '上海', '广州', '深圳', '北京', '上海', '广州', '杭州', '深圳', '北京'],
'薪资': [8000, 12000, 7500, 15000, 9000, 18000, 8500, 11000, 9500, 13000],
'职位': ['工程师', '产品经理', '设计师', '高级工程师', '工程师', '架构师', '设计师', '产品经理', '工程师', '高级工程师'],
'入职年份': [2020, 2018, 2021, 2017, 2019, 2015, 2020, 2019, 2022, 2018],
'绩效评分': [4.5, 4.0, 3.8, 4.8, 4.2, 4.9, 3.9, 4.1, 4.3, 4.6]
}
df = (data)
# 添加一些缺失值,以便演示缺失值处理
[2, '薪资'] =
[5, '城市'] =
[8, '绩效评分'] =
print("原始DataFrame:")
print(df)
print("-" * 50)
```

2. 基于列的数据选取


这是最基础也是最常用的数据选取方式。您可以根据列名来选择一个或多个列。

2.1 选取单个列



选取单个列会返回一个Series对象。
```python
# 方法一:使用方括号和列名 (推荐)
name_series = df['姓名']
print("选取单列 '姓名':")
print(name_series)
print("-" * 50)
# 方法二:使用点运算符 (当列名符合Python变量命名规则时可用,但不推荐用于包含空格或特殊字符的列名)
age_series = df.年龄
print("选取单列 '年龄':")
print(age_series)
print("-" * 50)
```

2.2 选取多个列



选取多个列需要传入一个包含列名的列表,这会返回一个新的DataFrame。
```python
# 选取 '姓名', '城市', '薪资' 三列
selected_columns_df = df[['姓名', '城市', '薪资']]
print("选取多列 '姓名', '城市', '薪资':")
print(selected_columns_df)
print("-" * 50)
```

3. 基于行的数据选取


DataFrame的行选取更为多样,主要依赖于索引和条件过滤。

3.1 通过切片选取行



类似于Python列表的切片操作,可以选取连续的行。这种方法是基于行位置的,且右边界不包含。
```python
# 选取前5行
first_five_rows = df[:5]
print("选取前5行:")
print(first_five_rows)
print("-" * 50)
# 选取第2行到第5行(索引1到4)
rows_1_to_4 = df[1:5]
print("选取索引1到4的行:")
print(rows_1_to_4)
print("-" * 50)
```

3.2 使用 `head()`, `tail()`, `sample()`



这些是快速查看DataFrame顶部、底部或随机行的实用方法。
```python
# 查看前3行
print("使用 (3):")
print((3))
print("-" * 50)
# 查看后2行
print("使用 (2):")
print((2))
print("-" * 50)
# 随机选取3行
print("使用 (3):")
print((3))
print("-" * 50)
```

4. 强大的布尔索引(条件过滤)


布尔索引是Pandas中最强大、最灵活的数据选取方法之一,它允许您根据行中的值来过滤数据。其核心是创建一个与DataFrame行数相同的布尔Series,其中`True`表示保留该行,`False`表示舍弃该行。

4.1 单条件过滤


```python
# 选取年龄大于30的员工
older_employees = df[df['年龄'] > 30]
print("年龄大于30的员工:")
print(older_employees)
print("-" * 50)
# 选取城市为北京的员工
beijing_employees = df[df['城市'] == '北京']
print("城市为北京的员工:")
print(beijing_employees)
print("-" * 50)
```

4.2 多条件组合过滤



可以使用逻辑运算符 `&`(与)、`|`(或)、`~`(非)来组合多个条件。注意: 每个条件表达式必须用括号括起来,以确保正确的运算顺序。
```python
# 选取年龄大于30且薪资大于10000的员工
condition_and = df[(df['年龄'] > 30) & (df['薪资'] > 10000)]
print("年龄大于30且薪资大于10000的员工:")
print(condition_and)
print("-" * 50)
# 选取城市为北京或上海的员工
condition_or = df[(df['城市'] == '北京') | (df['城市'] == '上海')]
print("城市为北京或上海的员工:")
print(condition_or)
print("-" * 50)
# 选取不是北京的员工
condition_not = df[~(df['城市'] == '北京')]
print("不是北京的员工:")
print(condition_not)
print("-" * 50)
```

4.3 `isin()` 方法



当您需要检查列中的值是否在某个列表中时,`isin()`方法非常有用。
```python
# 选取城市为北京、上海或广州的员工
cities_to_check = ['北京', '上海', '广州']
isin_employees = df[df['城市'].isin(cities_to_check)]
print("城市为北京、上海或广州的员工:")
print(isin_employees)
print("-" * 50)
```

4.4 字符串方法(`()`, `()`, `()` 等)



对于字符串类型的列,Pandas提供了丰富的字符串方法进行过滤。
```python
# 选取职位包含“工程师”的员工
engineer_employees = df[df['职位'].('工程师', na=False)] # na=False 处理可能存在的NaN
print("职位包含'工程师'的员工:")
print(engineer_employees)
print("-" * 50)
# 选取姓名以“李”开头的员工
li_employees = df[df['姓名'].('李', na=False)]
print("姓名以'李'开头的员工:")
print(li_employees)
print("-" * 50)
```

4.5 处理缺失值(`isnull()`, `notnull()`)



可以利用这两个方法来选取包含或不包含缺失值的行。
```python
# 选取薪资缺失的员工
missing_salary = df[df['薪资'].isnull()]
print("薪资缺失的员工:")
print(missing_salary)
print("-" * 50)
# 选取城市不缺失的员工
non_missing_city = df[df['城市'].notnull()]
print("城市不缺失的员工:")
print(non_missing_city)
print("-" * 50)
```

5. `loc` 和 `iloc` 的深度解析


`loc`(label-location based)和 `iloc`(integer-location based)是Pandas DataFrame最推荐和最强大的数据选取器,它们允许您同时选择行和列,并提供清晰、明确的意图。

5.1 `loc`:基于标签的选取



`loc` 使用行标签和列标签(即索引和列名)进行选取。它的语法是 `[行选择器, 列选择器]`。行和列选择器可以是单个标签、标签列表、标签切片或布尔Series。注意: `loc` 的切片是包含结束点的。
```python
# 选取索引为0, 2, 5的行,以及 '姓名' 和 '薪资' 列
print("使用 loc 选取特定行和列:")
print([[0, 2, 5], ['姓名', '薪资']])
print("-" * 50)
# 选取从索引1到4(包含)的所有行,以及从 '年龄' 到 '职位'(包含)的所有列
print("使用 loc 进行标签切片:")
print([1:4, '年龄':'职位'])
print("-" * 50)
# 结合布尔索引和loc:选取年龄大于30的员工,并显示他们的姓名、城市和薪资
print("loc 结合布尔索引:")
print([df['年龄'] > 30, ['姓名', '城市', '薪资']])
print("-" * 50)
```

5.2 `iloc`:基于位置的选取



`iloc` 使用整数位置(从0开始)进行选取。它的语法是 `[行位置选择器, 列位置选择器]`。行和列选择器可以是单个整数、整数列表或整数切片。注意: `iloc` 的切片是不包含结束点的(类似于Python列表切片)。
```python
# 选取第0, 2, 5行,以及第0 ('姓名') 和第3 ('薪资') 列
print("使用 iloc 选取特定行和列 (基于位置):")
print([[0, 2, 5], [0, 3]])
print("-" * 50)
# 选取从第1到第4行(不包含第4行),以及从第1到第4列(不包含第4列)
print("使用 iloc 进行位置切片:")
print([1:4, 1:4]) # 对应于索引1,2,3行 和 索引1,2,3列
print("-" * 50)
# iloc 不直接支持布尔Series作为行选择器,但可以通过先过滤再使用 iloc 达到类似效果
# 选取年龄大于30的员工,并显示他们的第0、2、3列 (姓名、城市、薪资)
filtered_df_for_iloc = df[df['年龄'] > 30]
print("iloc 结合布尔索引 (先过滤):")
print([:, [0, 2, 3]]) # 所有行,以及0、2、3列
print("-" * 50)
```

6. 其他高级选取方法

6.1 `query()` 方法



`query()` 方法提供了一种类似SQL的语法,可以使用字符串表达式进行DataFrame的行过滤。对于复杂的条件,它有时能提高代码的可读性。
```python
# 选取年龄大于30且薪资大于10000的员工
print("使用 query() 方法:")
print(('年龄 > 30 and 薪资 > 10000'))
print("-" * 50)
# 使用变量进行查询
min_age = 30
max_salary = 15000
print("query() 方法中使用变量:")
print(('年龄 > @min_age and 薪资 < @max_salary')) # 注意变量前加 '@'
print("-" * 50)
```

6.2 `at()` 和 `iat()`:单值快速选取



当您需要选取或修改DataFrame中的单个值时,`at()`(基于标签)和 `iat()`(基于位置)是最高效的方法。
```python
# 获取第0行 '姓名' 列的值
print(f"[0, '姓名']: {[0, '姓名']}")
print(f"[0, 0]: {[0, 0]}") # 获取第0行第0列的值
# 修改单个值
[0, '薪资'] = 8500
print("修改后的 [0, '薪资']:", [0, '薪资'])
print("-" * 50)
```

7. 最佳实践与注意事项

7.1 避免链式索引(`SettingWithCopyWarning`)



这是Pandas初学者常遇到的一个问题。当您通过链式索引(例如 `df[df['col_A'] > 0]['col_B'] = value`)进行赋值时,可能会遇到 `SettingWithCopyWarning`。这是因为第一个选取操作可能返回了一个DataFrame的"视图"而不是"副本",接着对视图进行赋值可能不会作用到原始DataFrame,或者在某些情况下会生效,导致行为不确定。


解决方法: 始终使用 `loc` 或 `iloc` 来同时指定行和列,以确保明确地操作原始DataFrame的特定位置。
```python
# 错误示范 (可能引发 SettingWithCopyWarning)
# df[df['城市'] == '北京']['薪资'] = 9999
# 正确示范:使用 loc
[df['城市'] == '北京', '薪资'] = 9999
print("修改北京员工薪资为9999 (正确姿势):")
print(df[df['城市'] == '北京'])
print("-" * 50)
```

7.2 性能考量



对于大型数据集,选择高效的选取方法至关重要:

避免显式循环: 尽可能使用Pandas的向量化操作(如布尔索引、`apply()`配合向量化函数),而不是Python的`for`循环,因为向量化操作通常由C语言实现,速度更快。
`loc`/`iloc` 优先: 它们是Pandas官方推荐的选取器,通常比直接使用 `df[]` 进行复杂条件过滤更高效和清晰。
`at`/`iat` 用于单值: 当只需访问或修改单个元素时,`at`/`iat` 比 `loc`/`iloc` 更快。
`query()` 的适用性: `query()` 在某些情况下可能比布尔索引稍慢,但对于可读性要求高的复杂字符串条件,它的优势明显。

7.3 代码可读性



清晰的代码注释、有意义的变量名以及适当的代码结构都能提高数据选取逻辑的可读性。对于复杂的布尔条件,可以将其分解为多个子条件,并用变量存储,最后再组合。

8. 总结


掌握Pandas DataFrame的数据选取是进行高效数据分析和处理的基石。本文详细介绍了从基础的列选取、行切片,到强大的布尔索引、`loc`和`iloc`的标签/位置选取,再到`query()`、`at()`/`iat()`等高级方法。我们还强调了避免链式索引和关注性能的最佳实践。


通过不断实践和结合具体的数据场景,您将能够灵活运用这些工具,从海量数据中精准地提取所需信息,为后续的数据分析工作打下坚实的基础。希望这篇指南能帮助您在Pandas数据处理的旅程中走得更远。
---

2025-10-10


上一篇:Python 文件查找终极指南:从基础到高级,全面掌握文件系统操作

下一篇:Python量化交易实战:从零开始构建均线策略与可视化