跳到主要內容

Day 19 Pandas 合併與時間序列

Day 19 Pandas 合併與時間序列

引言

真實世界的資料很少只放在一張表格裡。訂單資料有客戶基本資料、銷售資料、產品資料,當我們要分析「哪個區域、哪個年齡層買了哪些商品」,就得把多張表合併在一起。此外,許多分析都跟時間有關:每日銷售量、月活躍使用者、股價走勢,這些都屬於時間序列。

今天要學習的 Pandas 合併與時間序列處理,正是把零散資料整合成可用資訊的關鍵技能。我們會介紹 concat()、merge() 與 join() 三種合併方式,再學會把字串轉成日期、用日期做索引,最後用 resample() 做不同時間尺度的彙總。學完後,你就能處理「跨表格」與「跨時間」這兩類最常見的資料分析情境。

這一篇會延續 Day 17、Day 18 學過的 Pandas 基礎。如果你對 DataFrame、groupby()、缺失值處理還不熟悉,可以先回去複習。今天的重點放在「如何把多份資料拼起來」與「如何用日期做分析」這兩件事上。

使用 concat() 串接資料表

concat() 是最直觀的合併方式:把多個 DataFrame 沿著某一個軸(列或欄)疊在一起。常見的情境是把同一個欄位結構的資料,例如各月份、各分店報表,垂直串成一張大表。concat() 的運作方式類似「疊紙牌」,依預設會以列方向(垂直)堆疊,並保留原本的索引。

import pandas as pd

jan = pd.DataFrame({
    "product": ["A", "B", "C"],
    "sales": [120, 230, 180]
})
feb = pd.DataFrame({
    "product": ["A", "B", "C"],
    "sales": [150, 200, 210]
})

result = pd.concat([jan, feb], ignore_index=True)
print(result)

輸出:

  product  sales
0       A    120
1       B    230
2       C    180
3       A    150
4       B    200
5       C    210

ignore_index=True 會重新編排索引,讓結果的索引是 0 到 5,而不是各自保留原本的 0、1、2。當我們要疊加多筆相同結構的資料時,這個參數幾乎都會用上。如果想維持原本的索引並看出資料來源,可以改成多層索引:pd.concat([jan, feb], keys=["1月", "2月"])。

使用 merge() 合併兩張表

merge() 的概念類似資料庫的 JOIN:透過一個或多個共用欄位(鍵),把兩張表的對應列拼在一起。這是合併「客戶資料」與「訂單資料」這類有對應關係的表時的主力。與 concat() 不同的是,merge() 是依「鍵的值」對應合併,而不是單純疊在一起。

customers = pd.DataFrame({
    "customer_id": [1, 2, 3],
    "name": ["Alice", "Bob", "Charlie"]
})

orders = pd.DataFrame({
    "order_id": [101, 102, 103, 104],
    "customer_id": [1, 1, 2, 3],
    "amount": [250, 180, 320, 150]
})

merged = pd.merge(customers, orders, on="customer_id")
print(merged)

輸出:

   customer_id     name  order_id  amount
0            1    Alice       101     250
1            1    Alice       102     180
2            2      Bob       103     320
3            3  Charlie       104     150

on="customer_id" 指定要用哪個欄位當作合併鍵;如果兩張表的鍵名稱不同,可以用 left_on 與 right_on 分別指定。merge() 預設是 inner join,只保留兩邊都有的鍵;如果要保留某一邊全部的紀錄,可以改用 how="left"、how="right" 或 how="outer"。merge 也支援多個鍵,例如 on=["year", "month"] 同時用兩個欄位對應。

處理合併後的缺失值

合併之後常常會冒出 NaN,例如某些客戶沒有訂單、或某些訂單對不到客戶資料。Day 18 學過的處理方式在這裡一樣適用:可以用 fillna(0) 補 0,或用 dropna() 直接拿掉。選擇哪一種,要看那筆缺失在分析上有沒有意義。

all_customers = pd.DataFrame({
    "customer_id": [1, 2, 3, 4],
    "name": ["Alice", "Bob", "Charlie", "David"]
})

left_join = pd.merge(all_customers, orders, on="customer_id", how="left")
left_join["amount"] = left_join["amount"].fillna(0)
print(left_join)

輸出:

   customer_id     name  order_id  amount
0            1    Alice     101.0   250.0
1            1    Alice     102.0   180.0
2            2      Bob     103.0   320.0
3            3  Charlie     104.0   150.0
4            4    David       NaN     0.0

David 沒有任何訂單,但我們用 fillna(0) 把金額補成 0,這樣在算總和時就不會把他誤算成「缺資料」。如果是要計算「沒有下單的客戶比例」這類指標,則要保留 NaN,才能正確反映實際情況。合併後的缺失值處理,往往決定了分析的結論能不能站得住腳。

