Skip to content

Python Pandas - 重新索引

重新索引是将 DataFrame 或 Series 符合新的索引标签集的过程。它是数据对齐和处理(data alignment and manipulation)的基本操作。

通过重新索引实现的关键功能包括:

  • 按照新的索引顺序对现有数据进行重新排序(reordering)。
  • 为新索引中存在但原始对象中不存在的标签插入缺失值标记(missing value markers) (NaN)。
  • 丢弃原始对象中存在但新索引中不存在的标签对应的数据。

主要方法是 .reindex()。

让我们创建一个 DataFrame,然后对其行和列进行重新索引。

import pandas as pd
import numpy as np
N = 5 # Using a smaller N for brevity
df = pd.DataFrame({
'A': pd.date_range(start='2023-01-01', periods=N, freq='D'),
'x': np.linspace(0, stop=N - 1, num=N),
'y': np.random.rand(N),
'C': np.random.choice(['Low', 'Medium', 'High'], N).tolist(),
'D': np.random.normal(100, 10, size=(N)).tolist()
})
print("Original DataFrame:")
print(df)
# Reindex the DataFrame with a new index and new columns
# New index includes existing (0, 2) and non-existing (5) labels
# New columns include existing ('A', 'C') and non-existing ('B') labels
df_reindexed = df.reindex(index=[0, 2, 5], columns=['A', 'C', 'B'])
print("\nReindexed DataFrame:")
print(df_reindexed)

输出 (随机值可能不同):

Original DataFrame:
A x y C D
0 2023-01-01 0.0 0.123456 Low 95.123456
1 2023-01-02 1.0 0.654321 Medium 110.987654
2 2023-01-03 2.0 0.987654 High 105.456789
3 2023-01-04 3.0 0.321098 Low 89.123456
4 2023-01-05 4.0 0.789012 Medium 101.567890
Reindexed DataFrame:
A C B
0 2023-01-01 Low NaN
2 2023-01-03 High NaN
5 NaT NaN NaN

注意看:

  • 索引标签为 0 和 2 的行被保留。
  • 索引标签为 5 的行被添加,并填充了 NaN (或日期时间的 NaT)。
  • 列 ‘A’ 和 ‘C’ 被保留。
  • 列 ‘B’ 被添加,并填充了 NaN。
  • 原来的行 1, 3, 4 以及列 ‘x’, ‘y’, ‘D’ 被丢弃,因为它们不在新的索引/列列表中。

使用 reindex_like 与另一个对象对齐

Section titled “使用 reindex_like 与另一个对象对齐”

有时,您希望重新索引一个对象,使其索引(index)和/或列(columns)与另一个现有对象匹配。reindex_like() 方法提供了一种便捷的方式来实现这一点。

import pandas as pd
import numpy as np
# Create two DataFrames with different sizes and potentially different indexes/columns
df1 = pd.DataFrame(np.random.randn(5, 3), index=list('acefh'), columns=['col1', 'col2', 'col3'])
df2 = pd.DataFrame(np.random.randn(7, 3), index=list('abcdefg'), columns=['col1', 'col2', 'col3'])
print("DataFrame 1 (Original):")
print(df1)
print("\nDataFrame 2 (Target Shape):")
print(df2)
# Reindex df1 to match the index and columns of df2
df1_aligned = df1.reindex_like(df2)
print("\nDataFrame 1 Aligned with DataFrame 2:")
print(df1_aligned)

输出 (随机值可能不同):

DataFrame 1 (Original):
col1 col2 col3
a 0.123456 -0.987654 1.456789
c -0.321098 1.098765 -0.765432
e 0.543210 -0.109876 0.210987
f -1.876543 0.765432 -0.987654
h 0.112233 -0.445566 1.678901
DataFrame 2 (Target Shape):
col1 col2 col3
a -0.567890 1.234567 -0.112233
b 0.876543 -0.345678 1.998877
c -1.112233 0.556677 -0.223344
d 0.445566 -1.778899 0.334455
e -0.667788 0.112233 -1.445566
f 1.889900 -0.556677 0.778899
g -0.223344 1.334455 -0.889900
DataFrame 1 Aligned with DataFrame 2:
col1 col2 col3
a 0.123456 -0.987654 1.456789
b NaN NaN NaN
c -0.321098 1.098765 -0.765432
d NaN NaN NaN
e 0.543210 -0.109876 0.210987
f -1.876543 0.765432 -0.987654
g NaN NaN NaN

注意:df1_aligned 现在与 df2 具有相同的索引(从 ‘a’ 到 ‘g’)和列。对于 df2 的索引中存在但 df1 原始数据中也有数据的标签 (a, c, e, f),这些数据被保留。对于 df2 的索引中存在但 df1 原始数据中没有数据的标签 (b, d, g),则插入了 NaN 值。df1 原始数据中索引标签不在 df2 中的行(例如 ‘h’)被丢弃。

.reindex() 和 .reindex_like() 方法接受一个可选的 method 参数,用于填充重新索引过程中创建的空洞(NaN 值)。常用的填充方法包括:

  • method='pad' 或 method='ffill':前向填充 - 将最后一个有效观测值向前传播以填充空白。
  • method='bfill' 或 method='backfill':后向填充 - 使用下一个有效观测值向后填充空白。
  • method='nearest':使用最近的索引标签对应的值填充。
