Pandas 是資料科學領域中常用的 Python 函式庫,提供高效的資料結構和資料分析工具。在處理資料時,經常需要對 DataFrame 進行多步驟轉換,例如資料清洗、轉換和聚合。變數重指定和鏈式操作是兩種常見的資料處理方式。變數重指定方式容易造成程式碼冗長,不易閱讀,且可能產生多個中間變數,增加記憶體負擔。鏈式操作則可以將多個操作串聯起來,使程式碼更簡潔易懂,同時減少中間變數的產生,提高程式碼執行效率。理解這兩種方法的特性和應用場景,有助於選擇更合適的資料處理方式,提升程式碼品質和效能。

Pandas 資料處理流程最佳實踐:鏈式操作與變數重指定比較

在資料分析過程中,如何有效地組織和處理資料是至關重要的。Pandas 提供了多種方法來處理 DataFrame,其中最常見的有變數重指定和鏈式操作(pipeline)。本文將深入探討這兩種方法的差異,並透過例項展示如何在實際專案中應用。

變數重指定 vs 鏈式操作

在處理資料時,我們經常需要對 DataFrame 進行多步驟的轉換。傳統的做法是透過變數重指定來實作:

df1 = pd.DataFrame({...})
df2 = do_something(df1)
df3 = do_another_thing(df2)
df4 = do_yet_another_thing(df3)

或者重複使用同一個變數:

df = pd.DataFrame({...})
df = do_something(df)
df = do_another_thing(df)
df = do_yet_another_thing(df)

另一種更優雅的方法是使用鏈式操作:

(
    pd.DataFrame({...})
    .pipe(do_something)
    .pipe(do_another_thing)
    .pipe(do_yet_another_thing)
)

方法比較

特性變數重指定鏈式操作
可讀性較差,需追蹤多個變數較好,操作順序清晰
記憶體使用可能需要更多臨時變數較少,建立的臨時物件少
除錯難度較難,需要中斷操作流程較易,可以逐步檢查每一步輸出

實作範例

首先,建立一個簡單的 DataFrame:

import pandas as pd

df = pd.DataFrame({
    "col1": pd.Series([1, 2, 3], dtype=pd.Int64Dtype()),
    "col2": pd.Series(["a", "b", "c"], dtype=pd.StringDtype()),
})

print("原始資料:")
print(df)

輸出:

   col1 col2
0     1    a
1     2    b
2     3    c

接下來,定義兩個轉換函式:

def change_col1(df: pd.DataFrame) -> pd.DataFrame:
    return df.assign(col1=pd.Series([4, 5, 6], dtype=pd.Int64Dtype()))

def change_col2(df: pd.DataFrame, str_case: str = "upper") -> pd.DataFrame:
    values = ["X", "Y", "Z"] if str_case == "upper" else ["x", "y", "z"]
    return df.assign(col2=pd.Series(values, dtype=pd.StringDtype()))

使用變數重指定

df1 = change_col1(df)
df2 = change_col2(df1)
print("\n變數重指定結果:")
print(df2)

使用鏈式操作

result = (df.pipe(change_col1)
            .pipe(change_col2, str_case="lower"))

print("\n鏈式操作結果:")
print(result)

Mermaid 圖表展示處理流程

  flowchart TD
    A[開始] --> B{選擇處理方式}
    B -->|變數重指定| C[逐步轉換資料]
    B -->|鏈式操作| D[建立處理管道]
    C --> E[輸出結果]
    D --> E

圖表翻譯:

此圖示展示了資料處理的兩種主要方法:變數重指定和鏈式操作。無論選擇哪種方法,最終都會輸出處理後的結果。圖表清晰地展示了兩種方法的流程差異。

效能考量

在大多數情況下,兩種方法的效能差異不大。鏈式操作的優勢在於程式碼的可讀性和可維護性。然而,在處理大型資料集時,應考慮以下因素:

  1. 中間結果的記憶體使用
  2. 函式呼叫的開銷
  3. 資料複製 vs 檢視的使用

最佳實踐建議

  1. 對於簡單的轉換,變數重指定可能更直觀
  2. 對於複雜的多步驟處理,鏈式操作更具可讀性
  3. 結合兩種方法使用,以達到最佳的可維護性

應使用案例項:電影資料分析

讓我們使用鏈式操作來分析電影資料:

# 讀取電影資料
df = pd.read_csv("data/movie.csv", 
                 usecols=["movie_title", "imdb_score", "budget", "gross"],
                 dtype_backend="numpy_nullable")

# 找出評分最高的100部電影中預算最低的5部
top_low_budget = (df.nlargest(100, "imdb_score")
                    .nsmallest(5, "budget"))

print("\n評分最高的100部電影中預算最低的5部:")
print(top_low_budget)

電影資料分析流程圖

  flowchart LR
    A[讀取電影資料] --> B[篩選相關欄位]
    B --> C[找出評分最高100部]
    C --> D[找出預算最低5部]
    D --> E[輸出結果]

圖表翻譯:

