1-2 Index:挂号系统决定谁对齐谁 本节摘要:Index是行标签的容器,Pandas一切对齐、查找、切片都以它为准。本节讲清位置与标签两套坐标的区别、索引对齐的运算规则、setindex与resetindex的换号操作,以及索引对象的不可变性。 承接1-1:档案袋(DataFrame)已经建好,但还没挂号。这一节发的挂号条,将决定第02章loc叫号、第05章分组、第07章时间序列随访的全部规则。 挂号条不是行号 两者此刻结果相同,是因为挂号条恰好按排队顺序发。一旦顺序打乱或标签有重复,两套坐标就分道扬镳——这是第02章2-1的主题,这里先记住结论:loc认标签,iloc认位置,混用是隐式bug的第一大来源。 对齐:不同医院的两张化验单怎么合并读数 Index真正的主场是运算对齐。
本节摘要:Index是行标签的容器,Pandas一切对齐、查找、切片都以它为准。本节讲清位置与标签两套坐标的区别、索引对齐的运算规则、set_index与reset_index的换号操作,以及索引对象的不可变性。
承接1-1:档案袋(DataFrame)已经建好,但还没挂号。这一节发的挂号条,将决定第02章loc叫号、第05章分组、第07章时间序列随访的全部规则。
import pandas as pd df = pd.DataFrame({ '销售额': [128000, 96500, 173200], }, index=['门店A', '门店B', '门店C']) # 按标签取(挂号条叫号) print(df.loc['门店B']) # 销售额 96500 # Name: 门店B, dtype: int64 # 按位置取(第几个排队) print(df.iloc[1]) # 销售额 96500 # Name: 门店B, dtype: int64
两者此刻结果相同,是因为挂号条恰好按排队顺序发。一旦顺序打乱或标签有重复,两套坐标就分道扬镳——这是第02章2-1的主题,这里先记住结论:loc认标签,iloc认位置,混用是隐式bug的第一大来源。
Index真正的主场是运算对齐。两张化验单各测了几个日期,部分重叠、部分缺失,直接相加时Pandas会先做"取并集对表":
q1 = pd.Series([120, 135], index=['1月', '2月'], name='一季度') q2 = pd.Series([210, 198, 187], index=['2月', '3月', '4月'], name='二季度') print(q1 + q2) # 1月 NaN # 2月 345.0 # 3月 NaN # 4月 NaN # dtype: float64 # 两边都测过的只有2月;只在一边有的月份,另一边补NaN,加出来就是NaN # 想只保留重叠部分,用专门的索引运算 print(q1.add(q2, fill_value=0)) # 1月 120.0 # 2月 345.0 # 3月 198.0 # 4月 187.0 # dtype: float64
这个对齐行为解释了第03章会反复遇到的NaN从哪来:很多时候不是数据缺失,而是对齐产生的。把这一点想在前面,后面看到NaN先问一句"是缺测,还是没对上号"。
挂号条可以换人当。销售明细用交易序号排队,但按日期随访更方便,就把日期升格为索引:
sales = pd.DataFrame({ '日期': ['2024-01-03', '2024-01-10', '2024-01-15'], '金额': [1280, 1732, 880], }) dated = sales.set_index('日期') # 日期列升格为挂号条 print(dated) # 金额 # 日期 # 2024-01-03 1280 # 2024-01-10 1732 # 2024-01-15 880 back = dated.reset_index() # 挂号条降级回普通列 print(back.columns.tolist()) # ['日期', '金额'] # drop=True 则直接撕掉挂号条,不要这列了 print(dated.reset_index(drop=True)) # 金额 # 0 1280 # 1 1732 # 2 880
⚠️ 常见坑:set_index之后原列默认消失。如果既要按日期索引又要保留日期列做字符串处理,先复制一份列再set_index。
idx = pd.Index(['门店A', '门店B', '门店C']) print(idx.is_unique) # True:无重号 busy = pd.Index(['早', '早', '晚']) print(busy.is_unique) # False:重复挂号 print(busy.get_indexer(['早'])) # [0] 重号时只定位到第一个 # 索引是不可变的:idx[0]='门店X' 会直接抛异常 # 想改只能整体换发:dated.index = pd.to_datetime(dated.index)
重复索引在第02章按标签取行时会"一次叫出一群人",在merge时会引发笛卡尔式的膨胀。is_unique这个一行的检查,值得写进任何接诊脚本的固定动作里。

能,而且思路值得现在建立。索引的查找本质是哈希定位,reindex与loc在有序唯一索引上接近常数时间。一个实战细节:按某列反复筛选时,把该列set_index成索引再操作,常常比每次布尔比较划算;多级索引的取数在排序后更是走二分路径,这就是前面强调"乱序先sort_index"的性能原因。第08章谈性能时这一条会再次出现。
# 对比体会:百万行上按索引取数 vs 布尔比较 big = pd.DataFrame({'金额': range(1000000)}, index=[f'单{i:06d}' for i in range(1000000)]) print(big.loc['单500000', '金额']) # 500000 索引直达 print(big[big.index == '单500000']) # 同样结果,但要全表扫描比较
两者结果一致,规模小的时候无所谓,百万行以上差距就开始显现。什么时候值得把列升格为索引,判断标准是"这个键被查询的频率"。
档案挂好号,下一节打开登记窗口,把CSV、Excel、数据库里的数据接进来。