索引的修改与重置
本教程共 54 篇 · 第 23 篇 · 更新于 2026-08-11 · 约 5 分钟阅读
本节目标:掌握 set_index、reset_index、reindex、rename、sort_index 五个常用方法,能随心所欲调整索引,并学会索引去重。
为什么需要动索引
默认索引是从 0 开始的一串整数(RangeIndex,§08 讲过)。但分析时经常需要:把某列当索引、把索引变回普通列、按新顺序重排数据、给索引起名。这一节把常见的”索引手术”一次讲完。
set_index:把列变成索引
最常用的操作:把一列或多列设为行索引。
import pandas as pd
import numpy as np
df = pd.DataFrame({
"学号": ["S001", "S002", "S003"],
"姓名": ["张三", "李四", "王五"],
"分数": [85, 92, 78],
})
print(df.set_index("学号"))
set_index 默认丢掉原来的整数索引,被选中的列从数据区移到索引区。传一个列名列表,就得到多级索引(§22):
print(df.set_index(["学号", "姓名"]))
两个不常用的参数,按需使用:
- append=True:保留原索引,把新列叠上去(通常用于多级索引)
- drop=False:索引列在数据区保留一份(改了索引但列还在)
Tipset_index 返回新对象,原 df 不变(3.0 默认 Copy-on-Write,§09 讲过)。想保留结果就赋值:
df = df.set_index("学号")。
reset_index:把索引变回列
reset_index 是 set_index 的逆操作,步骤:
- 不带参数调用:旧索引变成一列,排在最前面,列名就是索引名
- 传 drop=True:旧索引直接丢弃,换成新的整数索引
- 多级索引时传 level:只重置部分层级
df2 = df.set_index("学号")
print(df2.reset_index()) # 学号回到普通列
print(df2.reset_index(drop=True)) # 旧索引直接扔掉
多级索引只重置一层:
df3 = df.set_index(["学号", "姓名"])
print(df3.reset_index(level="姓名"))
最常见的用法是:分组聚合(§37)后索引变成了分组键,reset_index() 把它还原成普通列,方便继续当普通数据处理。
reindex:按新标签重排
reindex 让数据按你给的标签顺序重排;标签不存在时补 NaN,或用 fill_value 补指定值:
s = pd.Series([10, 20, 30], index=["a", "b", "c"])
print(s.reindex(["c", "a", "d"])) # d 不存在 → NaN
print(s.reindex(["a", "b", "d"], fill_value=0))
reindex 也支持 method=“ffill” / “bfill” 配合 limit,用邻近值补新标签(和 §25 的填充是一回事):
s4 = pd.Series([10, 20, 30], index=[0, 2, 4])
print(s4.reindex([0, 1, 2, 3, 4], method="ffill"))
Warning索引里有重复标签时,reindex 直接抛错(结果无法确定)。先做索引去重(见下文),再 reindex。
reindex 还能补列:给 columns 传新列表,缺的列补 NaN(或 fill_value),常用于对齐两张表的列结构:
df6 = df.reindex(columns=["学号", "姓名", "分数", "排名"], fill_value=0)
print(df6)
rename:改名
rename 可以同时改行索引和列名,支持字典和函数两种写法:
print(df.rename(columns={"分数": "成绩"}))
print(df.rename(columns=str.upper)) # 列名全部大写
自定义索引时,改具体某几个标签用字典:
df4 = df.set_index("学号")
print(df4.rename(index={"S001": "2024001"}, columns={"分数": "成绩"}))
多级索引改名要指定 level,只动某一层:
print(df3.rename(index={"S001": "2024001"}, level="学号"))
sort_index:按标签排序
sort_index 按索引排序,区别于 sort_values 按数据值排序:
df5 = pd.DataFrame({"值": [1, 2, 3, 4]}, index=["c", "a", "d", "b"])
print(df5.sort_index())
print(df5.sort_index(ascending=False))
多级索引用 level 指定按哪一层排(§22 也提到过):
print(df3.sort_index(level="姓名"))
列名也可以排:sort_index(axis=1) 按列名排序,列多时找列方便。
给索引起名
索引名(index.name)相当于”这一列的标题”,多级索引每一层都有名字(index.names)。打印、reset_index 后的列名都会用到它:
s5 = pd.Series([1, 2, 3])
s5.index.name = "序号"
print(s5)
print(s5.reset_index())
索引去重
重复的索引会让 loc 返回多行、reindex 直接报错。先检查,再处理:
s2 = pd.Series([0, 1, 2], index=["a", "b", "b"])
print(s2.index.is_unique) # False
print(s2.index.duplicated()) # [False False True]
print(s2.loc[~s2.index.duplicated()]) # 只保留第一次出现的
如果希望”以后不允许出现重复标签”,用 set_flags 上一把锁:
s3 = s2.loc[~s2.index.duplicated()]
s3 = s3.set_flags(allows_duplicate_labels=False)
此后任何会引入重复标签的操作(比如 rename 撞名)都会抛 DuplicateLabelError,防止清洗后的数据又被污染。
Note去重还有一条思路:按索引分组聚合,
s2.groupby(level=0).mean()会把同标签的行合并成一个值(§37 细讲)。“留哪条”和”合并成什么”取决于业务,没有万能答案。
常见误区
三个容易翻车的点,提前排掉:
- 忘了赋值:set_index / reset_index / reindex 都返回新对象,不改原表(§09 CoW 更是如此),
df.set_index("学号")写完不赋值等于白写 - reset_index 后列名冲突:旧索引名和已有列重名时,pandas 会自动加后缀,检查一下再往下走
- 改了索引影响后续操作:合并(§34)、对齐都按索引匹配,索引改乱了,结果就乱了——每次改完用
df.index或df.head()确认一眼
小结
五个方法的分工很清晰:set_index 建索引,reset_index 拆索引,reindex 换顺序,rename 改名字,sort_index 排顺序。配合 §19 的 loc/iloc,索引这套体系就能随心所欲了。