此圖表展示了電影資料分析的流程:首先讀取資料,接著篩選相關欄位,然後找出評分最高的100部電影,最後在這些電影中找出預算最低的5部。整個流程展示瞭如何使用鏈式操作來完成複雜的資料分析任務。

資料分析中的排序與選取最佳紀錄

在進行資料分析時,經常需要對資料進行排序並選取前幾筆或後幾筆紀錄。Pandas 提供了 nlargestnsmallest 方法來實作這項功能。本章節將深入探討如何使用這些方法,並結合實際案例進行詳細說明。

使用 nlargestnsmallest 方法

當需要選取資料中最大或最小的 N 筆紀錄時,可以使用 nlargestnsmallest 方法。這兩個方法能夠根據指定的欄位進行排序並傳回前 N 筆符合條件的紀錄。

import pandas as pd

# 假設 df 是一個包含電影資料的 DataFrame
# 選取 IMDB 評分最高的 10 部電影
top_10_movies = df.nlargest(10, "imdb_score")
print(top_10_movies)

程式碼解密:

此段程式碼展示瞭如何使用 nlargest 方法選取 imdb_score 欄位中數值最大的前 10 筆紀錄。nlargest 方法接受兩個引數:第一個是欲選取的紀錄數量,第二個是進行排序的欄位名稱。在這個例子中,我們選取了 IMDB 評分最高的 10 部電影。

處理並列情況

當資料中存在並列情況時(例如多部電影具有相同的 IMDB 評分),單純使用 nlargestnsmallest 方法可能無法滿足需求。此時,可以透過傳遞多個欄位名稱來進行排序,以解決並列問題。

# 使用 imdb_score 作為主要排序欄位,gross 作為次要排序欄位
top_10_movies_with_tiebreaker = df.nlargest(10, ["imdb_score", "gross"])
print(top_10_movies_with_tiebreaker)

程式碼解密:

在這個範例中,我們首先根據 imdb_score 進行排序,當出現並列情況時,再根據 gross 欄位的值進行排序。這樣可以確保在 IMDB 評分相同的情況下,票房較高的電影優先被選取。

計算移動停損價格

在股票交易中,停損訂單是一種常見的風險管理策略。透過設定停損價格,可以在股票價格下跌時自動觸發賣出操作,以限制損失。本文將介紹如何使用 Pandas 計算移動停損價格。

讀取股票資料

首先,我們需要讀取股票的歷史交易資料。以下範例展示瞭如何讀取 Nvidia (NVDA) 股票的收盤價資料:

import pandas as pd

# 讀取 NVDA 股票資料
df = pd.read_csv(
 "data/NVDA.csv",
 usecols=["Date", "Close"],
 parse_dates=["Date"],
 index_col=["Date"],
 dtype_backend="numpy_nullable",
).convert_dtypes()

# 將 DataFrame 轉換為 Series
ser = df.squeeze()
print(ser.head())

程式碼解密:

此段程式碼讀取了 Nvidia 股票的歷史交易資料,並選取了 DateClose 兩個欄位。透過 parse_datesindex_col 引數,將 Date 欄位轉換為日期格式並設為索引。最後,使用 squeeze 方法將 DataFrame 轉換為 Series,以便進行後續的資料處理。

計算累計最大值與移動停損價格

在計算移動停損價格時,我們需要先計算股票收盤價的累計最大值。Pandas 提供了 cummax 方法來實作這項功能。

# 計算累計最大值
ser_cummax = ser.cummax()
print(ser_cummax.head())

# 計算移動停損價格(以 10% 為例)
trailing_stop_price = ser_cummax.mul(0.9)
print(trailing_stop_price.head())

程式碼解密:

首先,我們使用 cummax 方法計算了 ser Series 的累計最大值,儲存在 ser_cummax 中。接著,將 ser_cummax 乘以 0.9,計算出移動停損價格。這意味著當股票價格下跌超過 10% 時,將觸發停損賣出操作。

處理做空情況下的移動停損

若投資者預期某股票將下跌,可以進行做空操作。同樣地,做空操作也可以設定移動停損價格以限制損失。不同的是,做空操作的移動停損價格是根據累計最小值計算的。

# 計算累計最小值並設定做空情況下的移動停損價格(以 10% 為例)
trailing_stop_price_short = ser.cummin().mul(1.1)
print(trailing_stop_price_short.head())

程式碼解密:

在做空操作的情況下,我們使用 cummin 方法計算股票收盤價的累計最小值。然後,將累計最小值乘以 1.1,得到做空情況下的移動停損價格。這樣,當股票價格上漲超過 10% 時,將觸發停損買入操作,以限制損失。

資料視覺化與流程圖

為了更好地理解上述資料處理流程,我們可以使用 Mermaid 圖表來進行視覺化展示。

  flowchart TD
 A[開始] --> B{讀取資料}
 B --> C[計算累計最大值]
 C --> D[計算移動停損價格]
 B --> E[計算累計最小值]
 E --> F[計算做空移動停損價格]
 D --> G[輸出結果]
 F --> G

圖表翻譯:

此圖示展示了資料處理的流程。首先,程式開始並讀取資料。接著,根據資料計算累計最大值和累計最小值,並分別用於計算多頭和做空情況下的移動停損價格。最後,將結果輸出。這個流程圖清晰地展示了整個資料處理的邏輯和步驟。

