Pandas & NumPy Academy · 课时

索引对齐与重新索引

使用 reindex() 将两个 DataFrames 对齐到共同索引,填充缺失标签,并理解算术运算如何对齐索引。

第 3 / 4 课13 个步骤

索引对齐与重新索引 是 CoddyKit 上的免费 Pandas & NumPy Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Pandas & NumPy Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Pandas & NumPy Academy 课程共包含 4 节课。

Pandas 如何对齐索引

Pandas 最强大、也有时最令人意外的行为之一是自动索引对齐。当您对两个 Series 或 DataFrame 进行加法、减法或其他组合操作时,Pandas 会先根据索引标签对齐它们,然后再执行操作。匹配的标签会参与运算;不匹配的标签会产生 NaN。这可以避免因数据未对齐而产生不易察觉的错误,也让来自不同来源的数据无需手动排序或重排即可安全组合。

import pandas as pd

s1 = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
s2 = pd.Series([1, 2, 3], index=['b', 'c', 'd'])

# Alignment in action: a→NaN, d→NaN
result = s1 + s2
print('s1 + s2 with automatic alignment:')
print(result)

索引未对齐产生的 NaN

当索引标签不匹配时,Pandas 会用 NaN 填充缺失项。这是有意为之,用来表示“其中一个运算对象没有对应值”。请始终检查两个 Series 或 DataFrame 运算的结果是否出现意外的 NaN 值——这说明索引可能未对齐。在算术方法中使用 fill_value=0(s1.add(s2, fill_value=0)),可将对齐后缺失的值视为零,而不是让 NaN 继续传播。

import pandas as pd

s1 = pd.Series({'a': 10, 'b': 20, 'c': 30})
s2 = pd.Series({'b': 1, 'c': 2, 'd': 3})

# Default: NaN where labels don't match
print('Default (NaN for unmatched):')
print(s1 + s2)

# fill_value=0: treat missing as zero
print('\nWith fill_value=0:')
print(s1.add(s2, fill_value=0))

DataFrame 算术运算的对齐

组合两个 DataFrame 时,对齐会同时应用于行和列。结果包含两个索引集合和两个列集合的并集,如果任一 DataFrame 没有对应值,则该位置为 NaN。这相当于同时基于索引和列执行全外连接。这意味着您可以安全地相加来自不同财务期间的 DataFrame——某个月份只存在于其中一个 DataFrame 时,对应位置会得到 NaN,之后可以填充或删除。

import pandas as pd

df1 = pd.DataFrame({'revenue': [100, 200], 'cost': [80, 150]}, index=['Jan', 'Feb'])
df2 = pd.DataFrame({'revenue': [120, 210], 'headcount': [5, 6]}, index=['Feb', 'Mar'])

result = df1 + df2
print('Combined DataFrame (union of index and columns):')
print(result)

reindex() 方法

df.reindex(new_index) 返回一个按照 new_index 标签列表调整后的新 DataFrame。原索引和新索引中都存在的标签会被保留;存在于 new_index 但不在原索引中的标签会得到 NaN;存在于原索引但不在 new_index 中的标签会被删除。这是在执行操作前将 DataFrame 显式对齐到目标标签集合的方式。

import pandas as pd

s = pd.Series({'a': 10, 'b': 20, 'c': 30})

# Reindex to a new label set
reindexed = s.reindex(['b', 'c', 'd', 'e'])
print('Original:', s.index.tolist())
print('New index: [b, c, d, e]')
print('\nReindexed Series:')
print(reindexed)
# b, c preserved; d, e → NaN; a dropped

重新索引后填充缺失值

reindex() 接受 fill_value 参数,可用常量替换新标签对应的值;还接受 method 参数,用于向前填充('ffill')或向后填充('bfill')。当您将 Series 重新索引到完整日期范围,并希望用最后一个已知值填充缺失日期而不是使用 NaN 时,这些填充方法对时间序列数据最有用。

import pandas as pd

# Sparse daily data with gaps
s = pd.Series(
    [10, 20, 30],
    index=pd.to_datetime(['2024-01-01', '2024-01-03', '2024-01-07'])
)

# Reindex to complete date range
full_range = pd.date_range('2024-01-01', '2024-01-07')
print('Forward fill (carry last known value):')
print(s.reindex(full_range, method='ffill'))

print('\nFill with 0:')
print(s.reindex(full_range, fill_value=0))

重新索引列

reindex 同样适用于列:df.reindex(columns=['col1', 'col2', 'new_col'])。原 DataFrame 中不存在的新列会被添加,并填充 NaN。不在新列表中的现有列会被删除。当多个 DataFrame 来自不同来源且列集合不一致时,这对于强制使用统一的规范列结构非常有用。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob'],
    'age': [30, 25],
    'city': ['NY', 'LA']
})

# Enforce a canonical column order and add missing columns
canonical_cols = ['name', 'age', 'email', 'city', 'country']
df_reindexed = df.reindex(columns=canonical_cols)
print(df_reindexed)
# 'email' and 'country' are new → NaN

使用 align() 同步两个对象

s1.align(s2) 返回两个具有相同索引的新对象(根据 join 参数,可以使用并集或交集),使它们可以进行逐元素运算。这相当于同时将两个对象重新索引到同一个标签集合。使用 join='inner' 仅保留公共标签,或使用 join='outer'(默认值)保留两个对象中的所有标签,并用 NaN 表示不匹配项。

import pandas as pd

s1 = pd.Series({'a': 10, 'b': 20, 'c': 30})
s2 = pd.Series({'b': 1, 'c': 2, 'd': 3})

