Learning Objectives
- 用 dtypes、unique、describe、duplicated 找出資料表裡的問題
- 用 str.strip、str.replace 與對照表統一文字的寫法
- 把文字欄轉成數字與日期,說出 astype 與 to_numeric(errors="coerce") 的差別,以及日期月日順序的歧義
- 處理超出範圍的值與重複的列,並說明為什麼不該把錯誤的值改成「看起來合理」的值
人工輸入的資料
上一課處理的是空著的格子;這一課處理填了但填錯的格子。資料由好幾個人手動輸入、從不同系統匯出時,常見這些問題:
| 問題 | 例子 | 後果 |
|---|---|---|
| 同一個值有好幾種寫法 | 「資工」「資工系」「資訊工程」「 資工」 | 分組時被當成不同的系 |
| 數字欄混了文字 | 「85分」「 72 」「缺考」 | 整欄變成文字(object),不能算平均 |
| 超出合理範圍 | 期末 150 分、-5 分 | 平均與標準差被拉偏 |
| 日期寫法不一 | 2026-11-10、2026/11/08、11/03/2026 | 不能排序、不能算相隔幾天 |
| 重複的列 | 同一筆紀錄輸入兩次 | 重複計算 |
這一課的 grades_raw.csv 是程式產生的模擬資料(63 列),不是任何學校的真實資料,問題是刻意放進去的。
清理的第一步永遠是先看:看型別、看有哪些寫法、看最大最小值。
逐步看:先檢查,再動手
每一行都在找一種問題。注意 unique() 的結果裡,有幾個字串前後多了空白。
統一寫法與轉換型別
文字欄可以用 .str 接上字串的方法,一次處理整欄:
| 寫法 | 做什麼 |
|---|---|
s.str.strip() |
去掉前後的空白 |
s.str.replace("系", "") |
把「系」換成空字串(刪掉) |
s.replace({"資訊工程": "資工"}) |
用對照表把整格的值換掉 |
數字欄混了文字時,astype(float) 只要遇到一格轉不了就整個失敗;pd.to_numeric(s, errors="coerce") 則把轉不了的格子變成缺值(NaN)。
後者方便,但也會把真正寫錯的值靜靜地變成缺值,所以轉換前先用 unique() 看過有哪些寫法,轉換後再數一數多了幾個缺值。
逐步看:系所與期中
看 dept 在記憶體區的變化:每一步處理一種寫法上的差異。
超出範圍、日期與重複
超出範圍的值:期末 150 分顯然錯了,但「正確的分數」是多少,資料裡沒有答案。把它截成 100(clip)看起來整齊,
其實是編了一個分數;比較誠實的做法是改成缺值(where),並回頭查原始紀錄。刪掉整列也會讓問題消失,但同一列其他欄的資料也一起丟了。
日期:pd.to_datetime 把文字轉成日期型別,之後才能排序、比較、算相隔幾天。寫法不只一種時加 format="mixed",每一格各自判斷。
「11/03/2026」是 11 月 3 日還是 3 月 11 日?美國習慣「月/日/年」,很多國家習慣「日/月/年」;pandas 預設月在前,dayfirst=True 改成日在前。
只看資料本身常常分辨不出來,要問資料是誰、用什麼系統輸入的。
重複的列:duplicated() 標出和前面某一列完全相同的列,drop_duplicates() 刪掉它們。
要注意「完全相同」的意思:系所一個寫「資工」、一個寫「資工系」的兩列不算重複,所以先統一寫法,再找重複。
逐步看:期末、日期與重複的列
看 bad 與 dup:它們都是一串 True/False,用來選出要處理的列。
實作:清理系所與期中
讀入 grades_raw.csv,在 clean 上完成三件事:
1. 系所(dept)去掉前後空白、刪掉「系」字,再把「資訊工程」換成「資工」、「企業管理」換成「企管」;
2. 期中(midterm)去掉前後空白與「分」字,用 pd.to_numeric(..., errors="coerce") 轉成數字;
3. 刪掉重複的列。
最後三行會印出列數、各系筆數與期中平均。
小測驗:資料清理
下面程式執行時會發生什麼?
import pandas as pd
s = pd.Series(["85", "72分", "90"])
print(s.astype(float).tolist())
下面程式會印出什麼?
import pandas as pd
s = pd.Series(["85", "缺考", " 72 "])
print(pd.to_numeric(s, errors="coerce").tolist())
下面程式會印出什麼?
import pandas as pd
df = pd.DataFrame({"id": ["A", "A", "B", "A"], "dept": ["資工", "資工", "資工", "資工系"]})
print(df.duplicated().tolist())
期末成績有一格是 150,你查不到原始紀錄。下面哪一種處理最恰當?
下面程式會印出什麼?
import pandas as pd
d = pd.to_datetime("11/03/2026", dayfirst=True)
print(d.strftime("%Y-%m-%d"))
重點整理
- 清理前先看:
dtypes(數字欄是不是 object)、unique()(有哪些寫法)、describe()(最大最小值)、duplicated().sum()。 - 文字用
.str.strip()、.str.replace()與對照表replace({...})統一寫法。 - 轉數字:
astype遇到一格轉不了就失敗;pd.to_numeric(..., errors="coerce")把轉不了的變成缺值,轉完要數一數多了幾個缺值。 - 超出範圍的值不要「修」成看起來合理的數字;改成缺值、查原始紀錄,並在報告寫明。
- 日期用
pd.to_datetime,寫法不一時加format="mixed";斜線寫法的月日順序要確認資料來源。 - 先統一寫法,再用
drop_duplicates()刪重複的列。 - 下一課用乾淨的資料做分組彙總:各系、各課程的平均是多少。