import pandas as pd
import numpy as np
# DataFrame with fewer rows
df_sparse = pd.DataFrame(np.random.randn(3, 3), index=[0, 3, 5], columns=['col1', 'col2', 'col3'])
# Target index with more rows
target_index = pd.Index(range(7))
print("Original Sparse DataFrame:")
print(df_sparse)
# Reindex and fill NaN using forward fill
df_filled_ffill = df_sparse.reindex(target_index, method='ffill')
print("\nDataFrame Reindexed with Forward Fill:")
print(df_filled_ffill)

输出 (随机值可能不同):

Original Sparse DataFrame:
col1 col2 col3
0 0.123456 -0.987654 1.456789
3 -0.321098 1.098765 -0.765432
5 0.543210 -0.109876 0.210987
DataFrame Reindexed with Forward Fill:
col1 col2 col3
0 0.123456 -0.987654 1.456789 # Original
1 0.123456 -0.987654 1.456789 # Filled from index 0
2 0.123456 -0.987654 1.456789 # Filled from index 0
3 -0.321098 1.098765 -0.765432 # Original
4 -0.321098 1.098765 -0.765432 # Filled from index 3
5 0.543210 -0.109876 0.210987 # Original
6 0.543210 -0.109876 0.210987 # Filled from index 5

注意看,索引 0 的值用于填充索引 1 和 2,索引 3 的值填充索引 4,索引 5 的值填充索引 6。

limit 参数可以控制在重新索引期间,ffill 或 bfill 操作填充连续 NaN 值的数量。

import pandas as pd
import numpy as np
# DataFrame with fewer rows
df_sparse = pd.DataFrame(np.random.randn(3, 3), index=[0, 4, 7], columns=['col1', 'col2', 'col3'])
# Target index with more rows
target_index = pd.Index(range(9))
print("Original Sparse DataFrame:")
print(df_sparse)
# Reindex and forward fill, but limit filling to 2 consecutive NaNs
df_filled_limit = df_sparse.reindex(target_index, method='ffill', limit=2)
print("\nDataFrame Reindexed with Forward Fill (limit=2):")
print(df_filled_limit)

输出 (随机值可能不同):

Original Sparse DataFrame:
col1 col2 col3
0 0.123456 -0.987654 1.456789
4 -0.321098 1.098765 -0.765432
7 0.543210 -0.109876 0.210987
DataFrame Reindexed with Forward Fill (limit=2):
col1 col2 col3
0 0.123456 -0.987654 1.456789 # Original
1 0.123456 -0.987654 1.456789 # Filled (1st)
2 0.123456 -0.987654 1.456789 # Filled (2nd)
3 NaN NaN NaN # Not filled (limit reached)
4 -0.321098 1.098765 -0.765432 # Original
5 -0.321098 1.098765 -0.765432 # Filled (1st)
6 -0.321098 1.098765 -0.765432 # Filled (2nd)
7 0.543210 -0.109876 0.210987 # Original
8 0.543210 -0.109876 0.210987 # Filled (1st)

注意,索引 3 仍然是 NaN,因为从索引 0 开始的填充被限制为 2 步(索引 1 和 2)。

rename() 方法允许您基于映射(例如字典)或函数重新标记索引条目(index entries)或列名(column names)。这与重新索引不同,因为它保留了与原始标签关联的数据,并赋予其新名称,而不是基于标签值进行对齐。

让我们重命名一些列和索引标签:

import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(6, 3), index=range(6), columns=['col1', 'col2', 'col3'])
print("Original DataFrame:")
print(df)
renamed_df = df.rename(
columns={'col1': 'Column_A', 'col2': 'Column_B'},
index={0: 'Row_Zero', 1: 'Row_One', 2: 'Row_Two'}
)
print("\nDataFrame with Renamed Rows and Columns:")
print(renamed_df)
# To modify the DataFrame in place:
# df.rename(columns={'col1': 'C1'}, index={0: 'R0'}, inplace=True)
# print("\nDataFrame after inplace rename:")
# print(df)

输出 (随机值可能不同):

Original DataFrame:
col1 col2 col3
0 0.486791 0.105759 1.540122
1 -0.990237 1.007885 -0.217896
2 -0.483855 -1.645027 -1.194113
3 -0.122316 0.566277 -0.366028
4 -0.231524 -0.721172 -0.112007
5 0.438810 0.000225 0.435479
DataFrame with Renamed Rows and Columns:
Column_A Column_B col3
Row_Zero 0.486791 0.105759 1.540122
Row_One -0.990237 1.007885 -0.217896
Row_Two -0.483855 -1.645027 -1.194113
3 -0.122316 0.566277 -0.366028
4 -0.231524 -0.721172 -0.112007
5 0.438810 0.000225 0.435479

默认情况下,rename() 返回一个新的 DataFrame,其中包含更改 (inplace=False)。将 inplace 设置为 True 可以直接修改原始 DataFrame(请谨慎使用)。