数据选择与过滤
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()- 查询语句
最佳实践:
- 选择单个元素使用
at或iat(更快) - 条件赋值使用
loc(避免警告) - 复杂条件使用
query(代码更简洁) - 多条件使用括号分组
练习
- 使用 loc 选择 name 和 age 两列,且 age > 30 的行
- 使用 iloc 选择前3行、后2列的数据
- 使用布尔索引筛选出城市为北京或上海,且薪资大于10000的记录
- 使用 query 方法重写上述条件
- 将所有年龄大于30岁的人的薪资增加20%
下一步
掌握了数据选择后,让我们学习 数据清洗!