Pandas 多層索引

多層索引(MultiIndex)也稱為階層式索引(Hierarchical Index),可以在單一軸上擁有多個層級的索引。這對於處理高維度資料非常有用。

建立多層索引

從 tuple 建立

import pandas as pd

index = pd.MultiIndex.from_tuples([
    ('A', 1), ('A', 2),
    ('B', 1), ('B', 2)
])

s = pd.Series([10, 20, 30, 40], index=index)
print(s)
A  1    10
   2    20
B  1    30
   2    40
dtype: int64

從 arrays 建立

arrays = [
    ['A', 'A', 'B', 'B'],
    [1, 2, 1, 2]
]
index = pd.MultiIndex.from_arrays(arrays, names=['letter', 'number'])

s = pd.Series([10, 20, 30, 40], index=index)
print(s)

從 product 建立

建立所有組合:

index = pd.MultiIndex.from_product(
    [['A', 'B'], [1, 2, 3]],
    names=['letter', 'number']
)
print(index)
# MultiIndex([('A', 1), ('A', 2), ('A', 3),
#             ('B', 1), ('B', 2), ('B', 3)], ...)

使用 set_index

從 DataFrame 欄位建立:

df = pd.DataFrame({
    'year': [2023, 2023, 2024, 2024],
    'city': ['Taipei', 'Tokyo', 'Taipei', 'Tokyo'],
    'sales': [100, 200, 150, 250]
})

df = df.set_index(['year', 'city'])
print(df)
              sales
year city          
2023 Taipei    100
     Tokyo     200
2024 Taipei    150
     Tokyo     250

存取多層索引資料

使用 loc

df = pd.DataFrame({
    'year': [2023, 2023, 2024, 2024],
    'city': ['Taipei', 'Tokyo', 'Taipei', 'Tokyo'],
    'sales': [100, 200, 150, 250]
}).set_index(['year', 'city'])

# 選取第一層
print(df.loc[2023])

# 選取特定組合
print(df.loc[(2023, 'Taipei')])

# 選取多個組合
print(df.loc[[(2023, 'Taipei'), (2024, 'Tokyo')]])

使用 xs(cross-section)

# 選取第一層特定值
print(df.xs(2023, level='year'))

# 選取第二層特定值
print(df.xs('Taipei', level='city'))

切片

# 需要先排序索引
df = df.sort_index()

# 切片
print(df.loc[2023:2024])
print(df.loc[(2023, 'Taipei'):(2024, 'Tokyo')])

重設和調整索引

reset_index

# 將所有索引層級轉回欄位
df_flat = df.reset_index()

# 只重設特定層級
df_partial = df.reset_index(level='city')

swaplevel

交換索引層級:

df_swapped = df.swaplevel()
print(df_swapped)
              sales
city   year        
Taipei 2023    100
Tokyo  2023    200
Taipei 2024    150
Tokyo  2024    250

reorder_levels

重新排列索引層級順序:

df_reordered = df.reorder_levels(['city', 'year'])

排序

# 按索引排序
df = df.sort_index()

# 按特定層級排序
df = df.sort_index(level='city')

# 降冪排序
df = df.sort_index(ascending=False)

多層索引的聚合

df = pd.DataFrame({
    'year': [2023, 2023, 2023, 2024, 2024, 2024],
    'city': ['Taipei', 'Taipei', 'Tokyo', 'Taipei', 'Tokyo', 'Tokyo'],
    'sales': [100, 150, 200, 120, 180, 220]
}).set_index(['year', 'city'])

# 按第一層聚合
print(df.groupby(level='year').sum())

# 按第二層聚合
print(df.groupby(level='city').sum())

# 按所有層級聚合
print(df.groupby(level=['year', 'city']).sum())

多層欄位索引

欄位也可以有多層索引:

columns = pd.MultiIndex.from_tuples([
    ('sales', 'Q1'), ('sales', 'Q2'),
    ('profit', 'Q1'), ('profit', 'Q2')
])

df = pd.DataFrame([
    [100, 120, 10, 12],
    [200, 220, 20, 22]
], columns=columns, index=['Taipei', 'Tokyo'])

print(df)
        sales      profit     
           Q1   Q2     Q1   Q2
Taipei    100  120     10   12
Tokyo     200  220     20   22

存取:

# 選取第一層
print(df['sales'])

# 選取特定組合
print(df[('sales', 'Q1')])

堆疊和取消堆疊

stack

將欄位轉成索引(寬變長):

df = pd.DataFrame({
    'A': [1, 2],
    'B': [3, 4]
}, index=['x', 'y'])

stacked = df.stack()
print(stacked)
x  A    1
   B    3
y  A    2
   B    4
dtype: int64

unstack

將索引轉成欄位(長變寬):

unstacked = stacked.unstack()
print(unstacked)
   A  B
x  1  3
y  2  4

指定要 unstack 的層級:

df = pd.DataFrame({
    'year': [2023, 2023, 2024, 2024],
    'city': ['Taipei', 'Tokyo', 'Taipei', 'Tokyo'],
    'sales': [100, 200, 150, 250]
}).set_index(['year', 'city'])

# unstack city
print(df.unstack(level='city'))
      sales       
city Taipei Tokyo
year              
2023    100   200
2024    150   250

實際應用

時間序列的階層資料

# 建立年-月-日的階層索引
df = pd.DataFrame({
    'year': [2024] * 6,
    'month': [1, 1, 2, 2, 3, 3],
    'day': [1, 15, 1, 15, 1, 15],
    'value': [100, 110, 120, 115, 130, 125]
}).set_index(['year', 'month', 'day'])

# 取特定月份
print(df.loc[(2024, 2)])

# 按月彙總
print(df.groupby(level='month').sum())