跳到主要內容

Day 18 Pandas 資料清理

Day 18 Pandas 資料清理

引言

真實世界的資料幾乎都不乾淨:可能缺值、可能有重複列、可能有格式不一致、可能有離群值。資料科學家常說「資料清理佔了 80% 的時間」,剩下的 20% 才是建模與分析。Pandas 提供一整套清理工具,今天要學會的正是這些日常必備:缺失值處理、條件篩選、排序、去重、型別轉換,以及最實用的 groupby 分組聚合。這些動作不需要寫複雜的演算法,但每一招都得做扎實,模型訓練的成果才會穩定。

學完今天的內容,你會具備處理一份中等規模的真實表格資料的能力。從明天起,我們會進入視覺化階段,用 Matplotlib 與 Seaborn 把清理後的資料畫成圖表。資料清理與視覺化是資料分析的兩條腿,先把資料整理好,畫出來的圖才有意義。

缺失值處理:isnull、fillna、dropna

Pandas 用 NaN(Not a Number)表示缺失值。可以用 isnull() 或 notnull() 判斷位置,再用 fillna() 填補、dropna() 丟棄。實務上建議先檢查缺失比例,決定要補還是刪:缺失很少就刪,缺失有規律或占比偏高就補。判斷缺失型態也是清理的關鍵:是「完全隨機缺漏」還是「某個族群特別容易漏」,處理方式會很不一樣。如果是後者,直接補平均反而會掩蓋問題,這時通常會加上「是否缺值」的衍生欄位給模型參考。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "姓名": ["小明", "小華", "小美", "小李"],
    "數學": [90, np.nan, 95, 70],
    "英文": [78, 80, np.nan, 75],
})

print(df.isnull())        # 標出每個位置是否缺失
print(df.isnull().sum())  # 每欄缺失值數量

# 刪除有缺失的列
df_drop = df.dropna()

# 填補:固定值
df_fill_const = df.fillna(0)

# 填補:每欄平均(實務上很常用)
df_fill_mean = df.fillna(df.mean(numeric_only=True))

fillna() 還能接受字典,對不同欄位給不同填補策略:df.fillna({"數學": 0, "英文": df["英文"].mean()})。當資料有時間順序時,常用前一筆或後一筆填補(method="ffill" 或 "bfill"),這個技巧在處理感測器紀錄時特別好用。從 Pandas 2.1 起,也可以用 method="nearest" 找最近的非缺失值做填補,這對間距均勻的時間序列很實用。

資料篩選與排序

昨天學過布林索引,今天把它套用到 DataFrame 上。常見的篩選條件包括「數值大於某門檻」、「字串包含某關鍵字」、「日期在區間內」等。query() 方法則提供更接近 SQL 的寫法,對熟悉資料庫的人很友善。布林索引的回傳結果是原 DataFrame 的子集(複製),因此修改子集不會影響原資料,這點和 NumPy 的 view 不同。

import pandas as pd

df = pd.DataFrame({
    "姓名": ["小明", "小華", "小美", "小李"],
    "城市": ["台北", "台中", "台北", "高雄"],
    "消費": [1200, 850, 2300, 600],
})

# 布林索引
high_spend = df[df["消費"] >= 1000]

# 多條件
taipei_high = df[(df["城市"] == "台北") & (df["消費"] >= 1000)]

# 用 isin() 篩選多個城市
multi_city = df[df["城市"].isin(["台北", "高雄"])]

# 用 query() 寫法
result = df.query("消費 >= 1000 and 城市 == '台北'")

# 排序
df_sorted = df.sort_values("消費", ascending=False)
df_sorted_multi = df.sort_values(["城市", "消費"])

isin() 對於「多選一」的篩選特別方便,例如篩選多個會員等級、多個產品類別。query() 則把條件寫成字串,可讀性更高,但要注意字串中的引號與運算子的優先順序,必要時加括號。sort_values() 是排序的主力,能依一個或多個欄位排,也能指定遞增或遞減。如果想要穩定排序(相同值保持原本順序),可以加上 kind="stable" 參數。處理大型資料時,先用 sort_values() 排序再 drop_duplicates(),常常比直接刪重更省事,因為排序後的索引會比較有規律。

處理重複資料

實務上常會遇到完全重複或部分欄位重複的列,例如系統匯入時不小心多按幾次送出。duplicated() 會回傳布林 Series,標出每列是否為重複;drop_duplicates() 則直接刪除重複列。判斷重複時常常是看某幾個關鍵欄位(例如訂單編號、會員 ID),而不是看整列是否一致,這時 subset 參數就是關鍵。

import pandas as pd

df = pd.DataFrame({
    "顧客ID": [1, 2, 1, 3, 2],
    "消費": [1200, 850, 1200, 600, 850],
})

print(df.duplicated())               # 整列比對
print(df.duplicated(subset=["顧客ID"]))  # 只看顧客ID是否重複

