Pandas 提供了 Group By 和視窗函式等強大工具,方便我們對資料進行更精細的分析。Group By 可以根據指定的欄位將資料分組,並對每個分組應用聚合函式,例如計算總和、平均值等。視窗函式則允許我們在資料的不同子集上進行計算,例如計算移動平均、移動總和等,這在時間序列分析中特別有用。本文將會示範如何使用這些函式來分析股票價格走勢、計算電影評分以及處理棒球資料。我們也會探討如何使用自訂函式來滿足更複雜的分析需求,以及如何使用 pd.Grouper 處理時間序列資料。最後,我們還會使用視覺化工具來呈現分析結果,讓資料更容易理解和解讀。
分組與視窗運算:深入解析pandas中的Group By與視窗函式
在資料分析與處理中,pandas函式庫提供了強大的分組(Group By)與視窗運算(Window Operations)功能,讓我們能夠更靈活地對資料進行聚合運算與滑動計算。本文將深入探討這兩個重要概念,並結合實際範例展示其應用。
分組運算(Group By)與自訂函式
當我們需要對資料進行分組並套用自訂函式時,pandas的DataFrameGroupBy.apply()
方法提供了極大的彈性。這個方法允許我們對每個分組套用任意函式,並根據函式的回傳值推斷最合適的輸出結構。
import pandas as pd
# 建立範例資料
data = {
'group': ['group_a', 'group_a', 'group_b', 'group_b'],
'value': [1, 2, 3, 4]
}
df = pd.DataFrame(data)
# 定義自訂函式
def custom_function(group):
return group['value'].sum()
# 分組並套用自訂函式
result = df.groupby('group', observed=True).apply(custom_function, include_groups=False)
print(result)
程式碼解析:
- 我們首先建立了一個包含分組欄位和數值欄位的DataFrame。
- 定義了一個簡單的自訂函式
custom_function
,用於計算每個分組的數值總和。 - 使用
groupby()
方法對資料進行分組,並透過apply()
套用自訂函式。 include_groups=False
引數確保分組欄位不會包含在運算中。
視窗運算(Window Operations)
視窗運算允許我們在資料的不同子集上進行計算,常見的應用包括移動平均、移動總和等。pandas提供了兩種主要的視窗運算:滾動視窗(Rolling Window)和擴充套件視窗(Expanding Window)。
滾動視窗(Rolling Window)
滾動視窗運算是根據固定大小的視窗進行計算。
# 建立範例序列
ser = pd.Series([0, 1, 2, 4, 8, 16], dtype=pd.Int64Dtype())
# 進行滾動視窗求和
rolling_sum = ser.rolling(2).sum().astype(pd.Int64Dtype())
print(rolling_sum)
程式碼解析:
- 建立了一個包含2的冪次方的序列。
- 使用
rolling(2)
建立大小為2的滾動視窗。 - 套用
sum()
函式計算視窗內的總和。 - 將結果轉換為
Int64Dtype
以保持資料型別的一致性。
flowchart TD A[開始] --> B[建立序列] B --> C[定義滾動視窗大小] C --> D[進行滾動求和] D --> E[輸出結果]
圖表翻譯:
此圖示展示了滾動視窗運算的流程。首先建立輸入序列,接著定義滾動視窗的大小,然後進行滾動運算,最後輸出結果。這個過程清晰地說明瞭滾動視窗的工作原理。
擴充套件視窗(Expanding Window)
擴充套件視窗會考慮所有之前的資料點。
# 進行擴充套件視窗平均
expanding_mean = ser.expanding().mean().astype(pd.Float64Dtype())
print(expanding_mean)
程式碼解析:
- 使用
expanding()
建立擴充套件視窗。 - 套用
mean()
函式計算累積平均值。 - 將結果轉換為浮點數型別以處理小數。
實際應用:股票資料分析
讓我們使用輝達(Nvidia)股票資料來示範視窗運算的實際應用。
# 載入股票資料
df = pd.read_csv(
"data/NVDA.csv",
usecols=["Date", "Close"],
parse_dates=["Date"],
dtype_backend="numpy_nullable"
).set_index("Date")
# 計算不同週期的移動平均線
df_ma = df.assign(
ma30=df["Close"].rolling(30).mean().astype(pd.Float64Dtype()),
ma60=df["Close"].rolling(60).mean().astype(pd.Float64Dtype()),
ma90=df["Close"].rolling(90).mean().astype(pd.Float64Dtype())
)
# 繪製股價與移動平均線
import matplotlib.pyplot as plt
df_ma.plot()
plt.show()
程式碼解析:
- 載入輝達股票的歷史收盤價資料。
- 使用滾動視窗計算30天、60天和90天的移動平均線。
- 將原始股價與各條移動平均線繪製在同一個圖表上。
隨著資料分析需求的日益複雜,我們可以期待以下發展方向:
- 更高效的運算效能:未來版本可能會進一步最佳化這些運算的效能。
- 更豐富的視窗函式:可能會支援更多自訂的視窗運算方式。
- 與其他函式庫的整合:例如與機器學習函式庫的結合,將視窗運算應用於預測分析。
這些發展將進一步擴充套件資料分析的可能性,為使用者提供更多創新的應用場景。透過結合視窗運算與分組運算,我們可以實作更複雜的資料分析任務,為商業決策提供更有力的支援。
Pandas GroupBy 操作詳解與實務應用
在資料分析中,Pandas 的 GroupBy 功能是一個強大且靈活的工具,用於對資料進行分組並套用各種聚合函式。本文將深入探討 GroupBy 的使用方法,並結合實際案例進行詳細說明。
GroupBy 基本概念
GroupBy 操作的核心思想是根據一個或多個鍵將資料分成多個群組,然後對每個群組套用聚合函式。常見的應用場景包括:
- 按年份統計電影評分最高的值
- 按季度分析股票價格的變化趨勢
- 按類別計算產品的平均銷售量
時間序列資料的分組分析
對於時間序列資料,Pandas 提供了 pd.Grouper
函式,可以方便地按不同的時間頻率進行分組。例如:
import pandas as pd
# 建立示例資料
date_range = pd.date_range(start='2020-01-01', end='2023-12-31', freq='D')
df = pd.DataFrame({
'date': date_range,
'value': range(len(date_range))
})
df.set_index('date', inplace=True)
# 按年度進行分組並計算累積統計量
annual_stats = df.groupby(pd.Grouper(freq='YS')).expanding().agg(['min', 'max', 'mean'])
# 繪製結果
annual_stats.droplevel(axis=1, level=0).reset_index(level=0, drop=True).plot()
圖表翻譯:
此圖示展示了按年度分組的累積統計量變化趨勢。透過 pd.Grouper(freq='YS')
將資料按年度劃分,並使用 expanding()
計算每個年度內的累積最小值、最大值和平均值。圖表清晰地展示了不同年度間的資料變化情況。
電影資料分析例項
假設我們有一個包含電影資訊的資料集,現在需要找出每年評分最高的電影。以下是具體實作步驟:
- 資料準備:讀取 CSV 檔案並選擇需要的欄位
- 資料清理:將年份欄位轉換為整數型別
- 分組與排序:按年份分組並找出評分最高的電影
# 讀取資料並選擇需要的欄位
df = pd.read_csv('data/movie.csv', usecols=['movie_title', 'title_year', 'imdb_score'])
# 將年份轉換為整數型別
df['title_year'] = df['title_year'].astype(pd.Int16Dtype())
# 方法1:排序後分組取最後一個值
top_movies = df.sort_values(['title_year', 'imdb_score']).groupby('title_year')[['movie_title']].agg(top_rated_movie=pd.NamedAgg('movie_title', 'last'))
# 方法2:使用 idxmax 直接取得最高評分的電影
top_movies_idxmax = df.set_index('movie_title').groupby('title_year').agg(top_rated_movie=pd.NamedAgg('imdb_score', 'idxmax'))
自定義聚合函式
在某些情況下,預設的聚合函式無法滿足需求,這時可以自定義函式來實作更複雜的邏輯。例如:
def top_rated_movies(df):
top_rating = df['imdb_score'].max()
top_movies = df[df['imdb_score'] == top_rating]['movie_title'].unique()
return top_movies[0] if len(top_movies) == 1 else top_movies
# 套用自定義函式
top_movies_custom = df.groupby('title_year').apply(top_rated_movies, include_groups=False).to_frame('top_rated_movie(s)')
程式碼解析
pd.Grouper
:用於按不同的時間頻率進行分組,如按年度(‘YS’)、季度(‘QS’)或月度(‘MS’)劃分資料。expanding().agg()
:計算累積統計量,支援多種聚合函式,如最小值、最大值和平均值。sort_values()
:對資料進行排序,為後續的分組操作做準備。idxmax
:直接取得每組中最大值對應的索引,適用於快速定位最高評分的電影。自定義函式
:透過定義函式實作更靈活的聚合邏輯,如處理評分相同的多部電影情況。
視覺化呈現
為了更直觀地展示分析結果,可以使用 Matplotlib 或其他視覺化工具繪製相關圖表。例如:
flowchart TD A[資料讀取] --> B[資料清理] B --> C[分組操作] C --> D{是否需要自定義聚合函式} D -->|是| E[定義並套用自定義函式] D -->|否| F[使用內建聚合函式] E --> G[結果分析與視覺化] F --> G
圖表翻譯:
此流程圖展示了使用 Pandas 進行資料分析的完整流程。從資料讀取開始,經過資料清理後進行分組操作。根據需求選擇是否使用自定義聚合函式,最終對結果進行分析並視覺化展示。圖表清晰地展示了不同步驟之間的邏輯關係。
棒球資料分析:計算年度打擊率
在棒球資料分析中,打擊率(Batting Average)是一個重要的指標,用於評估球員的打擊表現。本文將介紹如何使用Python和Pandas函式庫來計算球員的年度打擊率,並探討資料品質問題對計算結果的影響。
資料準備
首先,我們需要載入包含棒球比賽資料的資料集。資料集以Parquet格式儲存,包含2000年至2023年間的棒球比賽紀錄。
import pandas as pd
# 載入資料集
df = pd.read_parquet("data/mlb_batting_lines.parquet")
print(df.head())
資料解讀:
此程式碼區塊用於載入棒球比賽資料。pd.read_parquet
函式從指定路徑讀取Parquet檔案,並將資料儲存在DataFrame物件df
中。print(df.head())
用於顯示資料的前幾行,以確認資料載入正確。
資料集介紹
資料集包含了每場比賽中球員的表現紀錄。每行代表一位球員在某場比賽中的資料。資料集的部分欄位如下:
year
:比賽年份game
:比賽IDstarttime
:比賽開始時間ab
:打席次數(At Bats)h
:安打次數(Hits)hr
:全壘打次數(Home Runs)
計算年度打擊率
要計算球員的年度打擊率,我們需要對資料進行分組匯總。首先,按year
和id
(球員ID)進行分組,然後計算每位球員每年的總打席次數和總安打次數。
# 按年份和球員ID分組,計算總打席次數和總安打次數
grouped_df = df.groupby(["year", "id"]).agg(
total_ab=pd.NamedAgg(column="ab", aggfunc="sum"),
total_h=pd.NamedAgg(column="h", aggfunc="sum"),
)
print(grouped_df.head())
資料解讀:
此程式碼區塊對資料進行分組匯總。groupby(["year", "id"])
將資料按年份和球員ID分組,agg
函式計算每組的總打席次數(total_ab
)和總安打次數(total_h
)。
計算打擊率
有了總打席次數和總安打次數後,我們可以計算打擊率。打擊率的計算公式為:安打次數 / 打席次數。
# 計算打擊率並刪除中間結果
batting_avg = grouped_df.assign(avg=lambda x: x["total_h"] / x["total_ab"]).drop(columns=["total_ab", "total_h"])
print(batting_avg.head())
資料解讀:
此程式碼區塊計算打擊率。assign
函式新增一個欄位avg
,其值為total_h
除以total_ab
。隨後,drop
函式刪除不再需要的欄位。
資料品質問題
在計算打擊率時,可能會遇到一些資料品質問題。例如,有些球員的打席次數很少,這可能導致打擊率的計算結果不準確。此外,如果某位球員沒有任何打席紀錄,則會出現除以零的錯誤。
# 篩選出打席次數大於400的球員
qualified_batters = grouped_df.loc[lambda df: df["total_ab"] > 400].assign(avg=lambda x: x["total_h"] / x["total_ab"]).drop(columns=["total_ab", "total_h"])
print(qualified_batters.head())
資料解讀:
此程式碼區塊篩選出打席次數大於400的球員,以確保計算出的打擊率具有參考價值。
Mermaid圖表:資料處理流程
flowchart TD A[載入資料] --> B[分組匯總] B --> C[計算打擊率] C --> D{篩選合格球員} D -->|是| E[輸出結果] D -->|否| F[忽略]
圖表翻譯:
此圖示展示了資料處理的流程。首先,載入棒球比賽資料。接著,按年份和球員ID進行分組匯總,計算總打席次數和總安打次數。然後,計算打擊率。最後,篩選出打席次數大於400的球員,並輸出結果。
資料分組分析與標準化處理
在資料分析過程中,分組匯總是一種常見且重要的操作。本章節將深入探討如何利用pandas的Group By功能對棒球選手的打擊資料進行深入分析,並透過標準化處理來比較不同年度的表現。
資料準備與初步分析
首先,我們需要對資料進行分組匯總,以計算各年度選手的打擊率。以下程式碼展示瞭如何實作這一點:
averages = (
df.groupby(["year", "id"]).agg(
total_ab=pd.NamedAgg(column="ab", aggfunc="sum"),
total_h=pd.NamedAgg(column="h", aggfunc="max"))
.loc[lambda df: df["total_ab"] > 400]
.assign(avg=lambda x: x["total_h"] / x["total_ab"])
.drop(columns=["total_ab", "total_h"])
)
程式碼解析
此段程式碼首先根據year
和id
進行分組,然後計算每個選手的總打數和總安打數。接著,篩選出總打數大於400的選手,並計算其打擊率。最後,刪除中間計算過程中的臨時欄位。
年度最佳打擊表現分析
進一步分析各年度的最佳打擊表現,可以透過以下程式碼實作:
averages.groupby("year").agg(
league_mean_avg=pd.NamedAgg(column="avg", aggfunc="mean"),
league_max_avg=pd.NamedAgg(column="avg", aggfunc="max"),
batting_champion=pd.NamedAgg(column="avg", aggfunc="idxmax")
)
分析結果解讀
此分析結果顯示了各年度的平均打擊率、最高打擊率及其對應的選手身份。透過比較不同年度的資料,可以發現打擊表現的變化趨勢。例如,2000年的平均打擊率為0.284,而2019年的平均打擊率下降至0.269,但兩年度的最佳打擊率均為0.335。
資料視覺化
為了更直觀地展示各年度打擊率的分佈情況,我們可以使用小提琴圖進行視覺化:
import matplotlib.pyplot as plt
import seaborn as sns
sns_df = averages.reset_index()
years = sns_df["year"].unique()
cat = pd.CategoricalDtype(sorted(years), ordered=True)
sns_df["year"] = sns_df["year"].astype(cat)
mask = (sns_df["year"] >= 2000) & (sns_df["year"] < 2010)
fig, ax = plt.subplots()
sns.violinplot(
data=sns_df[mask],
ax=ax,
x="avg",
y="year",
order=sns_df.loc[mask, "year"].unique()
)
ax.set_xlim(0.15, 0.4)
plt.show()
圖表解析
小提琴圖清晰地展示了2000-2009年間各年度打擊率的分佈情況。透過設定x軸的範圍,可以確保不同年度的比較是在相同的尺度下進行。
標準化處理
為了更公平地比較不同年度選手的表現,我們引入了Z-score標準化:
def normalize(ser: pd.Series) -> pd.Series:
return (ser - ser.mean()) / ser.std()
averages.assign(
normalized_avg=averages.groupby("year").transform(normalize)
).groupby("year").agg(
max_normalized_avg=pd.NamedAgg(column="normalized_avg", aggfunc="max")
)
標準化結果分析
透過標準化處理,我們可以發現2023年的選手錶現最為突出。這種方法有效地消除了不同年度間因整體水平差異所帶來的影響,使得比較更具參考價值。
圖表說明
此流程圖展示了整個資料分析的過程,從資料準備到最終的結果分析,每一步都清晰地展示了資料處理的流程和邏輯關係。透過這個流程圖,讀者可以快速理解資料分析的整體框架和關鍵步驟。
透過上述分析,我們不僅深入理解了棒球選手的打擊表現變化,還掌握了利用pandas進行高效資料分析的方法。這些技術和方法具有很高的實用價值,可以應用於多個領域的資料分析工作中。