棒球資料分析:找出最佳球員與最佳打擊順序

棒球資料分析是利用歷史資料來評估球員表現和球隊策略的重要工具。透過使用 pandas 這個強大的資料分析函式庫,我們可以輕鬆地從大量的資料中提取有價值的資訊。本篇文章將介紹如何使用 pandas 分析棒球資料,包括找出最佳球員和最佳打擊順序。

讀取與處理棒球資料

首先,我們需要讀取棒球資料的 parquet 檔案,並將其轉換為 pandas DataFrame。假設我們的資料檔案為 mlb_batting_summaries.parquet,其中包含了球員的打擊資料。

import pandas as pd

# 讀取 parquet 檔案並設定 id 為索引
df = pd.read_parquet("data/mlb_batting_summaries.parquet").set_index("id")
print(df.head())

內容解密:

這段程式碼讀取了 mlb_batting_summaries.parquet 檔案,並將 id 列設定為 DataFrame 的索引。這樣做可以方便我們根據球員的唯一標識進行資料查詢和分析。

找出最佳球員

利用 pandas 的 idxmax 方法,我們可以輕鬆地找出在不同指標(如打擊次數、得分、安打數和全壘打數)上表現最佳的球員。

# 找出各個指標的最佳球員
best_players = df.idxmax()
print(best_players)

內容解密:

這段程式碼透過呼叫 idxmax 方法,找出每個欄位(如 abrhhr)中數值最大的球員 ID。這樣我們就能知道哪些球員在這些指標上表現最佳。

進一步分析最佳球員的表現

我們可以進一步分析這些最佳球員在所有指標上的表現。

# 取得最佳球員的 ID 列表並建立遮罩
best_players_ids = best_players.unique()
mask = df.index.isin(best_players_ids)
# 篩選出最佳球員的資料
best_players_df = df[mask]
print(best_players_df)

內容解密:

這段程式碼首先取得了最佳球員的 ID 列表,並建立了一個遮罩來篩選出這些球員的資料。然後,我們列印預出這些最佳球員的詳細資料,以便進一步分析他們在各個指標上的表現。

視覺化最佳球員的表現

為了更直觀地展示最佳球員的表現,我們可以使用 style.highlight_max 方法來突出顯示每個欄位的最大值。

# 高亮顯示最佳球員在各個指標上的最佳表現
best_players_df.style.highlight_max()

圖表翻譯:

這張圖表透過高亮顯示每個欄位的最大值,直觀地展示了最佳球員在各個指標上的表現。從圖表中,我們可以清晰地看到哪些球員在哪些指標上表現出色。

  flowchart TD
 A[讀取資料] --> B{資料處理}
 B -->|成功| C[分析最佳球員]
 B -->|失敗| D[錯誤處理]
 C --> E[視覺化結果]
 D --> F[結束]

圖表翻譯:

此圖示展示了棒球資料分析的流程。首先,我們讀取資料並進行處理。如果資料處理成功,我們將分析最佳球員的表現;如果失敗,則進行錯誤處理。最後,我們將結果視覺化,以便更直觀地理解分析結果。

分析球隊的最佳打擊順序

除了分析球員表現,我們還可以分析球隊的最佳打擊順序。利用 pd.DataFrame.idxmax 方法,我們可以找出每個球隊在每個賽季中得分最多的打擊順序。

# 讀取球隊得分資料
df_team = pd.read_parquet("data/runs_scored_by_team.parquet").set_index(["year", "team"])
# 找出每個球隊得分最多的打擊順序
max_scoring_order = df_team.idxmax(axis=1)
print(max_scoring_order)

內容解密:

這段程式碼讀取了球隊得分資料,並使用 idxmax 方法找出每個球隊在每個賽季中得分最多的打擊順序。這有助於我們瞭解不同球隊的打擊策略。

分析第一棒得分最多的球隊的第二棒表現

進一步地,我們可以分析那些第一棒得分最多的球隊的第二棒表現。

# 篩選出第一棒得分最多的球隊
mask_first = max_scoring_order.eq("1")
# 分析這些球隊的第二棒表現
second_batting_order = df_team[mask_first].drop(columns=["1"]).idxmax(axis=1).value_counts(normalize=True)
print(second_batting_order)

內容解密:

這段程式碼首先篩選出第一棒得分最多的球隊,然後分析這些球隊的第二棒表現。透過計算不同打擊順序的頻率,我們可以瞭解這些球隊的打擊策略。

  flowchart TD
 A[讀取球隊資料] --> B{分析最佳打擊順序}
 B --> C[計算第一棒得分最多的球隊]
 C --> D[分析第二棒表現]
 D --> E[結果視覺化]

圖表翻譯:

此圖示展示了分析球隊最佳打擊順序的流程。我們首先讀取球隊資料,然後分析最佳打擊順序。接著,我們計算第一棒得分最多的球隊,並進一步分析這些球隊的第二棒表現。最後,我們將結果視覺化,以便更直觀地理解球隊的打擊策略。