Pandas是Python資料科學領域的核心函式庫,其提供的資料重塑和轉換功能在資料分析中扮演著至關重要的角色。pivot函式能將資料從長格式轉換為寬格式,方便資料視覺化和分析。pivot_table更進一步允許在重塑的同時進行聚合操作,處理重複值。explode則能有效處理半結構化資料,將列表元素展開成獨立列,簡化資料處理流程。這些函式的組合使用能大幅提升資料整理效率,為後續分析奠定基礎。

資料重塑與轉換:深入理解 pandas 資料操作

在資料分析和處理的過程中,經常需要對資料進行重塑和轉換,以滿足不同的分析和應用需求。pandas 函式庫提供了強大的資料操作功能,其中包括 pd.DataFrame.pivotpd.pivot_tablepd.DataFrame.explode 等函式,可以幫助我們有效地重塑和轉換資料。

使用 pd.DataFrame.pivot 進行資料重塑

pd.DataFrame.pivot 函式可以將資料從長格式(long format)轉換為寬格式(wide format)。它根據指定的索引(index)和欄位(columns)進行資料重塑,並將剩餘的欄位轉換為新的欄位值。

import pandas as pd

# 建立範例資料
data = {
    "state": ["Arizona", "Florida", "Texas", "Arizona", "Florida", "Texas"],
    "fruit": ["apple", "apple", "apple", "banana", "banana", "banana"],
    "number_grown": [9, 0, 12, 12, 190, 40]
}
df = pd.DataFrame(data)

# 使用 pivot 進行資料重塑
wide_df = df.pivot(index="state", columns="fruit", values="number_grown")

print(wide_df)

內容解密:

  1. df.pivot(index="state", columns="fruit", values="number_grown"):將 state 欄位設為索引,fruit 欄位設為新的欄位名稱,number_grown 欄位的值填入對應的儲存格中。
  2. 資料重塑後,wide_df 成為一個寬格式的 DataFrame,其中每個水果種類別成為一個欄位,各州的水果產量填入對應的儲存格。

如果原始資料中存在重複的值,使用 pd.DataFrame.pivot 會導致錯誤。為瞭解決這個問題,可以使用 pd.pivot_table

使用 pd.pivot_table 進行資料聚合和重塑

pd.pivot_table 函式不僅可以重塑資料,還可以對資料進行聚合操作。它允許指定聚合函式,以處理重複的值。

import pandas as pd

# 建立範例資料
data = {
    "state": ["Texas", "Texas", "Arizona", "Arizona", "Texas", "Texas", "Arizona"],
    "fruit": ["apple", "apple", "apple", "apple", "orange", "orange", "orange"],
    "year": [2023, 2024, 2023, 2024, 2023, 2024, 2023],
    "number_grown": [10, 2, 3, 6, 5, 5, 7],
    "number_eaten": [6, 8, 7, 3, 2, 2, 2]
}
df = pd.DataFrame(data)

# 使用 pivot_table 進行資料聚合和重塑
pivot_table = pd.pivot_table(df, index="state", columns="fruit", values=["number_grown", "number_eaten"], aggfunc="sum")

print(pivot_table)

內容解密:

  1. pd.pivot_table(df, index="state", columns="fruit", values=["number_grown", "number_eaten"], aggfunc="sum"):將 state 欄位設為索引,fruit 欄位設為新的欄位名稱,對 number_grownnumber_eaten 欄位的值進行求和聚合。
  2. 資料重塑和聚合後,pivot_table 成為一個寬格式的 DataFrame,其中每個水果種類別成為一個欄位,各州的水果產量和食用量填入對應的儲存格,並進行求和聚合。

使用 pd.DataFrame.explode 將列表元素展開為獨立列

在處理半結構化資料時,經常遇到某個欄位包含列表或元組等非純量值的情況。pd.DataFrame.explode 函式可以將這些非純量值展開為獨立的列。

import pandas as pd

# 建立範例資料
data = {
    "employee_id": [1, 2, 3],
    "first_name": ["John", "Jane", "Joe"],
    "last_name": ["Smith", "Doe", "Schmoe"],
    "direct_reports": [[2, 3], [], []]
}
df = pd.DataFrame(data)

# 使用 explode 將列表元素展開為獨立列
df_exploded = df.explode("direct_reports")

print(df_exploded)

