loc 与 iloc:按标签和位置选择
本教程共 54 篇 · 第 19 篇 · 更新于 2026-08-11 · 约 8 分钟阅读
本节目标:分清 loc 和 iloc 的规则,能用它们灵活地按标签或位置选取任意行列组合。
两种索引:标签和位置
pandas 里取数据有两条路:
loc:按标签(label)取,也就是索引的名字iloc:按位置(position)取,也就是第几行第几列,从 0 开始
先造一个索引不连续的 DataFrame,更容易看出区别:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"年龄": [25, 30, 28, 35],
"城市": ["北京", "上海", "广州", "深圳"],
}, index=[1, 3, 5, 7])
print(df)
行标签是 1、3、5、7,但位置永远是 0、1、2、3。标签和位置是两套独立的坐标系统,loc 和 iloc 就是分别访问这两套系统的入口。
loc:按标签选
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"年龄": [25, 30, 28, 35],
"城市": ["北京", "上海", "广州", "深圳"],
}, index=[1, 3, 5, 7])
print(df.loc[3]) # 标签为 3 的那一行(李四)
print(df.loc[[1, 5]]) # 标签 1 和 5 两行
print(df.loc[1:5]) # 标签从 1 到 5,两头都包含!
loc 的切片规则和 Python 不一样:结束标签也包含在内。df.loc[1:5] 取到标签 1、3、5 三行。
标签不存在会直接抛 KeyError,这是 loc 的严格性,能帮你早发现写错的标签。
iloc:按位置选
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"年龄": [25, 30, 28, 35],
"城市": ["北京", "上海", "广州", "深圳"],
})
print(df.iloc[0]) # 第 0 行
print(df.iloc[[0, 2]]) # 第 0、2 行
print(df.iloc[0:2]) # 第 0、1 行,结束位置不包含!
print(df.iloc[-1]) # 最后一行
print(df.iloc[-3:]) # 最后三行
iloc 的切片和 Python 列表一致:左闭右开,结束位置不包含。负数从末尾数,-1 是最后一行。
Warningloc 和 iloc 的切片规则正好相反:loc 包含结束标签,iloc 不包含结束位置。这是新手最容易翻车的地方,写之前先想清楚自己在按什么取。
行列组合:两个维度一起选
loc 和 iloc 都支持 [行, 列] 的写法,逗号左边选行,右边选列:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"年龄": [25, 30, 28, 35],
"城市": ["北京", "上海", "广州", "深圳"],
}, index=["a", "b", "c", "d"])
print(df.loc["b", "姓名"]) # 一个值:李四
print(df.loc["a":"c", ["姓名", "城市"]]) # 行切片 + 列列表
print(df.iloc[0:3, 0:2]) # 位置切片
print(df.iloc[:, 0]) # 所有行第 0 列
不写列部分等价于选所有列,df.loc["a"] 和 df.loc["a", :] 一样。行列两边都支持切片、列表、单个标签三种写法,自由组合:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"年龄": [25, 30, 28, 35],
"城市": ["北京", "上海", "广州", "深圳"],
}, index=["a", "b", "c", "d"])
print(df.loc["b":"d", "姓名":"城市"]) # 行列都是标签切片
print(df.iloc[1:4, 0:2]) # 行列都是位置切片
Series 也一样适用
Series 只有一条轴,用法更简单:
import pandas as pd
import numpy as np
s = pd.Series([10, 20, 30], index=["a", "b", "c"])
print(s.loc["a":"b"]) # 标签切片,包含 b
print(s.iloc[0:2]) # 位置切片,不含 2
print(s.iloc[-1]) # 最后一个
选列还是选行:别混
df["姓名"]:按列名取一列,最常用df.loc[:, "姓名"]:显式”所有行 + 这一列”,更啰嗦但更明确df.loc["a"]:按行标签取一行,返回 Series
列名和行标签有时长得一样(比如都是数字),用 [] 和用 loc 的结果完全不同。需要选行时一律用 loc/iloc,别用 [] 猜。
Note老教程里常见一个
df.ix索引器,能同时按标签和位置取。它早在 0.20 版就被移除了,现在的写法是:按标签用 loc,按位置用 iloc,不要混用。
切片规则速查
| 写法 | 规则 | 例子结果 |
|---|---|---|
| df.loc[1:5] | 标签,包含两端 | 标签 1、3、5 |
| df.iloc[0:2] | 位置,不包含右端 | 第 0、1 行 |
| df.loc[[1, 5]] | 标签列表 | 标签 1、5 两行 |
| df.iloc[[0, 2]] | 位置列表 | 第 0、2 行 |
| df.iloc[-1] | 负位置 | 最后一行 |
什么时候用哪个
- 索引有业务含义(日期、订单号、人名)时,用 loc,代码一看就懂
- 索引只是默认的 0、1、2 时,两者差不多,用哪个都行
- 要按顺序取前 N 行、循环遍历、或者对接按位置传参的库时,用 iloc
- 索引可能变化(比如筛选后)时,用位置比用标签更稳
一个综合示例
把本节的工具串起来用:取前几行、挑列、再排序:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六", "钱七"],
"年龄": [25, 30, 28, 35, 22],
"城市": ["北京", "上海", "广州", "北京", "深圳"],
"薪资": [12000, 15000, 11000, 18000, 9000],
})
sub = df.loc[:2, ["姓名", "薪资"]] # 前 3 行(标签 0 到 2)的两列
print(sub.sort_values("薪资", ascending=False)) # 按薪资降序
报错行为:提前知道不慌
- loc 找不到标签:抛 KeyError
- iloc 位置越界:抛 IndexError
- 但切片越界不报错:
df.iloc[5:100]会静默返回空结果,跟 Python 切片行为一致
注意返回类型
取单行返回 Series,取多行返回 DataFrame,两种结果的后续操作不一样:
import pandas as pd
import numpy as np
df = pd.DataFrame({"姓名": ["张三", "李四"], "年龄": [25, 30]})
row = df.loc[0] # 单行,Series
sub = df.loc[[0, 1]] # 多行,DataFrame
print(type(row)) # <class 'pandas.core.series.Series'>
print(type(sub)) # <class 'pandas.core.frame.DataFrame'>
拿不准返回类型时,print(type(...)) 确认一下,比猜靠谱。
布尔数组也能用
loc 和 iloc 都接受布尔数组作为”行”的选择条件,这正好是下一节布尔索引的入口:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"年龄": [25, 30, 28, 35],
}, index=[1, 3, 5, 7])
print(df.loc[df["年龄"] > 28]) # 标签 + 条件
print(df.iloc[[0, 2]]) # 位置列表
布尔数组的长度必须和轴长度一致,否则会报错,这是 pandas 的自我保护。
赋值也一样
loc/iloc 不光能读,还能写:
import pandas as pd
import numpy as np
df = pd.DataFrame({
"姓名": ["张三", "李四", "王五", "赵六"],
"年龄": [25, 30, 28, 35],
"城市": ["北京", "上海", "广州", "深圳"],
}, index=[1, 3, 5, 7])
df.loc[3, "年龄"] = 31 # 改一个值
df.loc[1:3, "城市"] = "杭州" # 改一片
df.loc[:, "备注"] = "新成员" # 新增一列,所有行同一个值
print(df)
pandas 3.0 默认开启写时复制(如 §09 所述),这种”先定位再赋值”的写法是最安全的,放心用。
顺带一提:loc 的行位置还支持 callable(可调用对象),比如 df.loc[lambda x: x["年龄"] > 28],效果和布尔数组一样。用得不多,知道有这么个能力就行。
本节的要点
loc 和 iloc 是 pandas 最基础也最重要的两个索引器,后面的布尔筛选、赋值、分组都建立在它们之上。记住三句话:标签用 loc,位置用 iloc;loc 切片含两端,iloc 不含;选行列写 [行, 列]。
选数据有个三层视角,方便记忆:
- 取列:
df["列名"]或df.loc[:, "列名"] - 取行:
df.loc["标签"]或df.iloc[位置] - 取单个值:
df.loc["标签", "列名"],下一节还有更快的 at/iat
切片操作在 pandas 3.0 的写时复制机制下更省心,返回的是新对象,不会悄悄改动原数据(如 §09 所述)。
Tip
df.loc[行, 列]的”行”位置还能放布尔条件,比如df.loc[df["年龄"] > 28, "城市"],条件筛选和列选择一步完成,下一节会用到。记住一个原则:能写进 loc 的,都能用来筛选。