在資料分析過程中,經常需要在寬格式和長格式資料表格之間切換,以利於不同分析工具和模型的使用。Pandas 提供了多種方法來實作這些轉換,例如 stack、unstack、melt 和 wide_to_long 等。選擇哪種方法取決於資料的結構和分析目標。對於簡單的轉換,stack 和 melt 通常足以應付;而當需要從欄位名稱中提取資訊時,wide_to_long 則更為適用。理解這些方法的差異和應用場景,能有效提升資料處理效率,讓資料分析工作更加順暢。
資料表格轉換的技巧
從廣式轉換為長式資料表格
在資料建模中,我們常會遇到「廣式」資料表格,即每一行代表一個特定的狀態或實體,而不同的資料列則分別表示該實體的不同屬性。以水果生產資料為例,我們可以將各州生產的不同水果數量放在不同的列中:
import pandas as pd
data = {
"state": ["Texas", "Arizona", "Florida"],
"Apple": [12, 9, 0],
"Orange": [10, 7, 14],
"Banana": [40, 12, 190]
}
df = pd.DataFrame(data).set_index("state")
df = df.convert_dtypes(dtype_backend="numpy_nullable")
df
此圖示
假設我們想將這個廣式表格轉換為「長式」資料表格,即每一行代表某個州和某種水果的組合。這時我們可以使用 pd.DataFrame.stack 方法來實作。這個方法會將水果的列標籤移到行索引中,形成一個多層索引(MultiIndex):
stacked_df = df.stack()
stacked_df
內容解密:
此段程式碼首先建立了一個包含美國各州及其生產的各種水果數量的 DataFrame。接著使用 convert_dtypes 方法將 DataFrame 的型態轉換為支援可空型別(nullable types)。最後使用 stack 方法將原本的列標籤移到行索引中,形成一個多層索引(MultiIndex),使得每一行都代表一個州與某種水果的組合。
此圖示
還原為 DataFrame
通常,在呼叫 pd.DataFrame.stack 後,會繼續使用 pd.Series.reset_index 方法將多層索引轉換回 DataFrame,並且可以指定新的列名稱:
long_df = stacked_df.reset_index(name="number_grown")
long_df
內容解密:
在這段程式碼中,我們使用 reset_index 方法將之前轉換後的多層索引還原為一般 DataFrame。引數 name 用於指定新增加的一列名稱為 “number_grown”。
此圖示
長式轉換回廣式
有時候我們需要將長式資料表格轉換回廣式。這樣做可以讓資料更加緊湊、便於顯示和分析。例如,當我們需要總結資料時,使用兩維度來顯示通常比讓觀眾滾動觀看大量資料更有效。
讓我們從前面的長式資料開始:
stacked_series = long_df.set_index(["state", "fruit"])["number_grown"]
stacked_series.unstack()
內容解密:
在這段程式碼中,我們首先使用 set_index 機制將原本的 DataFrame 的列名設定為 MultiIndex(多重索引),然後使用 unstack() 函式將內層索引(fruits)展開成欄位。
測試 unstack 的 level
# 預設狀況下,unstack() 傾向將最內層的行索引移到欄位上。
# level=0 則是指將最外層的行索引移到欄位上。
# level="state" 則是根據行索引名稱來決定要移動哪一層。
wide_df_unstack_level_innermost = stacked_series.unstack()
wide_df_unstack_level_outmost = stacked_series.unstack(level=0)
wide_df_unstack_level_name = stacked_series.unstack(level="state")
print("Default unstack:")
print(wide_df_unstack_level_innermost)
print("\nUnstack by outermost level:")
print(wide_df_unstack_level_outmost)
print("\nUnstack by level name 'state':")
print(wide_df_unstack_level_name)
說明 reshape 的 unstack 的三種情境
假如你能正確理解 unstack 的三種情境,當然你就能正確運用:
- 預設狀況下 unstack() 是依照最內層的 MultiIndex 層級來操作。
- 指定 level=0 是依照最外層的 MultiIndex 層級來操作。
- 指定 level=name 是依照指定名稱來操作。
內容解密:
這段程式碼演示了 unstack() 函式的三種不同操作方式:預設情況下會展開最內層索引、指定展開最外層索引以及根據索引名稱展開。透過這些操作,你可以根據需求選擇適合自己的資料展開方式。
此圖示 default unstack:
特殊狀況
Unstack by outermost level:
Unstack by name state:
@startuml
note
無法自動轉換的 Plantuml 圖表
請手動檢查和調整
@enduml
Reshaping with pd.DataFrame.melt
在上述範例中,玄貓針對資料表格進行轉換時,是先把 state 放到 row index 中再進行 stack 操作。但在某些情況下,可能並不希望進行這樣的預處理步驟。這時可以使用 pd.DataFrame.melt 函式來直接轉換資料表格。
df = pd.DataFrame([["Texas", "apple", "orange", "banana"],["Arizona", "orange" "apple" "banana"],["Florida", "orange" "apple" "banana"]],columns=["state","fruit","value"])
df.convert_dtypes(dtype_backend="numpy_nullable")
df.melt(id_vars=["state"],var_name="fruit",value_name="number_grown")
內容解密:
在這段程式碼中,我們建立了一個包含各州和其對應水果數量的 DataFrame。接著利用 melt 函式直接將其從廣式轉換為長式格式。引數 id_vars=["state"] 指定不參與融化操作的欄位;var_name="fruit" 和 value_name="number_grown" 分別指定融化後新增欄位名稱。
特殊狀況:
此處很值得注意:
- 預設狀況下 column name 需要特別指定。
- 若是想只挑選特定 column 或排除特定 column 則可以使用 value_vars=[] 或 exclude_cols=[]。
拆解實戰案例:只挑選特定欄位融化:
df.melt(id_vars=["state"],var_name="fruit",value_name="number_grown",value_vars=["apple","orange"])
如果不希望包含 banana 的值則可以透過 value_vars=[“apple”,“orange”] 指定僅融化 apple 和 orange 的值。
內容解密:
這段程式碼演示瞭如何利用 pd.DataFrame.melt 函式來進行更精細控制的廣至長轉換。透過 value_vars 指定僅融化特定欄位(如 apple 和 orange),可以有效篩選出所需之資料。
擴充套件與轉換 DataFrame 格式
在資料科學的實務應用中,經常需要將資料在寬格式(wide format)和長格式(long format)之間進行轉換。這不僅有助於資料的視覺化和分析,還能夠滿足不同分析工具和模型的需求。本章將介紹如何使用 pandas 函式庫中的多種方法來進行這些轉換,並以具體案例說明其應用。
使用 pd.wide_to_long 轉換寬格式為長格式
在前文中,我們已經介紹了使用 pd.DataFrame.stack 和 pd.DataFrame.melt 將寬格式資料轉換為長格式。這些方法雖然功能強大,但當資料結構較為複雜時,可能會顯得繁瑣。這時,pd.wide_to_long 函式就派上了用場。
情境介紹
假設我們有一個 DataFrame,其中包含一個 widget 變數和四個代表業務季度銷售資料的列。每個銷售列都以 "quarter_" 作為字首:
import pandas as pd
df = pd.DataFrame([
["Widget 1", 1, 2, 4, 8],
["Widget 2", 16, 32, 64, 128],
], columns=["widget", "quarter_1", "quarter_2", "quarter_3", "quarter_4"])
df = df.convert_dtypes(dtype_backend="numpy_nullable")
資料展示
widget quarter_1 quarter_2 quarter_3 quarter_4
0 Widget 1 1 2 4 8
1 Widget 2 16 32 64 128
轉換方法
如果我們回到之前使用 pd.DataFrame.stack 的範例,可以透過以下方法將資料從寬格式轉換為長格式:
df.set_index("widget").stack().reset_index().rename(columns={
"level_1": "quarter",
0: "quantity",
})
結果展示
widget quarter quantity
0 Widget 1 quarter_1 1
1 Widget 1 quarter_2 2
2 Widget 1 quarter_3 4
3 Widget 1 quarter_4 8
4 Widget 2 quarter_1 16
5 Widget 2 quarter_2 32
6 Widget 2 quarter_3 64
7 Widget 2 quarter_4 128
使用 pd.DataFrame.melt 則可以更加簡潔地完成這個轉換:
df.melt(
id_vars=["widget"],
var_name="quarter",
value_name="quantity",
)
結果展示
widget quarter quantity
0 Widget 1 quarter_1 1
1 Widget 2 quarter_1 16
2 Widget 1 quarter_2 2
3 Widget 2 quarter_2 32
4 Widget 1 quarter_3 4
5 Widget 2 quarter_3 64
6 Widget 1 quarter_4 8
7 Widget quarter_4 (此處應該是Widget) . (此處應該是2) (此處應該是)
然而,這些方法並不能直接從列標籤中提取出新的變數。pd.wide_to_long 則能夠解決這個問題,將季度字首 "quarter_" 提取出來,留下數字部分:
pd.wide_to_long(
df,
i=["widget"],
stubnames="quarter_",
j="quarter"
).reset_index().rename(columns={"quarter_": "quantity"})
結果展示
widget quarter quantity
0 Widget (此處應該是1) . (此處應該是) (此處應該是)
內容解密:
i=["widget"]: 指定唯一識別變數。stubnames="quarter_": 指定要提取的列字首。j="quarter": 指定新的變數名稱。reset_index(): 還原索引。rename(columns={"quarter_": "quantity"}): 還原原來的列名。
在不同 DataFrame 轉換方法間的選擇
根據實際情況,可以選擇不同的轉換方法:
- 簡單且直接:
pd.DataFrame.stack和pd.DataFrame.melt是最常見且簡單的選擇。 - 結構複雜:當需要從列標籤中提取新變數時,
pd.wide_to_long是更好的選擇。
自動化與高效性
在實務中,我們經常需要將大量資料從寬格式轉換為長格式,或反之亦然。這些方法不僅能夠提高資料處理的效率,還能夠確保資料的完整性和一致性。
其他轉換方法
除了上述方法外,pandas 中還提供了其他轉換工具,如 pd.pivot 和 pd.pivot_table。這些工具不僅能夠進行格式轉換,還能夠進行資料聚合和分析。
情境介紹
假設我們有一個長格式的 DataFrame,包含州份、水果種類別、生產量和食用量等資訊:
df = pd.DataFrame([
["Texas", "apple", "養殖車間", "養殖車間", ],
["Arizona", "apple", "養殖車間", "養殖車間"],
],
columns=["state", "fruit", "number_grown"]
df = df.convert_dtypes(dtype_backend="numpy_nullable")
資料展示
state fruit number_grown number_eaten (此處應該沒有出現)
0 Texas apple (此處應該沒有出現) (此處應該沒有出現)
...
8 Florida banana (此處應該沒有出現) (此處應該沒有出現)
轉換方法
使用 pd.pivot可以直接將長格式資料轉換為寬格式:
df.pivot(index=["state"], columns=["fruit"])
結果展示
Florida0 (此處應該是) 0 (此處應該是)
Texas (此處應該沒有出現) (此處應該沒有出現)
內容解密:
index=["state"]: 指定行索引。columns=["fruit"]: 指定列索引。
自動化與高效性
在實務中,我們經常需要將大量資料從長格式轉換為寬格式。這些方法不僅能夠提高資料處理的效率,還能夠確保資料的完整性和一致性。