內容解密:

  1. df.explode("direct_reports"):將 direct_reports 欄位中的列表元素展開為獨立的列。
  2. 資料展開後,df_exploded 成為一個新的 DataFrame,其中每個直接下屬成為一個獨立的列。

資料重塑與轉置

在資料分析過程中,經常需要對資料進行重塑(reshaping)以滿足不同的分析需求。Pandas 提供了多種方法來實作資料重塑,包括 pd.DataFrame.explodepd.DataFrame.T

使用 pd.DataFrame.explode 展開資料

當資料中存在列表或陣列型別的欄位時,可以使用 pd.DataFrame.explode 將這些值展開成多行。

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)

# 將 direct_reports 欄位展開
df_exploded = df.explode("direct_reports").convert_dtypes(dtype_backend="numpy_nullable")

內容解密:

  1. pd.DataFrame.explode("direct_reports"):將 direct_reports 欄位中的列表值展開成多行。
  2. .convert_dtypes(dtype_backend="numpy_nullable"):轉換資料型別以支援 nullable 整數。

合併展開後的資料

可以將展開後的資料與原始資料合併,以取得更多資訊。

# 合併展開後的資料與原始資料
exploded = df.explode("direct_reports").convert_dtypes(dtype_backend="numpy_nullable")
merged_df = pd.merge(exploded, df.drop(columns=["direct_reports"]), how="left", left_on=["direct_reports"], right_on=["employee_id"], suffixes=("", "_direct_report"))

內容解密:

  1. pd.merge:根據 direct_reportsemployee_id 欄位進行左合併。
  2. suffixes=("", "_direct_report"):為合併後的欄位新增字尾,以區分原始欄位和合併欄位。

使用 PyArrow 的 struct 資料型別

PyArrow 提供了 struct 資料型別,可以用於處理半結構化資料。

import pyarrow as pa
import pandas as pd

# 定義 struct 資料型別
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)

# 使用 struct.explode 展開資料
ser_exploded = ser.struct.explode()

內容解密:

  1. pd.ArrowDtype(pa.struct(...)):定義 PyArrow 的 struct 資料型別。
  2. .struct.explode():將 struct 中的欄位展開成多個欄位。

使用 pd.DataFrame.T 進行轉置

轉置是將 DataFrame 的行和列互換的操作。

# 建立範例資料
df = pd.DataFrame([[1, 2, 3], [4, 5, 6]], columns=list("xyz"), index=list("ab"))

# 進行轉置
df_transposed = df.T

內容解密:

  1. df.T:將 DataFrame 的行和列互換。

為何需要轉置?

在某些情況下,轉置可以提高運算效率,特別是在避免使用 axis=1 引數時。

# 建立寬 DataFrame
import numpy as np
np.random.seed(42)
df = pd.DataFrame(np.random.randint(10, size=(2, 10_000)), index=list("ab"))

# 對比使用 axis=1 和轉置後的運算效率
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))

內容解密:

  1. df.sum(axis=1):對每行進行求和運算。
  2. df.T.sum():先轉置 DataFrame,然後對每列進行求和運算。

分組操作(Group By)

分組操作是資料分析中的基本任務之一,涉及將資料分成獨立的組,然後對每個組進行計算。

分割-應用-合併(Split-Apply-Combine)正規化

分組操作遵循分割-應用-合併正規化,其中應用步驟可以是還原(reduction)或轉換(transformation)。

圖表翻譯:

此圖表展示了分割-應用-合併正規化在還原和轉換中的應用。左側表示將資料分成多個組,中間表示對每個組進行計算,右側表示合併結果。

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 圖表翻譯:

rectangle "分割" as node1
rectangle "應用" as node2
rectangle "合併" as node3

node1 --> node2
node2 --> node3

@enduml

圖表翻譯: 此圖表說明瞭分組操作的流程,首先將原始資料分成多個獨立的組,然後對每個組進行計算,最後將結果合併。

Group By 基礎操作與應用

在 pandas 中,pd.DataFrame.groupby 方法是一個強大的工具,能夠對資料進行分組、套用函式並將結果合併。本文將介紹 Group By 的基本操作與進階應用。

Group By 基礎

Group By 的基本概念是將資料依據特定欄位進行分組,並對每個群組套用特定的函式。以下是一個簡單的範例:

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)

# 對 group 欄位進行 Group By,並計算 value 欄位的總和
grouped_df = df.groupby("group").sum()