df_unique = df.drop_duplicates()                  # 預設保留第一次出現
df_last = df.drop_duplicates(subset=["顧客ID"], keep="last")  # 保留最後一次

subset 參數讓我們只看特定欄位是否重複,搭配 keep 可以決定要保留第一筆(first)、最後一筆(last),或全部丟掉(False)。處理交易資料時,常常會希望保留「最新一筆」,這時 keep="last" 就很實用。如果想依時間欄位判斷最新一筆,可以先用 sort_values() 排序再 drop_duplicates(),順序寫對就不會出錯。對於「同一個人同一天的多筆紀錄」這類常見場景,先用日期切、再對其他關鍵欄位去重是比較乾淨的做法。

apply 與型別轉換

有時候欄位裡的數字其實是字串(例如 "1,200"),或是需要把多欄位合併出新欄位。Pandas 提供 astype() 轉型、apply() 套用自訂函式、map() 對 Series 做映射。apply() 是最泛用的工具,可以處理複雜的邏輯。學會這幾招,面對格式不一的資料就能有條理地處理。

import pandas as pd

df = pd.DataFrame({
    "金額字串": ["1,200", "850", "2,300"],
    "顧客ID": ["A01", "A02", "A03"],
})

# 型別轉換:先去掉逗號再轉 int
df["金額"] = df["金額字串"].str.replace(",", "").astype(int)

# 用 map 做對應
city_map = {"A01": "台北", "A02": "台中", "A03": "高雄"}
df["城市"] = df["顧客ID"].map(city_map)

# 用 apply 套用自訂函式
def price_level(amount):
    if amount >= 2000:
        return "高"
    elif amount >= 1000:
        return "中"
    return "低"

df["消費等級"] = df["金額"].apply(price_level)

apply() 可以對整個 DataFrame 走訪(搭配 axis=1 對每列處理),但大量使用會比較慢,建議優先考慮向量化寫法或 NumPy 的 ufunc。只有在邏輯複雜、無法向量化時才用 apply。如果想用更複雜的鏈式操作,可以考慮 .pipe() 與 .assign() 讓程式碼讀起來更接近資料流。實務上可以先把 df["金額"].to_numpy() 轉成 NumPy 陣列運算,再寫回欄位,這樣會比 apply 快上好幾倍。

groupby:分組聚合

groupby() 是 Pandas 分析的靈魂:把資料依某個欄位分成多組,再對每組做彙總(例如加總、平均、計數)。寫法類似 SQL 的 GROUP BY,核心概念是「split-apply-combine」:先切、再算、最後合併。學會 groupby 之後,Excel 的樞紐分析能做的事幾乎都能用兩三行 Python 完成。

import pandas as pd

df = pd.DataFrame({
    "城市": ["台北", "台北", "台中", "台中", "高雄"],
    "通路": ["線上", "門市", "線上", "門市", "門市"],
    "金額": [1200, 800, 1500, 700, 900],
})

# 依城市分組,計算總金額
by_city = df.groupby("城市")["金額"].sum()

# 依城市分組,計算平均與筆數
by_city_stats = df.groupby("城市")["金額"].agg(["mean", "count"])

# 多欄分組
by_city_channel = df.groupby(["城市", "通路"])["金額"].sum()

# 對每組套用自訂函式
def top_amount(s):
    return s.max() - s.min()

spread = df.groupby("城市")["金額"].apply(top_amount)

groupby() 的回傳結果是 SeriesGroupBy 或 DataFrameGroupBy,需要呼叫彙總函式(sum、mean、count 等)才會真正計算。agg() 可以同時算多個統計量,apply() 則能套任意函式。學會 groupby 之後,很多 Excel 的樞紐分析都能用兩三行程式碼完成。做完 groupby 後如果想重置索引回一般 DataFrame,呼叫 reset_index() 即可,這對後續繪圖或輸出都很方便。想要更細的「每組前 N 名」可以用 groupby + head() 組合,這在電商、賽事報表很常見。

結語

今天把 Pandas 資料清理的核心工具都走了一遍:缺失值用 isnull / fillna / dropna、條件篩選用布林索引與 query、排序用 sort_values、重複用 duplicated / drop_duplicates、型別與衍生欄位用 astype / apply / map,分組彙總則用 groupby。這些工具的組合,足以應付 80% 日常的資料清理需求。建議找一份過去工作中曾經處理過的 CSV,把今天的工具實際用一次,看看能省下多少時間。

明天,我們會進入 Pandas 的合併與時間序列:學會 merge()、concat() 處理多份資料的整合,並用 to_datetime() 與 resample() 處理日期時間資料。這些是處理真實世界資料的進階工具,學會之後就能應付常見的多來源、跨期資料分析需求。

留言

這個網誌中的熱門文章

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 等工具能處理和分析龐...