Pandas是資料科學領域中不可或缺的工具,其提供的資料重塑與轉換功能,讓資料處理更加靈活高效。explode函式能有效處理列表或陣列型態的資料,將其展開成個別列,方便後續分析。搭配merge函式,可以將展開後的資料與原始資料合併,取得更全面的資訊。PyArrow的struct資料型別則提供更進階的巢狀資料處理方式,提升資料處理效率。轉置操作除了改變資料的行列結構外,在特定場景下還能最佳化運算效能,例如對每一列進行聚合運算時,轉置後再進行列運算能顯著提升速度。

資料重塑與轉換

在資料分析的過程中,經常需要對資料進行重塑(reshaping)以滿足不同的分析需求。Pandas 提供了多種方法來實作資料重塑,其中包括 pd.DataFrame.explodepd.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.explodedirect_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_reportsemployee_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 提供了一系列方法來實作群組運算,包括 groupbysplit-apply-combine 等。

Split-Apply-Combine 正規化

Split-Apply-Combine 正規化是一種常見的資料分析方法。它包括三個步驟:

  1. Split:將資料分成多個群組。
  2. Apply:對每個群組進行運算。
  3. 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)

內容解密:

  1. 我們首先匯入必要的pandas函式庫。
  2. 建立一個包含兩列(groupvalue)的DataFrame,代表不同的群組及其對應的值。
  3. 使用convert_dtypes方法將DataFrame的資料型別轉換為numpy_nullable,以便更好地處理缺失值。

分組與匯總計算

接下來,我們使用groupby方法對資料進行分組,並計算每個群組的匯總值:

# 按照group欄位進行分組,並計算每個群組的總和
grouped_sum = df.groupby("group").sum()

print("\n每個群組的總和:")
print(grouped_sum)

內容解密:

  1. df.groupby("group")按照group欄位對資料進行分組,傳回一個DataFrameGroupBy物件。
  2. .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)

內容解密:

  1. 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)

內容解密:

  1. NamedAgg類別允許我們指定匯總結果的欄位名稱(sum_of_value)和對應的匯總函式("sum")。

常用的GroupBy匯總與轉換函式

pandas提供了多種內建的匯總與轉換函式,可以直接應用於GroupBy物件。常見的匯總函式包括:

  • summeanminmax
  • anyall
  • idxminidxmax
  • varstd
  • semskew
  • firstlast

常見的轉換函式包括:

  • cumprodcumsum
  • cummincummax
  • rank

這些函式能夠幫助我們進行更複雜的資料分析與處理。

分組資料分析:多欄位處理與自訂函式應用

在進行資料分析時,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

多重分組

我們也可以根據多個欄位進行分組,例如同時根據 widgetregion

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)

內容解密:

  1. 自訂函式 custom_mode:此函式計算輸入 Series 的眾數。如果眾數唯一,則傳回該值;如果有多個眾數,則傳回包含所有眾數的列表。
  2. groupbyapply 方法:使用 groupby 對 DataFrame 進行分組後,透過 apply 方法將自訂函式應用於每組的指定欄位(此例中為 sales)。
  3. 結果解讀:輸出的結果顯示每個 widget 的銷售量眾數。