# Align to common labels only (inner join)
s1_aligned, s2_aligned = s1.align(s2, join='inner')
print('Inner-aligned s1:')
print(s1_aligned)
print('Inner-aligned s2:')
print(s2_aligned)

print('\nProduct on common labels:')
print(s1_aligned * s2_aligned)

merge 与算术运算中的对齐

Pandas 提供了两种组合 DataFrame 的思路:merge/join(类似 SQL,显式匹配键)和带索引对齐的算术运算(隐式、基于标签)。当您要组合具有明确键列的结构化表时,请使用 merge。当两个 DataFrame 自然应共享同一索引并需要进行计算时,请使用算术对齐——例如,对都按(地区、月份)建立索引的收入 DataFrame 和成本 DataFrame 进行相减。

import pandas as pd

# Two DataFrames sharing the same index
revenue = pd.Series({'North': 500, 'South': 300, 'East': 450})
costs = pd.Series({'North': 350, 'South': 280, 'West': 400})

# Automatic alignment: 'East' and 'West' don't match → NaN
profit = revenue - costs
print('Profit by region:')
print(profit)

# If you want only common regions
profit_inner = revenue.align(costs, join='inner')[0] - revenue.align(costs, join='inner')[1]
print('\nProfit (common regions only):')
print(profit_inner)

操作前检查索引是否相等

对两个 DataFrame 执行操作前,请使用 (df1.index == df2.index).all() 检查它们的索引是否匹配。如果索引只是在顺序上不同,请先对两者排序再比较。对于从不同来源加载的 DataFrame,良好的做法是在进行算术运算前显式对齐或重新索引,以避免 NaN 意外传播。请在注释或处理流程日志中记录任何关于对齐的假设。

import pandas as pd

df1 = pd.DataFrame({'sales': [100, 200, 300]}, index=['Q1', 'Q2', 'Q3'])
df2 = pd.DataFrame({'cost': [80, 160, 240]}, index=['Q1', 'Q2', 'Q3'])

# Check index equality
if (df1.index == df2.index).all():
    print('Indices match — safe to combine.')
    combined = pd.concat([df1, df2], axis=1)
    combined['profit'] = combined['sales'] - combined['cost']
    print(combined)
else:
    print('Indices differ — align before combining!')

实际模式:统一形状

从多个文件或 API 调用加载数据时,一个常见情况是每个批次覆盖的键子集不同。在进行拼接或聚合之前,请使用 fill_value=0 将所有批次重新索引到完整的已知键空间。这样可以确保各批次在操作前具有相同的形状(相同的索引和列),避免因形状不一致而悄然产生错误的聚合结果。

import pandas as pd

# Two sales batches with different product sets
batch1 = pd.Series({'laptop': 50, 'phone': 80, 'tablet': 30})
batch2 = pd.Series({'phone': 90, 'tablet': 40, 'headphones': 60})

# Full product catalogue
all_products = ['laptop', 'phone', 'tablet', 'headphones']

# Standardise both to the full catalogue
b1 = batch1.reindex(all_products, fill_value=0)
b2 = batch2.reindex(all_products, fill_value=0)

total = b1 + b2
print('Total sales per product:')
print(total)

索引对齐的边界情况

有两个重要的边界情况:重复标签——如果两个 Series 都有重复的索引标签,对齐会产生类似笛卡尔积的扩展,并包含许多 NaN 值(Pandas 不会假定哪些重复项彼此对应)。进行算术对齐前,请始终确保索引唯一。整数索引与对象索引——RangeIndex(0,5) 和 Int64Index([0,1,2,3,4]) 在运算后可能无法完全对齐,因为前者是推断得到的,后者是显式指定的。请使用 reset_index(drop=True) 进行规范化。

import pandas as pd

# Duplicate label alignment — produces unexpected expansion
s1 = pd.Series([1, 2, 3], index=['a', 'a', 'b'])
s2 = pd.Series([10, 20], index=['a', 'b'])

result = s1 + s2
print('Result with duplicate indices (unexpected expansion):')
print(result)
print('\nAlways ensure unique indices before arithmetic!')

快速检查

请测试您对本课索引对齐和重新索引的理解。

课程回顾

在本课中,您学到了:Pandas 会在算术运算中执行自动索引对齐,对不匹配的标签生成 NaN;reindex() 会将 DataFrame 调整为目标标签集合,并可通过填充选项处理缺失标签;align() 会同时将两个对象同步到相同的索引。接下来,我们将探讨排序索引带来的性能优势,以及如何使用 timeit 对其进行测量。

免费开始

用 AI 导师学习 Python — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
30
课程
120

常见问题解答

「索引对齐与重新索引」课时是免费的吗?

是的 — 「索引对齐与重新索引」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Pandas & NumPy Academy 课程的其余内容,请升级到 CoddyKit PRO。 Pandas & NumPy Academy 课程共包含 4 节课。

「索引对齐与重新索引」这节课中我会学到什么?

使用 reindex() 将两个 DataFrames 对齐到共同索引,填充缺失标签,并理解算术运算如何对齐索引。 你通过在浏览器中直接运行的动手代码来练习 Pandas & NumPy Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Pandas & NumPy Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Pandas & NumPy Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「索引对齐与重新索引」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Pandas & NumPy Academy 课中编写并运行代码吗?

能。每节 Pandas & NumPy Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 创建 MultiIndex
  2. 从 MultiIndex 选择数据
  3. 索引对齐与重新索引
  4. 排序索引的性能优势
← 返回 Pandas & NumPy Academy