跳到主要内容

数据选择与过滤

Pandas 提供了多种灵活的数据选择方式,包括标签选择、位置选择、布尔索引等。掌握这些方法可以高效地操作数据。

基础选择

选择列

import pandas as pd
import numpy as np

df = pd.DataFrame({
'name': ['张三', '李四', '王五', '赵六', '钱七'],
'age': [25, 30, 35, 28, 40],
'city': ['北京', '上海', '广州', '深圳', '杭州'],
'salary': [8000, 12000, 15000, 10000, 20000]
})

# 选择单列 - 返回 Series
col = df['name']
print(type(col)) # <class 'pandas.core.series.Series'>

# 点号访问(仅当列名是有效 Python 标识符时)
col = df.name

# 选择多列 - 返回 DataFrame
cols = df[['name', 'age']]
print(type(cols)) # <class 'pandas.core.frame.DataFrame'>

# 注意:以下方式返回的是行,不是列
# df[0] # 错误!
# df[['name', 'age']] # 这是选择列,不是行

选择行

# 使用切片选择行(基于位置)
rows = df[0:3] # 选择前3行

# 使用布尔索引选择行
rows = df[df['age'] > 30]

# 注意:以下方式选择列,不是行
# df['name'] # 这是选择列

使用 loc 选择数据

loc 基于标签进行选择,包含结束边界。

选择行

# 设置索引
df_indexed = df.set_index('name')

# 选择单行
row = df_indexed.loc['张三']
print(row)
# age 25
# city 北京
# salary 8000

# 选择多行
rows = df_indexed.loc['张三':'王五'] # 包含王五

# 选择不连续的行
rows = df_indexed.loc[['张三', '王五', '钱七']]

选择列

# 选择单列
col = df.loc[:, 'name']

# 选择多列
cols = df.loc[:, ['name', 'age']]

# 选择连续列
cols = df.loc[:, 'name':'city'] # 包含 city

同时选择行和列

# 选择特定行和列
result = df.loc[0:2, ['name', 'age']]

# 使用条件选择行,并选择特定列
result = df.loc[df['age'] > 30, ['name', 'salary']]

# 复杂条件
result = df.loc[(df['age'] > 25) & (df['salary'] > 10000),
['name', 'city', 'salary']]

修改数据

# 修改单个值
df.loc[0, 'age'] = 26

# 修改整行
df.loc[0] = ['张三', 26, '北京', 8500]

# 修改整列
df.loc[:, 'salary'] = df['salary'] * 1.1 # 涨薪 10%

# 条件修改
df.loc[df['age'] > 30, 'salary'] = df['salary'] * 1.2

使用 iloc 选择数据

iloc 基于整数位置进行选择,不包含结束边界(与 Python 切片一致)。

选择行

# 选择单行
row = df.iloc[0] # 第一行

# 选择多行
rows = df.iloc[0:3] # 第0、1、2行(不包含第3行)

# 选择不连续的行
rows = df.iloc[[0, 2, 4]]

# 负数索引
row = df.iloc[-1] # 最后一行
rows = df.iloc[-3:] # 最后3行

选择列

# 选择单列
col = df.iloc[:, 0] # 第一列

# 选择多列
cols = df.iloc[:, 0:3] # 第0、1、2列

# 选择不连续的列
cols = df.iloc[:, [0, 2, 3]]

# 负数索引
cols = df.iloc[:, -2:] # 最后两列

同时选择行和列

# 选择特定区域
result = df.iloc[0:3, 0:2] # 前3行,前2列

# 选择不连续的行和列
result = df.iloc[[0, 2, 4], [0, 2]]

# 使用切片
result = df.iloc[::2, :] # 每隔一行
result = df.iloc[:, ::2] # 每隔一列

布尔索引(条件过滤)

基础条件

# 单一条件
df[df['age'] > 30] # age > 30 的行
df[df['city'] == '北京'] # city 为北京的行
df[df['name'].str.contains('张')] # name 包含"张"的行

# 多条件(使用 & | ~)
df[(df['age'] > 25) & (df['salary'] > 10000)] # 且
df[(df['city'] == '北京') | (df['city'] == '上海')] # 或
df[~(df['age'] > 30)] # 非

# 注意:每个条件要用括号括起来
# df[df['age'] > 25 & df['salary'] > 10000] # 错误!

常用条件方法

# isin - 判断是否在给定值中
df[df['city'].isin(['北京', '上海', '广州'])]

# between - 判断是否在范围内
df[df['age'].between(25, 35)]
df[df['salary'].between(10000, 15000)]

# isnull / notnull - 判断缺失值
df[df['age'].isnull()] # age 为空的行
df[df['age'].notnull()] # age 不为空的行

# str 方法
df[df['name'].str.startswith('张')]
df[df['name'].str.endswith('三')]
df[df['name'].str.contains('张|李', regex=True)]
df[df['name'].str.len() > 2]

# query 方法(更简洁)
df.query('age > 30')
df.query('age > 25 and salary > 10000')
df.query('city in ["北京", "上海"]')
df.query('age > @threshold') # 使用外部变量

过滤函数

# filter - 基于标签过滤
# 过滤列
df.filter(items=['name', 'age'])
df.filter(like='name') # 列名包含 'name'
df.filter(regex='^n') # 列名以 'n' 开头

# 过滤行(需要指定 axis)
df.filter(items=[0, 2, 4], axis=0)

高级选择技巧

使用 where