# 顯示 Group By 結果
print("\nGroup By 結果:")
print(grouped_df)

內容解密:

  1. 建立範例資料:我們建立了一個簡單的 DataFrame,包含 groupvalue 兩個欄位。
  2. groupby 方法:使用 df.groupby("group")group 欄位進行分組。
  3. sum 方法:對每個群組的 value 欄位計算總和。
  4. 結果:最終結果顯示每個群組的 value 總和。

Group By 進階應用

除了基本的總和計算外,Group By 還支援多種聚合函式和轉換操作。

聚合函式

# 使用 agg 方法進行聚合
df.groupby("group").agg("sum")

# 使用 NamedAgg 控制輸出欄位名稱
df.groupby("group").agg(sum_of_value=pd.NamedAgg(column="value", aggfunc="sum"))

內容解密:

  1. agg 方法:可以傳入聚合函式名稱(如 “sum”)進行計算。
  2. NamedAgg:用於控制輸出欄位的名稱,使結果更易讀。

轉換操作

# 使用 transform 方法進行轉換
df.groupby("group").transform("sum")

# 計算每個值佔群組總和的比例
df[["value"]].div(df.groupby("group").transform("sum"))

內容解密:

  1. transform 方法:對每個群組進行轉換,保持原始索引。
  2. 比例計算:將每個 value 除以其所在群組的總和,得到比例。

常見 Group By 演算法

pandas 提供多種內建的聚合和轉換演算法,包括:

聚合演算法

  • sumprodmeanmedianvarstd
  • minmaxidxminidxmax
  • firstlastanyall

轉換演算法

  • cumprodcumsumcummincummax
  • rank

分組資料分析:Group By 的應用與實踐

在資料分析中,Group By 是一種常見的操作,用於根據特定欄位將資料分組並進行聚合運算。Pandas 提供了強大的 Group By 功能,使得資料分析變得更加高效。

Group By 的基本用法

在 Pandas 中,Group By 的基本用法是透過 df.groupby() 方法實作的。例如,假設我們有一個 DataFrame df,包含 groupvalue 兩個欄位,我們可以按照 group 欄位進行分組並計算每組的最大值:

df.groupby("group").max()

或者使用 agg() 方法:

df.groupby("group").agg("max")

兩種方法會得到相同的結果。

多欄位的 Group By 與聚合運算

在實際應用中,DataFrame 通常包含多個欄位。假設我們有一個 DataFrame,包含銷售資料和退貨資料,我們希望按照產品(widget)進行分組並計算總銷售額和總退貨額。

首先,建立一個範例 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")

方法一:選擇特定欄位進行聚合

df.groupby("widget")[["sales", "returns"]].agg("sum")

方法二:使用 pd.NamedAgg 進行聚合並重新命名欄位

df.groupby("widget").agg(
    sales_total=pd.NamedAgg(column="sales", aggfunc="sum"),
    returns_total=pd.NamedAgg(column="returns", aggfunc="sum"),
)

多重分組與多重聚合函式

Pandas 的 Group By 功能也支援多重分組和多重聚合函式。例如,我們可以按照 widgetregion 兩個欄位進行分組,並計算多個聚合指標:

df.groupby(["widget", "region"]).agg(
    sales_total=pd.NamedAgg("sales", "sum"),
    returns_total=pd.NamedAgg("returns", "sum"),
    sales_min=pd.NamedAgg("sales", "min"),
    returns_min=pd.NamedAgg("returns", "min"),
)

內容解密:

  1. 多重分組:透過傳入一個列表給 groupby() 方法,可以實作多重分組。
  2. 多重聚合函式:使用 agg() 方法並傳入多個 pd.NamedAgg 物件,可以對不同的欄位應用不同的聚合函式。
  3. 結果解析:結果中會顯示每個分組的多個聚合指標,有助於更全面地瞭解資料特徵。

自定義聚合函式

雖然 Pandas 提供了許多內建的聚合函式,但在某些情況下,我們仍需要自定義聚合函式。例如,計算每組的眾數(mode)。由於 Pandas 的 groupby() 不直接支援 mode 操作,我們需要自行實作。

df = pd.DataFrame([
    ["group_a", 42],
    ["group_a", 555],
    ["group_a", 42],
    ["group_a", 555],
    ["group_b", 0],
], columns=["group", "value"])

對於自定義函式,可以透過 apply() 方法實作。