ESG 資料品質與清理
資訊
本課使用 DS101_ESG_Company_Data_Practice.csv。這是一份模擬的教學資料,刻意保留了一些真實工作中常見的資料品質問題。請先不要急著計算,而是像審閱一份別人交來的報表一樣,先檢查:有沒有空白?有沒有重複?數字欄位真的是數字嗎?單位一致嗎?有沒有超出合理範圍或特別突出的值?公司名稱寫法一致嗎?
import pandas as pd
df = pd.read_csv("DS101_ESG_Company_Data_Practice.csv")
# 每個欄位有幾個缺失值
df.isna().sum()
# 有幾列與前面某一列完全相同
df.duplicated().sum()
# 每個欄位被讀成什麼資料型態
df.dtypes- 缺失值不等於零:空白代表「不知道」,0 代表「確定沒有」,兩者意義完全不同。
- 重複紀錄不一定是真的重複:先檢查 company_id 與 year 這組鍵值,再判斷是重複輸入還是兩筆不同的紀錄。
- 數字欄位可能被讀成文字:例如數字裡有千分位逗號,整欄就會變成 object,無法直接計算。
- 單位不一致會讓比較失效:同一欄若混用不同單位,數字大小就不能直接比較。
- 異常值是需要調查的訊號,不是自動刪除的對象:它可能是錯誤,也可能是真實發生的變化。
- 看起來合理的數值仍可能是錯的:落在正常範圍內,不代表已經和來源核對過。
注意
# 同一個 company_id 與 year 是否出現超過一次
print(df[df.duplicated(subset=["company_id", "year"], keep=False)])
# 公司名稱有沒有寫法不一致(例如多了空白)
print(df["company_name"].unique())
# 哪些值無法被讀成數字(另存成新變數,不覆寫原始欄位)
revenue_num = pd.to_numeric(df["revenue_m_twd"], errors="coerce")
print(df.loc[revenue_num.isna() & df["revenue_m_twd"].notna(), ["company_id", "year", "revenue_m_twd"]])
# 百分比欄位是否超出 0 到 100
print(df[(df["renewable_energy_pct"] < 0) | (df["renewable_energy_pct"] > 100)])
# 用摘要統計找出特別突出的值,再回頭看同一家公司的前後年度
df.describe()| Issue(問題) | Evidence(證據) | Impact(若不處理的影響) | Treatment decision(處理決定) | Justification(為什麼這樣處理合理) | What must remain unknown(必須維持未知/不變的部分) |
|---|---|---|---|---|---|
| 某公司某一年的 waste_tonnes 約為其他年度的 1000 倍 | describe() 顯示最大值特別突出;來源附註寫的單位是 kg,而資料字典規定是 tonnes | 若不處理,該年度的廢棄物量會被高估約 1000 倍,連帶影響任何加總或比較 | 與資料提供者確認後,在新欄位中換算為 tonnes,保留原始欄位 | 同一欄必須使用同一單位,否則年度之間無法比較;換算前先與來源核對,而不是自行猜測 | 其他年度是否也曾誤用 kg 尚未逐一確認,仍是未知 |
| 某公司某一年的 employees 為空白 | isna().sum() 顯示該欄有 1 個缺失值 | 若補成 0 或平均值,該年度的人均指標會被錯誤地拉低或拉高 | 維持缺失,不補值;計算每位員工指標時排除該年,並在限制中說明 | 沒有來源可以確認員工人數,補值等於自行創造資料 | 該年度真實的員工人數維持未知;該年的人均指標趨勢中斷,不予估計 |
| 某公司名稱在不同列的寫法不完全一致(例如多了結尾空白) | df["company_name"].unique() 列出的值看起來重複,但字串比較結果不相等 | 若不處理,groupby("company_name") 會把同一家公司拆成兩組,彙總結果被低估 | 去除頭尾空白並統一寫法後存成新欄位,再用新欄位分組,保留原始欄位 | 分組與彙總要依賴名稱完全相符;統一寫法前要先確認這些列確實是同一家公司,而不是直接改寫 | 是否還有其他尚未發現的拼法差異,仍需要在後續檢查中留意 |
好的清理習慣是:保留原始欄位,把清理後的結果放在新欄位或新的 DataFrame;每一個修改都寫進決策紀錄;無法確認的地方就維持缺失,並在結論的限制中說出來。
- 先看整體:df.shape 與 df.dtypes,確認資料大小與型態符合預期
- 再看缺失:df.isna().sum(),知道哪些欄位有空白
- 再看重複:df.duplicated(subset=["company_id","year"]).sum(),確認鍵值沒有重複
- 最後看範圍與極端值:df.describe() 與欄位層級的範圍檢查,找出需要查證的數字
- 建議依照這個順序,而不是看到哪個問題就先處理哪個,否則容易漏掉前面的問題
你發現某一列的 scope2_tco2e 是空白。接下來第一步應該怎麼做?