Pandas 索引系统精讲
Pandas 的数据选择是整个库中最容易让初学者困惑的部分,因为有多种方式可以完成"同一件事",但背后的语义和性能差异显著。
df[col] 或 df[[cols]]
按列名选择,df['A'] 返回 Series,df[['A', 'B']] 返回 DataFrame。注意不能用于行选择。
df.loc[行标签, 列标签]
基于标签的索引。行和列都用标签(名称)来指定。切片时两端都是闭合区间:
df.loc[0:3] 包含第0、1、2、3行(如果索引是整数)。df.iloc[行位置, 列位置]
基于位置的索引(integer location)。始终使用整数位置,不管索引是什么类型。切片遵循 Python 惯例:
df.iloc[0:3] 包含第0、1、2行(不含第3行)。import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Carol', 'Dave'],
'age': [25, 30, 27, 35],
'salary': [50000, 60000, 55000, 70000],
'dept': ['Dev', 'Dev', 'HR', 'Dev']
}, index=['a', 'b', 'c', 'd']) # 注意:使用字符串索引
# loc:按标签
df.loc['a'] # 选择索引标签为 'a' 的行
df.loc['a':'c'] # 选择 a 到 c 的行(含 c!)
df.loc[:, 'age':'salary'] # 选择 age 到 salary 的列
df.loc['a', 'name'] # 单个元素:第 'a' 行,'name' 列
# iloc:按整数位置
df.iloc[0] # 第 0 行(位置,不是标签)
df.iloc[0:3] # 第 0、1、2 行(不含第 3 行!)
df.iloc[:, 1:3] # 第 1、2 列('age' 和 'salary')
df.iloc[-1] # 最后一行
布尔索引
# 单条件
df[df['age'] > 27] # 年龄大于 27 的行
df[df['dept'] == 'Dev'] # 部门为 Dev 的行
# 多条件:必须用 & (and) | (or) ~ (not),不能用 and/or/not
df[(df['dept'] == 'Dev') & (df['salary'] > 55000)]
df[(df['age'] < 28) | (df['dept'] == 'HR')]
# isin:检查是否在指定集合中
df[df['dept'].isin(['Dev', 'QA'])]
# between:数值范围过滤(含两端)
df[df['salary'].between(50000, 65000)]
query 方法:更简洁的过滤语法
# query() 接受字符串表达式,更接近 SQL 风格
df.query("dept == 'Dev' and salary > 55000")
# 引用 Python 变量:用 @ 前缀
min_salary = 55000
dept_filter = 'Dev'
df.query("dept == @dept_filter and salary > @min_salary")
# 列名含空格时用反引号
df.query("`employee name` == 'Alice'")
Copy-on-Write 与链式操作安全
Pandas 2.0 引入了 Copy-on-Write(CoW) 语义,默认在 2.2 开启。这改变了链式赋值的行为:
# ❌ Pandas 1.x 中可能有效,但语义不清晰
df[df['age'] > 25]['salary'] = 0 # CoW 下不会修改原 df!
# ✅ 正确方式一:用 loc 定位后赋值
df.loc[df['age'] > 25, 'salary'] = 0
# ✅ 正确方式二:用 where/mask
df['salary'] = df['salary'].where(df['age'] <= 25, other=0)
# ✅ 正确方式三:用 assign 返回新 DataFrame(函数式风格,推荐)
df_new = df.assign(salary=df['salary'].where(df['age'] <= 25, 0))
CoW 的设计哲学
Copy-on-Write 使链式操作的语义变得清晰和可预测:任何返回新 DataFrame 的操作都创建独立副本,不会意外修改原数据。这消除了 SettingWithCopyWarning 警告,代码行为更安全。代价是某些就地修改需要用 .loc 重写。
本章小结
- loc 用标签,iloc 用位置;loc 切片含两端,iloc 切片遵循 Python 惯例(不含右端)
- 布尔索引多条件用 & | ~,不能用 and/or/not
- query() 方法让过滤语法更简洁,可用 @ 引用外部变量
- CoW 下链式赋值不生效,用 loc 或 assign 进行安全的数据修改