pandas loc 与 iloc (Label vs Position Indexing)

1. 定义

lociloc 是 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/arrayiloc 不支持布尔 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,“列标签”会去查 columns
  • iloc 直接把整数当作”第几行/第几列”,从 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. 对比辨析表

维度lociloc
索引依据标签(index/column 名)整数位置(0 起的序号)
切片区间闭区间 a:cc左闭右开 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. 常见误区

  • ❌ “lociloc 完全可以互换” → 错。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() — 字符串表达式筛选行,等价于布尔索引 loc
  • df.set_index() / reset_index() — 改 index 后 loc 的行为会变,注意区分
  • SettingWithCopyWarning — pandas 经典警告,根源就是链式索引;统一用 loc 一次性赋值即可避免
  • 官方文档:Indexing and selecting data