# where 返回与原 DataFrame 相同形状的结果
# 不满足条件的填充为 NaN(或指定值)
result = df.where(df['age'] > 30)
result = df.where(df['age'] > 30, other=0) # 不满足条件填充 0

# 与 numpy where 的区别
import numpy as np
result = np.where(df['age'] > 30, df['salary'], 0)

使用 mask

# mask 与 where 相反
# 满足条件的填充为 NaN(或指定值)
result = df.mask(df['age'] > 30)
result = df.mask(df['age'] > 30, other='Senior')

使用 isin

# 多值匹配
df[df['city'].isin(['北京', '上海'])]

# 多列匹配
df[df[['name', 'city']].isin({'name': ['张三'], 'city': ['北京']}).all(axis=1)]

使用 at 和 iat

# at - 基于标签快速访问单个元素
value = df.at[0, 'name'] # 比 loc 更快
df.at[0, 'age'] = 26

# iat - 基于位置快速访问单个元素
value = df.iat[0, 1] # 比 iloc 更快
df.iat[0, 1] = 26

索引操作

设置索引

# 设置单列索引
df_indexed = df.set_index('name')

# 设置多列索引(层次化索引)
df_indexed = df.set_index(['city', 'name'])

# 就地修改
df.set_index('name', inplace=True)

重置索引

# 重置索引为默认整数索引
df_reset = df.reset_index()

# 保留原索引作为列
df_reset = df.reset_index(drop=False)

# 丢弃原索引
df_reset = df.reset_index(drop=True)

# 对于层次化索引
df_reset = df.reset_index(level='city') # 重置特定级别

重新索引

# 重新索引(添加/删除行,填充缺失值)
df_reindexed = df.reindex([0, 1, 2, 5, 6])

# 填充缺失值
df_reindexed = df.reindex([0, 1, 2, 5, 6], fill_value=0)
df_reindexed = df.reindex([0, 1, 2, 5, 6], method='ffill') # 前向填充

# 重新索引列
df_reindexed = df.reindex(columns=['name', 'age', 'gender'])

实战示例

示例 1:复杂条件筛选

# 筛选条件:年龄大于25且薪资大于10000,或者城市是北京
condition = ((df['age'] > 25) & (df['salary'] > 10000)) | (df['city'] == '北京')
result = df[condition]

# 使用 query 更简洁
result = df.query('(age > 25 and salary > 10000) or city == "北京"')

示例 2:数据抽样

# 随机抽样
sample = df.sample(n=3) # 随机选3行
sample = df.sample(frac=0.5) # 随机选50%的行
sample = df.sample(n=3, replace=True) # 有放回抽样
sample = df.sample(n=3, random_state=42) # 固定随机种子

# 分层抽样
sample = df.groupby('city').apply(lambda x: x.sample(n=1))

示例 3:选择最大/最小值行

# 薪资最高的3人
top_salary = df.nlargest(3, 'salary')

# 薪资最低的2人
bottom_salary = df.nsmallest(2, 'salary')

# 多列排序后选择
top = df.nlargest(3, ['age', 'salary'])

示例 4:条件赋值

# 根据条件添加新列
df['level'] = 'Junior'
df.loc[df['age'] > 30, 'level'] = 'Senior'
df.loc[df['age'] > 25, 'level'] = 'Mid'

# 使用 np.where
df['level'] = np.where(df['age'] > 30, 'Senior',
np.where(df['age'] > 25, 'Mid', 'Junior'))

# 使用 apply
df['level'] = df['age'].apply(lambda x: 'Senior' if x > 30 else ('Mid' if x > 25 else 'Junior'))

性能对比

方法适用场景性能
df[col]选择单列
df.loc[]标签选择中等
df.iloc[]位置选择中等
df.at[]单个元素(标签)最快
df.iat[]单个元素(位置)最快
布尔索引条件过滤中等
query()复杂条件中等(代码简洁)

常见错误

# 错误 1:链式赋值警告
# df[df['age'] > 30]['salary'] = 0 # 警告!
# 正确做法:
df.loc[df['age'] > 30, 'salary'] = 0

# 错误 2:忘记括号
# df[df['age'] > 30 & df['salary'] > 10000] # 错误!
# 正确做法:
df[(df['age'] > 30) & (df['salary'] > 10000)]

# 错误 3:混淆 loc 和 iloc
# df.loc[0:3] # 包含第3行
df.iloc[0:3] # 不包含第3行

# 错误 4:使用 = 而不是 ==
# df[df['city'] = '北京'] # 错误!
df[df['city'] == '北京'] # 正确

小结

选择数据的方法

  • df[col] / df[[col1, col2]] - 选择列
  • df.loc[] - 基于标签选择
  • df.iloc[] - 基于位置选择
  • df[condition] - 布尔索引
  • df.query() - 查询语句

最佳实践

  • 选择单个元素使用 atiat(更快)
  • 条件赋值使用 loc(避免警告)
  • 复杂条件使用 query(代码更简洁)
  • 多条件使用括号分组

练习

  1. 使用 loc 选择 name 和 age 两列,且 age > 30 的行
  2. 使用 iloc 选择前3行、后2列的数据
  3. 使用布尔索引筛选出城市为北京或上海,且薪资大于10000的记录
  4. 使用 query 方法重写上述条件
  5. 将所有年龄大于30岁的人的薪资增加20%

下一步

掌握了数据选择后,让我们学习 数据清洗