Pandas 資料重塑
資料重塑(Reshape)是將資料在「長格式」和「寬格式」之間轉換的操作。這在資料分析和視覺化中經常需要用到。
長格式 vs 寬格式
寬格式(Wide Format):每個變數佔一欄
name math english
0 Alice 90 85
1 Bob 80 90
長格式(Long Format):一欄是變數名稱,一欄是值
name subject score
0 Alice math 90
1 Alice english 85
2 Bob math 80
3 Bob english 90
melt():寬轉長
import pandas as pd
# 寬格式資料
df_wide = pd.DataFrame({
'name': ['Alice', 'Bob'],
'math': [90, 80],
'english': [85, 90]
})
# 轉成長格式
df_long = df_wide.melt(id_vars='name', var_name='subject', value_name='score')
print(df_long)
name subject score
0 Alice math 90
1 Bob math 80
2 Alice english 85
3 Bob english 90
melt 參數說明
df.melt(
id_vars='name', # 保持不變的欄位(識別欄位)
value_vars=['math', 'english'], # 要轉換的欄位(預設全部)
var_name='subject', # 新的變數名稱欄位
value_name='score' # 新的值欄位
)
多個識別欄位
df = pd.DataFrame({
'name': ['Alice', 'Bob'],
'class': ['A', 'B'],
'math': [90, 80],
'english': [85, 90]
})
df_long = df.melt(id_vars=['name', 'class'], var_name='subject', value_name='score')
pivot():長轉寬
df_long = pd.DataFrame({
'name': ['Alice', 'Alice', 'Bob', 'Bob'],
'subject': ['math', 'english', 'math', 'english'],
'score': [90, 85, 80, 90]
})
# 轉成寬格式
df_wide = df_long.pivot(index='name', columns='subject', values='score')
print(df_wide)
subject english math
name
Alice 85 90
Bob 90 80
如果有重複的組合,pivot() 會報錯,需要使用 pivot_table()。
stack() 和 unstack()
stack():將欄位層級轉成索引
df = pd.DataFrame({
'math': [90, 80],
'english': [85, 90]
}, index=['Alice', 'Bob'])
stacked = df.stack()
print(stacked)
Alice math 90
english 85
Bob math 80
english 90
dtype: int64
unstack():將索引層級轉成欄位
unstacked = stacked.unstack()
print(unstacked)
english math
Alice 85 90
Bob 90 80
指定層級
對於多層索引,可以指定要操作的層級:
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'))
# unstack 第一層(year)
print(df.unstack(level='year'))
wide_to_long()
處理有規則命名的欄位:
df = pd.DataFrame({
'name': ['Alice', 'Bob'],
'score_2023': [90, 80],
'score_2024': [92, 85]
})
# 轉成長格式
df_long = pd.wide_to_long(
df,
stubnames='score', # 欄位前綴
i='name', # 識別欄位
j='year', # 新的變數欄位名稱
sep='_' # 分隔符號
)
print(df_long.reset_index())
name year score
0 Alice 2023 90
1 Alice 2024 92
2 Bob 2023 80
3 Bob 2024 85
transpose():轉置
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6]
})
# 轉置(列欄互換)
df_t = df.T
# 或
df_t = df.transpose()
print(df_t)
0 1 2
A 1 2 3
B 4 5 6
explode():展開列表
將包含列表的儲存格展開成多列:
df = pd.DataFrame({
'name': ['Alice', 'Bob'],
'hobbies': [['reading', 'music'], ['sports']]
})
df_exploded = df.explode('hobbies')
print(df_exploded)
name hobbies
0 Alice reading
0 Alice music
1 Bob sports
實際應用
資料視覺化前處理
很多視覺化工具需要長格式資料:
# 原始寬格式
df = pd.DataFrame({
'month': ['Jan', 'Feb', 'Mar'],
'product_A': [100, 120, 150],
'product_B': [80, 90, 110]
})
# 轉成長格式以便繪圖
df_long = df.melt(id_vars='month', var_name='product', value_name='sales')
多時間點資料
df = pd.DataFrame({
'id': [1, 2],
'pre_test': [60, 70],
'post_test': [75, 85]
})
# 轉換
df_long = df.melt(id_vars='id', var_name='test_time', value_name='score')
df_long['test_time'] = df_long['test_time'].str.replace('_test', '')
建立交叉表
df = pd.DataFrame({
'gender': ['M', 'F', 'M', 'F', 'M'],
'city': ['Taipei', 'Tokyo', 'Taipei', 'Taipei', 'Tokyo'],
'count': [1, 1, 1, 1, 1]
})
# 使用 pivot_table 建立交叉表
cross_tab = df.pivot_table(index='gender', columns='city', values='count', aggfunc='sum', fill_value=0)