loc 按标签取,iloc 按位置取。两种都对,混用就取错行。 最大的区别在切片:loc['张伟':'李娜'] 含尾巴,iloc[0:3] 不含尾巴。 行标签换成姓名之后,s[0] 会报 KeyError——这不是 bug,是它在按标签找 0。
上一篇认识了 Series 有标签,这一篇把「怎么取」讲透。 这一节是全章最容易混的地方。混了不会报错,只会悄悄取错数据,所以值得花一整篇。
一、先把标签换成姓名
set_index('姓名') 之后,index 从 0 到 9 变成十个姓名。 取值方式跟着变:从「第几个」变成「谁」。s.loc['张伟'] 直接给 92。 换标签这个动作本身不改数据,只改「怎么找」。理解这一点,后面就不会乱。
二、loc:按标签
单个:s.loc['张伟'] 给一个数。 多个:s.loc[ ['张伟','王芳']] 给一个 Series,顺序按你给的列表来。 切片:s.loc['张伟':'李娜'] 给三行,两头都包含。
这个「两头都含」是 loc 最反直觉的地方。Python 列表切片是含头不含尾,loc 不是。 原因:标签不像位置那样能「往前挪一格」。从张伟到李娜,本来就该把李娜算进去。
三、iloc:按位置
单个:s.iloc[0] 给第一个。 多个:s.iloc[ [0, 2]] 给第 1、3 个。 切片:s.iloc[0:3] 给前三个,尾巴不含,和 Python 列表一致。
loc 含尾、iloc 不含尾——这一条记住,两个方法就再也不会混。 口诀:loc 是「从谁到谁」,iloc 是「从第几个数几个」。
四、什么时候用哪个
| 场景 | 用哪个 | 理由 |
|---|---|---|
| 知道是谁,要取他的分 | loc | 标签稳定,不随排序变 |
| 要看前五行 | iloc | 位置天然就是「前几个」 |
| 按条件筛 | 布尔索引 | 两个都不用,下一节讲 |
两个都能用的时候(标签正好是 0、1、2),用 loc 更安全,因为它不依赖顺序。 但更推荐的做法是:一开始就把标签设成有意义的东西,从源头上避免「第几行」这种问法。
五、报错怎么读
KeyError: 0 说明你在用 loc 找一个不存在的标签 0。改成 iloc[0] 或换回数字标签。 IndexingError 通常是把两个方括号写反了,比如 s.loc[0:3, 1]。 报错的类型能反推你用的是哪套索引:KeyError 是标签,IndexError 是位置。
养成一个习惯:出错先看是哪种错,比一行行读代码快。
六、动手 Demo:Series 的索引与取值
把姓名设成行标签,然后分别用 loc 和 iloc 取单个、取多个、取切片。 重点看切片的边界:loc 含尾,iloc 不含尾。
import pandas as pd
s = pd.Series([88, 92, 75, 85, 90],
index=["张伟", "王芳", "李娜", "刘洋", "陈静"],
name="数学")
print("loc 单个 %d" % s.loc["张伟"])
print("iloc 单个 %d" % s.iloc[0])
print("loc 三个 %s" % s.loc["张伟":"李娜"].tolist())
print("iloc 三个 %s" % s.iloc[0:3].tolist())
print("loc 列表 %s" % s.loc[ ["张伟", "王芳"]].tolist())
print("iloc 列表 %s" % s.iloc[ [0, 1]].tolist())
try:
s.loc[0]
except KeyError:
print("s.loc[0] 报 KeyError:标签里没有 0")
实跑输出:
--------------------------------------------------------------------
Series 的索引与取值
--------------------------------------------------------------------
① 先换成有意义的标签
--------------------------------------------------------------------
把姓名设成行标签之后,index 变成:张伟、王芳、李娜、刘洋。 ……
| 姓名 | 数学 |
|---|---:|
| 张伟 | 92 |
| 王芳 | 95 |
| 李娜 | 68 |
| 刘洋 | 88 |
! 行标签一变,取值方式也跟着变:从「第几个」变成「谁」。
② 按标签取:loc
--------------------------------------------------------------------
s.loc['张伟'] = 92。
s.loc[ ['张伟', '王芳']] 一次取两个:
| 姓名 | 数学 |
|---|---:|
| 张伟 | 92 |
| 王芳 | 95 |
切片也能用标签:s.loc['张伟':'李娜'] 取的是这三个人,两头都包含。
| 姓名 | 数学 |
|---|---:|
| 张伟 | 92 |
| 王芳 | 95 |
| 李娜 | 68 |
! loc 的切片两头都含,这是它和普通 Python 切片最大的不同。
③ 按位置取:iloc
--------------------------------------------------------------------
s.iloc[0] = 92,取第一个。
s.iloc[0:3] 取前三个,尾巴不含。
| 姓名 | 数学 |
|---|---:|
| 张伟 | 92 |
| 王芳 | 95 |
| 李娜 | 68 |
看到了吗:loc 的 0:3 含第 3 个,iloc 的 0:3 不含。同样的写法,两个结果。
! 记住这一条就不会混:loc 是「从谁到谁」,iloc 是「从第几个数几个」。
④ 什么时候用哪个
--------------------------------------------------------------------
| 场景 | 用哪个 | 理由 |
|---|---|---|
| 知道是谁,要取他的分 | loc | 标签稳定,不随排序变 |
| 要前五行看看 | iloc | 位置天然就是「前几个」 |
| 按条件筛 | 布尔索引 | 两个都不用,见 3.9 篇 |
两个都能用的时候(比如标签正好是 0,1,2…),用 loc 更安全,因为它不依赖顺序。
! 新手最常见的 bug:设了姓名索引之后还在用 s[0],报 KeyError: 0。改成 iloc[0] 就好了。
两行切片的结果一样长,但含义不同:loc 那次给的是「张伟到李娜」,iloc 那次给的是「前三个」。 碰巧重合,不是一回事。 最后一个 try 是这一节的重点:把报错现场打出来,比读十行说明管用。
30 秒实验
- 把标签改成 0、1、2、3、4,再跑一遍,s.loc[0] 会成功吗?结果和 iloc[0] 一样吗?
- 试 s.loc['王芳':'陈静'],一共几行?再试 s.iloc[1:4],一共几行?
- 想一想:为什么 pandas 要设计成两套索引,而不是统一成一套?
常见误区
- 以为 loc 和 iloc 只是写法不同:边界规则不一样,取出来可能差一行。
- 标签改了还用 s[0]:pandas 3 里直接 KeyError。
- 把行标签和行号当同一件事:删过行之后,行标签就不是 0、1、2 连续的了。
- 不敢用 loc,全用 iloc:排序一变,iloc 取到的就不是同一个人了。
自测三问(点开对答案)
1. loc 和 iloc 最关键的差别是什么? —— loc 按标签,切片含尾;iloc 按位置,切片不含尾。 2. 为什么 s.loc['张伟':'李娜'] 要把李娜算进去? —— 因为标签切片是按「范围」取的,从张伟到李娜这个范围本身包含两端。 3. s.loc[0] 报 KeyError,怎么改? —— 看你要什么:要第一行用 s.iloc[0],要标签是 0 的那行就把标签设回去。一条 Series 讲完了,该讲整张表。下一篇讲 DataFrame 的三种造法——从字典、从列表、从 numpy 数组。
- ☐ (本篇完,下一篇 3.4 DataFrame:一张能算的表格)