pandas loc 与 iloc (Label vs Position Indexing)
1. 定义
loc 和 iloc 是 pandas DataFrame/Series 的两套索引器:
loc—— 基于**标签(label)**索引行和列iloc—— 基于**整数位置(integer position)**索引行和列
形象记法:
loc= “location by label”,用”门牌号”找;iloc= “integer location”,用”第几个”找。门牌号可能重复也可能跳跃,但”第 0 个、第 1 个”永远从 0 数起、连续不断。
2. 核心概念
| 概念 | 含义 | 备注 |
|---|---|---|
标签索引 loc | 行/列用 index/column 名字 | 切片闭区间,包含右端 |
位置索引 iloc | 行/列用 0 起的整数位置 | 切片左闭右开,和 Python 切片一致 |
| 布尔索引 | loc 支持传布尔 Series/array | iloc 不支持布尔 list 之外的非整数标签 |
| 列表索引 | 传 list 同时取多行多列 | 顺序即输出顺序 |
: | 全选该轴 | df.loc[:, 'a'] 取整列 |
最容易踩的坑:当 index 是默认
0,1,2,...时,loc[0]和iloc[0]结果一样,让人以为两者等价——一旦 index 被改成'a','b','c'或乱序数字,区别立刻显现。
3. 工作原理
两套索引器都按 [行选择, 列选择] 两轴工作,遵循”先选行再选列”的二维语法:
DataFrame
col_a col_b col_c
idx_1 1 2 3
idx_2 4 5 6
idx_3 7 8 9
loc['idx_2', 'col_b'] → 5 (标签)
iloc[1, 1] → 5 (位置)
loc拿到的”行标签”会去查 index,“列标签”会去查 columnsiloc直接把整数当作”第几行/第几列”,从 0 数起,不考虑标签是什么
4. 基本用法
import pandas as pd
df = pd.DataFrame(
{"name": ["Alice", "Bob", "Carol"],
"age": [25, 30, 35]},
index=["a", "b", "c"]
)
# ── loc:标签索引 ──
df.loc["a"] # 行标签 'a',返回 Series
df.loc["a", "name"] # 单个值 → "Alice"
df.loc["a":"c"] # 切片闭区间,含 'c'
df.loc["a":"b", "name"] # 行切片 + 单列
df.loc[:, ["name", "age"]] # 全行 + 多列
df.loc[df["age"] > 28] # 布尔索引,筛出 age>28 的行
df.loc[df["age"] > 28, "name"] = "X" # 条件赋值,常用
# ── iloc:位置索引 ──
df.iloc[0] # 第 0 行
df.iloc[0, 1] # 第 0 行第 1 列 → 25
df.iloc[0:2] # 左闭右开,不含第 2 行
df.iloc[0:2, 0:1] # 行列都切片
df.iloc[[0, 2]] # 取第 0、2 行
df.iloc[-1] # 最后一行,支持负索引
# ── 修改:loc 比较安全 ──
df.loc["a", "age"] = 26 # 用标签定位修改切片行为对比:
df.loc["a":"c"]含'c'行(闭区间);df.iloc[0:2]不含第 2 行(左闭右开)。这是两者最直观的差异之一。
5. 对比辨析表
| 维度 | loc | iloc |
|---|---|---|
| 索引依据 | 标签(index/column 名) | 整数位置(0 起的序号) |
| 切片区间 | 闭区间 a:c 含 c | 左闭右开 0:2 不含 2 |
| 支持布尔索引 | ✅ 支持 df.loc[mask] | ⚠️ 仅支持布尔 list/array,不支持布尔 Series |
| 列选择 | 用列名 / 列名列表 | 用整数位置 |
| 负索引 | ❌ 不支持 | ✅ iloc[-1] 取最后 |
| 新增行/列 | ✅ df.loc["d"] = ... | ❌ 不能用于扩展 |
| 赋值修改 | ✅ 推荐 | ⚠️ 可用但易错 |
| 当 index 默认 0,1,2… | 与 iloc 看起来一样 | 与 loc 看起来一样 |
6. 典型工作流
# 场景 1:条件筛选 + 列赋值(最常用模式)
df.loc[df["age"] < 30, "category"] = "young"
df.loc[df["age"] >= 30, "category"] = "senior"
# 场景 2:按列名子集化
subset = df.loc[:, ["name", "age"]]
# 场景 3:按位置取前 N 行(不知道列名时)
head = df.iloc[:5]
# 场景 4:取最后一列的整列
last_col = df.iloc[:, -1]
# 场景 5:reset_index 后混用
df2 = df.reset_index(drop=True) # index 变成 0,1,2
df2.loc[0] # 用标签 0
df2.iloc[0] # 用位置 0,结果相同7. 常见误区
- ❌ “
loc和iloc完全可以互换” → 错。index 是字符串或乱序时,loc[0]会报 KeyError,iloc[0]才是取第一行 - ❌ “
loc[a:b]和 Python 切片一样不含 b” → 错,loc是闭区间,含右端;iloc才是左闭右开 - ❌ “
iloc支持布尔 Series” → 严格说不支持,df.iloc[df['x']>0]会报错;要用df.loc[df['x']>0]或传布尔 ndarray - ❌ “用
df[col]选列就够了,不用 loc/iloc” → 仅当选一列且不涉及行筛选时够用;多列、行+列同时选、条件赋值都需要loc - ❌ “
df.loc[5] = ...是在第 5 行位置插入” → 错,是给标签为5的行赋值;如果 index 里没有5,会新增一行,而不是修改第 6 行 - ❌ “链式赋值
df[df['x']>0]['y'] = 1能改原表” → 错,会触发 SettingWithCopyWarning;正确写法是df.loc[df['x']>0, 'y'] = 1
8. 延伸阅读 / 关联概念
df.at/df.iat— 单个标量取值的高速版本,分别对标loc/iloc的单点访问df.query()— 字符串表达式筛选行,等价于布尔索引locdf.set_index()/reset_index()— 改 index 后loc的行为会变,注意区分- SettingWithCopyWarning — pandas 经典警告,根源就是链式索引;统一用
loc一次性赋值即可避免 - 官方文档:Indexing and selecting data