Year 欄位含 NaN,怎麼安全轉換成整數?
研究團隊接下來想要將 Year 欄位轉換為整數型態,以便後續進行年份趨勢分析。考慮到資料中可能包含缺失值(NaN),請選出最合適的轉換方式。
vgsales.csv 的 Year 欄位目前是 float64,部分資料有缺失值(NaN)。研究團隊要把它轉成整數型態,方便做年份相關的趨勢分析。四個選項各提供了不同的轉換語法,有的在碰到 NaN 時會直接報錯,有的會用假值填補,有的能保留 NaN 同時轉成整數型別。
問你:在資料可能含有缺失值的情況下,哪一種轉換方式最合適?
一句話總結
含缺失值的欄位要轉整數,最合適的方式是 astype('Int64')(大寫 I):這是 pandas 的可空整數型別,可以同時存放整數值和 NaN,不需要先填補缺失值。直接 astype(int) 會報錯,fillna(0) 或 fillna(1) 用假值污染資料。
先感受問題:轉型前要先處理 NaN 嗎?
遊戲研究公司的分析師小凱確認了 Year 欄有幾筆 NaN(某些遊戲沒有記錄發售年份)。他要把 Year 轉成整數以便按年份計算趨勢,但馬上發現問題:
→ 執行後立刻報錯:Cannot convert non-finite values (NA or inf) to integer
方案二:fillna(0).astype(int)
→ 不報錯,但缺失年份被填成 0,分析時會出現「0 年」的假資料,污染趨勢圖
方案三:fillna(1).astype(int)
→ 同樣污染,缺失年份變成「西元 1 年」,更荒謬
方案四:astype('Int64')(大寫 I)
→ 成功轉換,dtype 變成 Int64,NaN 保留為 <NA>,不污染資料
正確答案是方案四:用 pandas 的可空整數型別 Int64,讓缺失值保持缺失,不用假值填補。
三個錯誤做法各有什麼問題
- astype(int) 直接轉型:numpy 的 int 型別不支援 NaN,遇到缺失值就拋出 ValueError,整個轉換中斷。只有在確認資料完全沒有缺失值的情況下才能安全使用。
- fillna(0).astype(int) 填零再轉:技術上不報錯,但把缺失年份填成 0,年份分析時「西元 0 年」的資料就成了假資料。做趨勢圖時 x 軸會多一個 0,嚴重干擾視覺呈現和統計結果。
- fillna(1).astype(int) 填 1 再轉:和 fillna(0) 同樣的問題,只是假值換成 1,遊戲發售年份出現「西元 1 年」更不合邏輯,是語義上更嚴重的錯誤。
- 不處理 NaN 就分析:如果不轉型就直接做年份趨勢分析,float64 的年份計算可能出現 2006.0 這樣的小數年份,且 NaN 會在 groupby 等操作時被自動跳過,行為有時不符合預期。
- drop NaN 後再轉型:dropna() 後再 astype(int) 是另一種做法,但這會永久刪除有 NaN 的整筆資料,損失其他欄位的有效資訊。Int64 保留 NaN 是更好的選擇。
astype('Int64') 一行搞定:轉整數又保留缺失值
小凱最終採用:
轉換後驗證:
data['Year'].dtype → Int64
data['Year'].isna().sum() → 同前(NaN 筆數不變)
data['Year'].head() → 2006, 1985, 2008, <NA>, 2010(有缺失的顯示 <NA>)
轉換後 dtype 顯示 Int64(大寫),缺失值以 pandas 的 <NA> 表示,不是 numpy 的 nan,且整欄都是整數值,可以直接做年份趨勢分析。這就是選項 D 講的:data['Year'] = data['Year'].astype('Int64')。
技術版:Int64 vs int64 的差別與可空整數型別
中級考試大概率會考程式碼跟公式,所以這部分你還是要學。但如果現在學起來很痛苦,可以先跳過,等讀完其他題目回頭再來。
想像兩種信封:一種只能裝數字(numpy int64),另一種可以裝數字或一張「此信封空著」的說明卡(pandas Int64)。當你有幾封空信封時,只能用第二種信封盒子來收納它們。fillna(0) 是把空信封強制塞進「0」這個假內容,讓人以為裡面有東西。astype('Int64') 是換成能正確標記「空著」的信封盒,不需要造假。
| 情境 | 程式碼與結果 |
|---|---|
| 直接轉,有 NaN 時報錯 | astype(int) → ValueError |
| 填 0 後轉,NaN 變假資料 | fillna(0).astype(int) → dtype: int64,NaN→0 |
| 轉可空整數,NaN 保留 | astype('Int64') → dtype: Int64,NaN→<NA> |
| 確認型別 | data['Year'].dtype → Int64 |
| 確認缺失值個數不變 | data['Year'].isna().sum() |
- int64(小寫 i)
- numpy 的 64 位元整數型別,不支援 NaN,遇到缺失值報錯。
- Int64(大寫 I)
- pandas 1.0+ 推出的可空整數型別(Nullable Integer Dtype),用 pandas.NA 表示缺失值,支援整數和 NaN 共存。
- pandas.NA 和 numpy.nan
- Int64 欄的缺失值顯示為 <NA>(pandas.NA),與 float64 欄的 nan(numpy.nan)不同,但 isna() 和 isnull() 都能偵測到兩者。
- astype()
- pandas Series 的型別轉換方法,傳入字串 'Int64' 時使用可空整數,傳入 int 時使用 numpy int(不可空)。
- fillna()
- 用指定值填補缺失值的方法,使用時需確保填入的值在語意上合理,否則會污染資料。
本題沒有數學公式,核心是 pandas 的型別系統設計:
- numpy int64:不可為空,記憶體效率高,不支援 NaN。
- pandas Int64:可為空(Nullable),底層用 numpy int64 + 一個 boolean 遮罩(mask)標記哪些位置是 NA,記憶體比 float64 更精確表達整數意圖。
- float64:浮點數,用 IEEE 754 的 nan 表示缺失,轉整數後會有精度疑慮(2006.9999 可能因捨入誤差導致問題)。
- astype(int) 和 astype('Int64') 最關鍵的差別是什麼?
- fillna(0).astype(int) 技術上不報錯,為什麼在年份分析中是壞做法?
- Int64 的缺失值顯示為 <NA>,和 float64 的 nan 有什麼不同?isna() 都能偵測嗎?
- 如果已確定 Year 欄完全沒有缺失值,應該用 astype(int) 還是 astype('Int64')?
- 讀 CSV 時如果想直接把 Year 欄讀成 Int64,應該怎麼寫 read_csv 參數?
為什麼其他選項是錯的
字面在說什麼:直接把 Year 欄轉成整數型態。
為什麼不對:numpy int(即 int64)不支援 NaN 值。Year 欄有缺失值,執行 astype(int) 時會拋出 ValueError:"Cannot convert non-finite values (NA or inf) to integer"。程式會在這行中斷,無法繼續執行。
誰會選錯:只知道「轉整數用 astype(int)」但不知道 int 無法存 NaN 的人,或者沒有仔細看題目說「考慮到可能包含缺失值」這個提示的人。
字面在說什麼:先把缺失值填成 0,再轉整數,兼顧了 NaN 問題和型別轉換。
為什麼不對:技術上不報錯,但語意上錯了。把缺失的年份填成 0(西元 0 年)是無意義的假資料。後續做年份趨勢分析時,x 軸出現「Year = 0」,嚴重影響視覺化和統計正確性。題目說「最合適」,語意上正確才算合適。
誰會選錯:只想解決「不能有 NaN」的問題,沒有考慮填入假值對後續分析的影響。看到「不報錯」就認為是正確答案的人。
字面在說什麼:先把缺失值填成 1,再轉整數,這樣 NaN 問題和型別轉換都解決了。
為什麼不對:和選項 B 同樣的問題,只是假值更荒謬。把遊戲缺失的發售年份填成 1(西元 1 年),比填 0 更明顯是錯誤的,任何人看到 Year = 1 都知道是假資料。fillna 填入的值必須要有語意合理性,1 對年份分析沒有任何意義。
誰會選錯:同選 B 的人,但選了更荒謬的填補值。
同個考點下次怎麼變形
直覺:轉成 Int64 之後,還能對 Year 欄做 groupby 或算術運算嗎?
答案:可以。Int64 欄支援 groupby、sum()、mean() 等操作,行為和 int64 類似。差別在於:涉及 <NA> 的計算預設會跳過缺失值(類似 skipna=True),不會因為 NaN 讓整個聚合結果變成 NaN。這讓後續的年份趨勢分析(例如每年遊戲銷售量 groupby Year)能正確運作。
直覺:如果確定 Year 欄完全沒有缺失值,用 astype(int) 還是 astype('Int64') 更好?
答案:沒有缺失值時,astype(int) 或 astype('int64') 稍微更好,因為 numpy int64 的記憶體效率比 pandas Int64 高(Int64 需要額外的 boolean 遮罩),且與 numpy 運算的相容性更高。但如果不確定資料後續會不會引入 NaN,保守起見用 Int64 也沒問題。
直覺:除了 astype('Int64'),還有什麼方法可以把有 NaN 的 float64 欄轉成整數型態?
答案:兩種:(1) pd.array(data['Year'], dtype='Int64'),等效於 astype('Int64')。(2) 如果確定要刪掉有 NaN 的資料,先 dropna(subset=['Year']) 再 astype('int64'),但這會永久刪除整筆資料列。最安全的做法仍是 astype('Int64'),保留缺失值資訊,後續視分析需求決定如何處理。
直覺:什麼時候應該填補缺失值(fillna),什麼時候應該保留缺失值(Int64)?
答案:保留缺失值(Int64):缺失本身有意義(例如某遊戲真的沒有記錄年份),或後續分析工具能自動跳過 NaN。填補(fillna):缺失值必須被處理才能進行特定分析(例如機器學習模型不接受 NaN),且填入的值語意合理(例如年齡缺失填平均年齡)。以中位數、平均數、眾數填補是常見的合理策略;填 0 或 1 通常是語意錯誤的。
直覺:轉換後如何確認 Int64 轉換是否成功、NaN 是否正確保留?
答案:兩行驗證:data['Year'].dtype 應該顯示 Int64(注意大寫 I);data['Year'].isna().sum() 的結果應與轉換前相同(缺失值個數不變)。如果 dtype 是 int64(小寫)就表示轉換到了 numpy int,不是可空整數。isna().sum() 變成 0 表示缺失值被意外填補了,需要重新檢查轉換步驟。
想再往下看,這 5 個
- 資料填補(Data Imputation)決定是否填補 NaN 及填什麼值,是資料前處理最需要謹慎的決策,直接影響下游分析的正確性。
- 資料前處理(Data Preprocessing)型別轉換是資料前處理的標準步驟,必須在建模或分析之前確保每個欄位的型別符合需求。
- 特徵工程(Feature Engineering)Year 轉整數後可以進一步衍生特徵,例如計算遊戲上市至今年數、做年份分組等。
- 資料管線(Data Pipeline)生產環境中,型別推斷和轉換通常寫在資料管線的前處理步驟,確保下游模型收到乾淨資料。
- 資料品質監控(Data Quality Monitoring)缺失值比例和型別一致性是資料品質的關鍵指標,建立監控機制能及時發現上游資料問題。