日期時間處理:to_datetime 與 dt 存取器

時間序列分析的第一步,是讓 Pandas 認得我們手上的日期字串。pd.to_datetime() 可以把字串轉成 datetime64 型別,之後就能用 dt 存取器取得年、月、日、星期幾等資訊。這個步驟看似簡單,但很多新手會卡在「資料看起來像日期,其實還是字串」的陷阱。

df = pd.DataFrame({
    "date": ["2024-01-15", "2024-02-20", "2024-03-10"],
    "sales": [200, 340, 280]
})

df["date"] = pd.to_datetime(df["date"])
df["year"] = df["date"].dt.year
df["month"] = df["date"].dt.month
df["weekday"] = df["date"].dt.day_name()
print(df)

輸出:

        date  sales  year  month   weekday
0 2024-01-15    200  2024      1     Monday
1 2024-02-20    340  2024      2   Tuesday
2 2024-03-10    280  2024      3     Sunday

轉成 datetime 之後,這個欄位就支援加減天數、比較先後,也能直接丟進繪圖函式,省下不少前置處理功夫。to_datetime() 對常見的 "YYYY-MM-DD"、"YYYY/MM/DD"、甚至中英文混雜的日期格式都能自動判斷。對於不規則的格式,可以加上 format="%Y年%m月%d日" 明確指定。實務上從資料庫或 API 拉回的時間欄位常常是「Unix 時間戳」(例如 1704153600),這時候可以用 unit="s" 讓 Pandas 自動轉成人類可讀的日期。

如果只想取出日期、不要時間部分,可以用 df["date"].dt.date;只想取時間則用 df["date"].dt.time。這些小工具在處理「同一天不同時段」的資料時特別有用,例如把訂單時間轉成「日期+小時」兩個欄位,再依小時做分組分析。

時間索引與 resample() 重取樣

把日期欄位設成索引後,就能用 resample() 把資料重新彙總到不同時間尺度,例如把日資料變成月資料或季資料。這在做趨勢分析時非常方便,能把雜訊平滑掉,看出真正的走向。resample() 的概念類似 groupby(),只是它依時間頻率分組。

import numpy as np

dates = pd.date_range("2024-01-01", periods=90, freq="D")
daily = pd.DataFrame({
    "date": dates,
    "sales": np.random.randint(100, 400, size=90)
})

daily = daily.set_index("date")
monthly = daily.resample("ME").sum()
print(monthly)

輸出:

             sales
date
2024-01-31    7820
2024-02-29    6540
2024-03-30    7010

"ME" 代表月底頻率;如果是 "W" 就是週、"QE" 就是季。除了 sum(),也可以換成 mean()、max()、min(),看分析需求而定。需要注意的是,resample() 會把每個時段的數字彙總起來,若該時段沒有資料,預設會用 NaN 填補,這時可以用 fillna(method="ffill") 把前一筆的值補上。另一個常見需求是「同月份跨年度比較」,這時候可以用 df.groupby(df.index.month).mean() 把資料依月份重新分組,得到「1 月平均、2 月平均」這類季節性指標。

計算滾動平均

另一個常用的時間序列技巧是「滾動平均」(rolling mean),用鄰近幾筆資料的平均值來平滑短期波動。當我們想看長期趨勢、又不想丟掉原始資料時,這個做法很實用。例如把 7 日滾動平均疊在原始銷售曲線上,就能看出「整體上升,但日資料波動很大」的趨勢。

daily["rolling_7d"] = daily["sales"].rolling(window=7).mean()
print(daily.head(10))

輸出:

            sales  rolling_7d
date
2024-01-01    312         NaN
2024-01-02    185         NaN
2024-01-03    256         NaN
2024-01-04    298         NaN
2024-01-05    341         NaN
2024-01-06    120         NaN
2024-01-07    275    255.29
2024-01-08    388    266.14
2024-01-09    152    261.43
2024-01-10    266    263.71

前 6 天因為資料筆數不足,會顯示 NaN;從第 7 天開始就是前 7 筆的移動平均。視覺化時把原始銷售與 7 日滾動平均疊在一起,最能看出趨勢。window 參數決定視窗大小,數字越大曲線越平滑,但也越延遲。常見的選擇是 7(日)、30(月)、90(季),依資料的時間尺度與分析目的調整。

結語

今天把 Pandas 的合併與時間序列兩個主題一次補齊。concat() 適合疊加同結構的資料,merge() 適合處理有鍵對應的多張表;把日期欄位轉成 datetime 並設成索引後,就能用 resample() 與 rolling() 做出不同尺度的彙總與平滑。掌握這些技巧,分析「跨表」與「跨時間」的資料就不再是難事。

明天,我們會進入 Matplotlib,從最基礎的折線圖開始,學習怎麼把資料轉成清楚又專業的圖表。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...