Pandas是資料科學領域中不可或缺的工具,其提供的資料重塑與轉換功能,讓資料處理更加靈活高效。explode函式能有效處理列表或陣列型態的資料,將其展開成個別列,方便後續分析。搭配merge函式,可以將展開後的資料與原始資料合併,取得更全面的資訊。PyArrow的struct資料型別則提供更進階的巢狀資料處理方式,提升資料處理效率。轉置操作除了改變資料的行列結構外,在特定場景下還能最佳化運算效能,例如對每一列進行聚合運算時,轉置後再進行列運算能顯著提升速度。
資料重塑與轉換
在資料分析的過程中,經常需要對資料進行重塑(reshaping)以滿足不同的分析需求。Pandas 提供了多種方法來實作資料重塑,其中包括 pd.DataFrame.explode 和 pd.DataFrame.T。
使用 pd.DataFrame.explode 展開資料
當資料中存在列表或陣列等結構時,可以使用 pd.DataFrame.explode 將這些結構展開成獨立的列。例如,假設我們有一個員薪水料的 DataFrame,其中包含直接下屬的員工 ID 列表:
import pandas as pd
data = [
{"employee_id": 1, "first_name": "John", "last_name": "Smith", "direct_reports": [2, 3]},
{"employee_id": 2, "first_name": "Jane", "last_name": "Doe", "direct_reports": []},
{"employee_id": 3, "first_name": "Joe", "last_name": "Schmoe", "direct_reports": []}
]
df = pd.DataFrame(data)
df = df.convert_dtypes(dtype_backend="numpy_nullable")
print(df)
輸出結果:
employee_id first_name last_name direct_reports
0 1 John Smith [2, 3]
1 2 Jane Doe []
2 3 Joe Schmoe []
使用 pd.DataFrame.explode 將 direct_reports 欄位展開:
exploded_df = df.explode("direct_reports").convert_dtypes(dtype_backend="numpy_nullable")
print(exploded_df)
輸出結果:
employee_id first_name last_name direct_reports
0 1 John Smith 2
0 1 John Smith 3
1 2 Jane Doe <NA>
2 3 Joe Schmoe <NA>
內容解密:
df.explode("direct_reports")將direct_reports欄位中的列表展開成獨立的列。- 如果原來的值是空列表,則展開後的結果為
<NA>。
合併展開後的資料
接下來,我們可以將展開後的資料與原來的員薪水料合併,得到每個員工的直接下屬資訊:
merged_df = pd.merge(
exploded_df,
df.drop(columns=["direct_reports"]),
how="left",
left_on=["direct_reports"],
right_on=["employee_id"],
suffixes=("", "_direct_report"),
)
print(merged_df)
輸出結果:
employee_id first_name last_name direct_reports employee_id_direct_report first_name_direct_report last_name_direct_report
0 1 John Smith 2 2.0 Jane Doe
1 1 John Smith 3 3.0 Joe Schmoe
2 2 Jane Doe <NA> <NA> <NA> <NA>
3 3 Joe Schmoe <NA> <NA> <NA> <NA>
內容解密:
pd.merge將展開後的資料與原來的員薪水料合併,根據direct_reports和employee_id進行匹配。suffixes=("", "_direct_report")用於區分合併後的欄位名稱。
使用 PyArrow 的 struct 資料型別
PyArrow 提供了一種 struct 資料型別,可以用於處理巢狀資料。使用 pd.Series.struct.explode 可以將 struct 中的成員展開成獨立的欄位:
import pyarrow as pa
dtype = pd.ArrowDtype(pa.struct([
("int_col", pa.int64()),
("str_col", pa.string()),
("float_col", pa.float64()),
]))
ser = pd.Series([
{"int_col": 42, "str_col": "Hello, ", "float_col": 3.14159},
{"int_col": 555, "str_col": "world!", "float_col": 3.14159},
], dtype=dtype)
print(ser.struct.explode())
輸出結果:
int_col str_col float_col
0 42 Hello, 3.14159
1 555 world! 3.14159
內容解密:
pd.Series.struct.explode將 struct 中的成員展開成獨立的欄位。
資料轉置
資料轉置是指將 DataFrame 的行和列進行交換。Pandas 提供了 pd.DataFrame.T 方法來實作資料轉置:
df = pd.DataFrame([
[1, 2, 3],
[4, 5, 6],
], columns=list("xyz"), index=list("ab"))
print(df.T)
輸出結果:
a b
x 1 4
y 2 5
z 3 6
圖表翻譯:
此圖示呈現了 DataFrame 在轉置前後的結構變化。轉置後,原來的行變成了列,原來的列變成了行。
此圖示
使用資料轉置最佳化效能
在某些情況下,使用資料轉置可以最佳化運算效能。例如,當需要對 DataFrame 的每一行進行聚合運算時,可以先將 DataFrame 轉置,然後對列進行聚合運算:
import numpy as np
np.random.seed(42)
df = pd.DataFrame(
np.random.randint(10, size=(2, 10_000)),
index=list("ab"),
)
def baseline_sum():
for _ in range(100):
df.sum(axis=1)
def transposed_sum():
transposed = df.T
for _ in range(100):
transposed.sum()
import timeit
print(timeit.timeit(baseline_sum, number=1))
print(timeit.timeit(transposed_sum, number=1))
輸出結果顯示,轉置後的運算效能明顯優於原始的運算方法。
圖表翻譯:
此圖示比較了兩種不同運算方法的效能差異。結果表明,轉置後的運算方法具有更好的效能。
此圖示
群組運算
群組運算是資料分析中的重要步驟。Pandas 提供了一系列方法來實作群組運算,包括 groupby、split-apply-combine 等。
Split-Apply-Combine 正規化
Split-Apply-Combine 正規化是一種常見的資料分析方法。它包括三個步驟:
- Split:將資料分成多個群組。
- Apply:對每個群組進行運算。
- Combine:將運算結果合併起來。
圖表翻譯:
此圖示呈現了 Split-Apply-Combine 正規化的流程。該正規化首先將資料分成多個群組,然後對每個群組進行運算,最後將結果合併起來。
此圖示
在實際應用中,Split-Apply-Combine 正規化可以用於各種資料分析任務,例如計算每個群組的平均值、總和等。Pandas 的 groupby 方法提供了一種方便的方式來實作 Split-Apply-Combine 正規化。接下來的章節將詳細介紹如何使用 Pandas 的 groupby 方法進行群組運算。
使用pandas進行資料分組(Group By)分析
在資料分析中,分組(Group By)是一種常見的操作,能夠幫助我們根據特定的欄位對資料進行分組,並對每個分組進行匯總計算。pandas函式庫提供了強大的groupby方法,能夠讓我們輕鬆實作資料的分組與分析。
Group By基礎操作
首先,我們建立一個簡單的DataFrame來示範groupby的基本用法:
import pandas as pd
# 建立範例資料
df = pd.DataFrame([
["group_a", 0],
["group_a", 2],
["group_b", 1],
["group_b", 3],
["group_b", 5],
], columns=["group", "value"])
# 將資料型別轉換為numpy_nullable
df = df.convert_dtypes(dtype_backend="numpy_nullable")
# 顯示原始資料
print("原始資料:")
print(df)
內容解密:
- 我們首先匯入必要的pandas函式庫。
- 建立一個包含兩列(
group和value)的DataFrame,代表不同的群組及其對應的值。 - 使用
convert_dtypes方法將DataFrame的資料型別轉換為numpy_nullable,以便更好地處理缺失值。
分組與匯總計算
接下來,我們使用groupby方法對資料進行分組,並計算每個群組的匯總值:
# 按照group欄位進行分組,並計算每個群組的總和
grouped_sum = df.groupby("group").sum()
print("\n每個群組的總和:")
print(grouped_sum)
內容解密:
df.groupby("group")按照group欄位對資料進行分組,傳回一個DataFrameGroupBy物件。.sum()方法對每個群組的value欄位進行求和計算,傳回結果是一個新的DataFrame。
分組後的索引處理
預設情況下,groupby操作會將分組欄位作為結果DataFrame的索引。如果希望保留原始欄位,可以使用as_index=False引數:
# 保留group欄位作為普通欄位
grouped_sum_no_index = df.groupby("group", as_index=False).sum()
print("\n保留group欄位的結果:")
print(grouped_sum_no_index)
內容解密:
as_index=False引數告訴pandas不要將分組欄位設為索引,而是保留為普通欄位。
使用NamedAgg自定義匯總欄位名稱
在某些情況下,我們可能需要自定義匯總結果的欄位名稱。pandas提供了NamedAgg類別來實作這一點:
from pandas import NamedAgg
# 使用NamedAgg自定義匯總欄位名稱
custom_agg = df.groupby("group").agg(
sum_of_value=NamedAgg(column="value", aggfunc="sum")
)
print("\n自定義匯總欄位名稱的結果:")
print(custom_agg)
內容解密:
NamedAgg類別允許我們指定匯總結果的欄位名稱(sum_of_value)和對應的匯總函式("sum")。
常用的GroupBy匯總與轉換函式
pandas提供了多種內建的匯總與轉換函式,可以直接應用於GroupBy物件。常見的匯總函式包括:
sum、mean、min、maxany、allidxmin、idxmaxvar、stdsem、skewfirst、last
常見的轉換函式包括:
cumprod、cumsumcummin、cummaxrank
這些函式能夠幫助我們進行更複雜的資料分析與處理。
分組資料分析:多欄位處理與自訂函式應用
在進行資料分析時,Group By 是一個強大的工具,能夠根據指定的欄位對資料進行分組並進行各種聚合運算。本文將介紹如何在 Pandas 中使用 Group By 對多個欄位進行處理,以及如何應用自訂函式。
多欄位分組與聚合
當處理包含多個欄位的 DataFrame 時,我們可能需要根據某個或某些欄位進行分組,並對其他欄位進行聚合運算。以下是一個例子:
import pandas as pd
# 建立範例 DataFrame
df = pd.DataFrame([
["North", "Widget A", "Jan", 10, 2],
["North", "Widget B", "Jan", 4, 0],
["South", "Widget A", "Jan", 8, 3],
["South", "Widget B", "Jan", 12, 8],
["North", "Widget A", "Feb", 3, 0],
["North", "Widget B", "Feb", 7, 0],
["South", "Widget A", "Feb", 11, 2],
["South", "Widget B", "Feb", 13, 4],
], columns=["region", "widget", "month", "sales", "returns"])
df = df.convert_dtypes(dtype_backend="numpy_nullable")
對 widget 欄位進行分組並計算總和
df_grouped = df.groupby("widget")[["sales", "returns"]].agg("sum")
print(df_grouped)
輸出結果:
sales returns
widget
Widget A 32 7
Widget B 36 12
使用 pd.NamedAgg 自訂輸出欄位名稱
df_grouped_named = df.groupby("widget").agg(
sales_total=pd.NamedAgg(column="sales", aggfunc="sum"),
returns_total=pd.NamedAgg(column="returns", aggfunc="sum"),
)
print(df_grouped_named)
輸出結果:
sales_total returns_total
widget
Widget A 32 7
Widget B 36 12
多重分組
我們也可以根據多個欄位進行分組,例如同時根據 widget 和 region:
df_grouped_multi = df.groupby(["widget", "region"]).agg(
sales_total=pd.NamedAgg("sales", "sum"),
returns_total=pd.NamedAgg("returns", "sum"),
)
print(df_grouped_multi)
輸出結果:
sales_total returns_total
widget region
Widget A North 13 2
South 19 5
Widget B North 11 0
South 25 12
自訂函式應用
雖然 Pandas 提供了許多內建的聚合函式,但有時我們仍需要使用自訂函式。以下是一個範例,展示如何計算每組的眾數(mode):
def custom_mode(series):
mode_result = series.mode()
if len(mode_result) == 1:
return mode_result.iloc[0]
else:
return list(mode_result)
df_mode = df.groupby("widget")["sales"].apply(custom_mode)
print(df_mode)
內容解密:
- 自訂函式
custom_mode:此函式計算輸入 Series 的眾數。如果眾數唯一,則傳回該值;如果有多個眾數,則傳回包含所有眾數的列表。 groupby與apply方法:使用groupby對 DataFrame 進行分組後,透過apply方法將自訂函式應用於每組的指定欄位(此例中為sales)。- 結果解讀:輸出的結果顯示每個 widget 的銷售量眾數。