ทำไม Missing Data ถึงเป็นปัญหาใหญ่ในงาน Data Science?
ถ้าคุณเคยโหลด CSV แล้วเจอคอลัมน์เต็มไปด้วย NaN — ยินดีต้อนรับเข้าสู่โลกความจริง ข้อมูลจริงๆ มันไม่มีทางสมบูรณ์ 100% หรอก ไม่ว่าจะเป็นแบบสำรวจที่คนตอบข้ามคำถาม เซ็นเซอร์ IoT ที่ส่งข้อมูลไม่ทัน หรือระบบที่บันทึกค่าผิดพลาด ทุกกรณีล้วนสร้าง "ช่องว่าง" ในข้อมูลทั้งนั้น
พูดตรงๆ ว่า ถ้าจัดการ Missing Data ไม่ดี ผลที่ตามมาค่อนข้างเจ็บปวดเลย:
- ค่าเฉลี่ยและสถิติเพี้ยน —
NaNจะถูกข้ามในการคำนวณ ทำให้ค่า mean, sum, count ไม่สะท้อนความเป็นจริง - โมเดล ML พัง — อัลกอริทึมส่วนใหญ่ (เช่น Linear Regression, SVM) ไม่ยอมรับค่า NaN ตรงนี้เจอบ่อยมาก
- กราฟบิดเบี้ยว — matplotlib จะข้ามจุดที่มี NaN ทำให้เส้นกราฟขาดหายไปเฉยๆ
- การ merge ผิดพลาด — NaN ใน key column จะทำให้ merge ไม่เจอคู่ที่ตรงกัน
บทความนี้จะพาคุณจัดการ Missing Data อย่างมืออาชีพ ตั้งแต่การตรวจจับไปจนถึงเทคนิคขั้นสูง อัปเดตสำหรับ pandas 3.0 พร้อมโค้ดที่ก๊อปไปใช้ได้ทันที
NaN vs None vs pd.NA — ค่าที่หายไป 3 แบบใน pandas 3.0
ก่อนจะลงมือจัดการอะไร ต้องเข้าใจก่อนว่า pandas ใช้ 3 ตัวแทน สำหรับค่าที่หายไป แต่ละตัวมีพฤติกรรมต่างกันพอสมควร
np.nan — ตัวเก่าแก่
np.nan คือค่า floating-point พิเศษจาก NumPy ที่ pandas ใช้มาตั้งแต่เริ่มต้น ข้อจำกัดสำคัญคือ มันบังคับให้คอลัมน์แปลงเป็น float — ถ้าคุณมีคอลัมน์จำนวนเต็มที่มีค่าหายไป pandas จะแปลงทั้งคอลัมน์เป็น float64 ให้โดยอัตโนมัติ ซึ่งเป็นเรื่องน่ารำคาญทีเดียว
import numpy as np
import pandas as pd
s = pd.Series([1, 2, np.nan, 4])
print(s.dtype)
# float64 ← ถูกบังคับแปลงจาก int!
None — ค่า null ของ Python
None คือ null object ของ Python ปกติ ใช้ได้กับคอลัมน์ประเภท object เมื่ออยู่ในคอลัมน์ตัวเลข pandas จะแปลงเป็น np.nan ให้อัตโนมัติ
pd.NA — มาตรฐานใหม่ของ pandas 3.0
pd.NA คือค่าที่หายไปแบบ "universal" ใช้ได้กับทุก dtype โดยไม่ต้องบังคับแปลง ใน pandas 3.0 ค่านี้มีบทบาทสำคัญมากขึ้นกว่าเดิมเยอะ:
# pandas 3.0 — ใช้ nullable int dtype
s = pd.array([1, 2, pd.NA, 4], dtype="Int64")
print(s.dtype)
# Int64 ← ไม่ถูกแปลงเป็น float!
# พฤติกรรมสำคัญ: pd.NA ใช้ three-valued logic
print(pd.NA == 1) # (ไม่ใช่ False เหมือน np.nan)
print(pd.NA | True) # True (Kleene logic)
print(pd.NA & False) # False (Kleene logic)
คำแนะนำสำหรับ 2026: ใช้ pd.NA กับ nullable dtypes (Int64, Float64, boolean, string) เป็นค่าเริ่มต้นไปเลย เพราะหลีกเลี่ยงปัญหา type coercion ที่เคยเป็นเรื่องปวดหัวมาตลอด
ตรวจจับ Missing Data — หาให้เจอก่อนแล้วค่อยจัดการ
ก่อนจะลบหรือเติมค่า ต้อง "สำรวจ" ก่อนว่ามีข้อมูลหายไปที่ไหน เท่าไหร่ เป็นรูปแบบอะไร ขั้นตอนนี้สำคัญมากแต่หลายคนชอบข้ามไป
isna() / isnull() — ตรวจจับค่าที่หายไป
import pandas as pd
import numpy as np
df = pd.DataFrame({
"name": ["สมชาย", "สมหญิง", None, "สมศักดิ์", "สมศรี"],
"age": [28, np.nan, 35, np.nan, 42],
"salary": [55000, 62000, np.nan, 48000, np.nan],
"department": ["IT", "HR", "IT", None, "HR"]
})
# ดูภาพรวม Missing Data ทุกคอลัมน์
print(df.isna().sum())
# name 1
# age 2
# salary 2
# department 1
# คำนวณเปอร์เซ็นต์ Missing ต่อคอลัมน์
pct_missing = (df.isna().sum() / len(df)) * 100
print(pct_missing)
# name 20.0
# age 40.0
# salary 40.0
# department 20.0
info() — ดูภาพรวมอย่างรวดเร็ว
df.info()
#
# RangeIndex: 5 entries, 0 to 4
# Data columns (total 4 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 name 4 non-null object
# 1 age 3 non-null float64
# 2 salary 3 non-null float64
# 3 department 4 non-null object
สังเกตว่า info() แสดง Non-Null Count ถ้าน้อยกว่าจำนวนแถวทั้งหมด แปลว่ามี Missing Data ในคอลัมน์นั้น วิธีนี้ง่ายดีเลยสำหรับดูคร่าวๆ
Heatmap — สร้างแผนที่ Missing Data
สำหรับ DataFrame ขนาดใหญ่ การดูด้วยตาเปล่าคงไม่พอ ลองใช้ seaborn สร้าง heatmap ดูแทน:
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 6))
sns.heatmap(df.isna(), cbar=True, yticklabels=False, cmap="viridis")
plt.title("Missing Data Heatmap")
plt.tight_layout()
plt.show()
ถ้าเห็นรูปแบบใน heatmap — เช่น ค่าหายไปพร้อมกันหลายคอลัมน์ หรือหายไปเป็นช่วงๆ — ข้อมูลพวกนี้จะช่วยให้เลือกกลยุทธ์จัดการได้ถูกต้องมากขึ้น
dropna() — ลบแถวหรือคอลัมน์ที่มีค่าหายไป
dropna() เป็นวิธีที่ตรงไปตรงมาที่สุด ลบทิ้งเลย แต่ต้องระวังนิดนึงเพราะคุณกำลังสูญเสียข้อมูลจริงๆ นะ
การใช้งานพื้นฐาน
# ลบแถวที่มี NaN อย่างน้อย 1 ค่า
df_clean = df.dropna()
print(f"เหลือ {len(df_clean)} จาก {len(df)} แถว")
# เหลือ 2 จาก 5 แถว ← หายไปเกินครึ่ง!
เห็นไหม? แค่ใช้ dropna() แบบไม่ระวัง ข้อมูลก็หายไปเกินครึ่งแล้ว
พารามิเตอร์สำคัญ
# how='all' — ลบเฉพาะแถวที่ทุกค่าเป็น NaN
df.dropna(how="all")
# axis=1 — ลบคอลัมน์แทนแถว
df.dropna(axis=1)
# thresh=3 — เก็บแถวที่มีค่าจริงอย่างน้อย 3 ค่า
df.dropna(thresh=3)
# subset — ดูเฉพาะบางคอลัมน์เท่านั้น
df.dropna(subset=["name", "age"])
เมื่อไหร่ควรใช้ dropna()?
- ข้อมูลหายไปน้อยกว่า 5% ของ dataset
- ข้อมูลหายไปแบบ MCAR (Missing Completely At Random) — ไม่มีรูปแบบชัดเจน
- มีข้อมูลเยอะพอที่ลบแล้วไม่กระทบคุณภาพ
คำเตือน: ถ้าข้อมูลหายไปอย่างเป็นระบบ (เช่น คนรายได้สูงไม่กรอกรายได้) การลบจะทำให้ dataset มี bias ตรงนี้ต้องใช้วิธีอื่นแทน
fillna() — เติมค่าที่หายไปด้วยวิธีต่างๆ
มาถึงฟังก์ชันที่น่าจะใช้บ่อยที่สุดแล้ว fillna() เป็นเครื่องมือหลักสำหรับเติมค่าที่หายไป มีหลายกลยุทธ์ให้เลือกขึ้นอยู่กับลักษณะข้อมูลของคุณ
เติมด้วยค่าคงที่
# เติมทุกคอลัมน์ด้วยค่าเดียว
df.fillna(0)
# เติมค่าต่างกันสำหรับแต่ละคอลัมน์
df.fillna({"age": 0, "salary": 0, "department": "ไม่ระบุ", "name": "ไม่ทราบ"})
เติมด้วยค่าเฉลี่ย / มัธยฐาน / ฐานนิยม
# เติมด้วยค่าเฉลี่ย (เหมาะกับข้อมูลแจกแจงปกติ)
df["age"] = df["age"].fillna(df["age"].mean())
# เติมด้วยมัธยฐาน (เหมาะกับข้อมูลเบ้)
df["salary"] = df["salary"].fillna(df["salary"].median())
# เติมด้วยฐานนิยม (เหมาะกับข้อมูล categorical)
df["department"] = df["department"].fillna(df["department"].mode()[0])
Forward Fill (ffill) และ Backward Fill (bfill)
เติมค่าจากแถวก่อนหน้าหรือแถวถัดไป วิธีนี้เหมาะมากๆ สำหรับข้อมูล Time Series:
# สร้างข้อมูลราคาหุ้นจำลอง
stock = pd.DataFrame({
"date": pd.date_range("2026-01-01", periods=7, freq="D"),
"price": [100, np.nan, np.nan, 105, np.nan, 110, 112]
})
# Forward fill — ใช้ค่าล่าสุดที่มีเติมลงไป
stock["price_ffill"] = stock["price"].ffill()
# Backward fill — ใช้ค่าถัดไปเติมย้อนกลับ
stock["price_bfill"] = stock["price"].bfill()
# จำกัดจำนวน NaN ที่เติม
stock["price_ffill_limited"] = stock["price"].ffill(limit=1)
print(stock)
# date price price_ffill price_bfill price_ffill_limited
# 0 2026-01-01 100.0 100.0 100.0 100.0
# 1 2026-01-02 NaN 100.0 105.0 100.0
# 2 2026-01-03 NaN 100.0 105.0 NaN
# 3 2026-01-04 105.0 105.0 105.0 105.0
# 4 2026-01-05 NaN 105.0 110.0 105.0
# 5 2026-01-06 110.0 110.0 110.0 110.0
# 6 2026-01-07 112.0 112.0 112.0 112.0
สิ่งที่เปลี่ยนใน pandas 3.0: fillna(method="ffill") ถูก deprecated แล้ว ต้องใช้ df.ffill() และ df.bfill() โดยตรงแทนนะ
เติมค่าแยกตามกลุ่ม — ใช้ร่วมกับ groupby
ถ้าค่าเฉลี่ยรวมทั้ง DataFrame มันไม่ make sense ลองเติมด้วยค่าเฉลี่ยของกลุ่มแทน ผมใช้วิธีนี้บ่อยมากในงานจริง:
# สร้างข้อมูลตัวอย่าง
employees = pd.DataFrame({
"name": ["สมชาย", "สมหญิง", "สมศักดิ์", "สมศรี", "สมปอง", "สมใจ"],
"department": ["IT", "IT", "HR", "HR", "IT", "HR"],
"salary": [55000, np.nan, 42000, np.nan, 60000, 38000]
})
# เติม salary ด้วยค่าเฉลี่ยของแผนก (ไม่ใช่ค่าเฉลี่ยรวม)
employees["salary"] = employees.groupby("department")["salary"].transform(
lambda x: x.fillna(x.mean())
)
print(employees)
# name department salary
# 0 สมชาย IT 55000.0
# 1 สมหญิง IT 57500.0 ← ค่าเฉลี่ย IT
# 2 สมศักดิ์ HR 42000.0
# 3 สมศรี HR 40000.0 ← ค่าเฉลี่ย HR
# 4 สมปอง IT 60000.0
# 5 สมใจ HR 38000.0
วิธีนี้แม่นยำกว่าการเติมด้วยค่าเฉลี่ยรวมมาก เงินเดือนแผนก IT กับ HR ย่อมต่างกัน เติมด้วยค่าเฉลี่ยรวมมันก็ไม่สมเหตุสมผล
interpolate() — ประมาณค่าจากข้อมูลรอบข้าง
interpolate() คำนวณค่าที่หายไปโดยอาศัยจุดข้อมูลรอบข้าง ตัวนี้เหมาะมากกับข้อมูลที่มี แนวโน้ม (trend) หรือ ฤดูกาล (seasonality)
Linear Interpolation — วิธีเริ่มต้น
# ข้อมูลอุณหภูมิที่มีค่าหายไป
temp = pd.DataFrame({
"date": pd.date_range("2026-01-01", periods=8, freq="D"),
"temperature": [20.0, np.nan, np.nan, 26.0, 28.0, np.nan, 32.0, 33.0]
})
# Linear interpolation
temp["temp_linear"] = temp["temperature"].interpolate(method="linear")
print(temp)
# date temperature temp_linear
# 0 2026-01-01 20.0 20.0
# 1 2026-01-02 NaN 22.0 ← ประมาณค่าระหว่าง 20 กับ 26
# 2 2026-01-03 NaN 24.0
# 3 2026-01-04 26.0 26.0
# 4 2026-01-05 28.0 28.0
# 5 2026-01-06 NaN 30.0 ← ประมาณค่าระหว่าง 28 กับ 32
# 6 2026-01-07 32.0 32.0
# 7 2026-01-08 33.0 33.0
ผลลัพธ์ดูสมเหตุสมผลดีเลย ค่าที่เติมมาจะกระจายตัวสม่ำเสมอระหว่างจุดข้อมูลที่มีอยู่
วิธี Interpolation อื่นๆ
# Time-based interpolation (คำนึงถึง index ที่เป็น datetime)
temp = temp.set_index("date")
temp["temp_time"] = temp["temperature"].interpolate(method="time")
# Polynomial interpolation (เหมาะกับข้อมูลที่ไม่เป็นเส้นตรง)
temp["temp_poly"] = temp["temperature"].interpolate(method="polynomial", order=2)
# Spline interpolation (เส้นโค้งที่ราบเรียบ)
temp["temp_spline"] = temp["temperature"].interpolate(method="spline", order=3)
ควบคุมทิศทางการ Interpolate
# เติมเฉพาะไปข้างหน้า (ไม่ย้อนกลับ)
df["col"].interpolate(limit_direction="forward")
# เติมเฉพาะย้อนกลับ
df["col"].interpolate(limit_direction="backward")
# จำกัดจำนวน NaN ที่เติม
df["col"].interpolate(limit=2)
scikit-learn Imputer — เติมค่าสำหรับ ML Pipeline
ส่วนนี้สำคัญมากสำหรับคนที่ทำ Machine Learning เมื่อเตรียมข้อมูลสำหรับ ML ควรใช้ Imputer จาก scikit-learn แทน fillna() เพราะมันรวมอยู่ใน Pipeline ได้ ป้องกัน data leakage จาก test set
SimpleImputer — เติมด้วยค่าสถิติ
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split
# แยก train/test ก่อน
X_train, X_test = train_test_split(df[["age", "salary"]], test_size=0.3, random_state=42)
# สร้าง imputer — คำนวณค่าเฉลี่ยจาก train เท่านั้น
imputer = SimpleImputer(strategy="mean")
X_train_imputed = imputer.fit_transform(X_train)
# ใช้ค่าเฉลี่ยเดียวกันกับ test set
X_test_imputed = imputer.transform(X_test)
ทำไมต้องทำแบบนี้? ถ้าคำนวณค่าเฉลี่ยจากข้อมูลทั้งหมดรวม test set ด้วย แล้วเอามาเติม ข้อมูลจาก test set จะ "รั่ว" เข้ามาในขั้นตอนเตรียมข้อมูล ทำให้ผลประเมินโมเดลดีเกินจริง ตรงนี้เป็นข้อผิดพลาดที่เจอบ่อยมากจริงๆ
KNNImputer — เติมด้วย K-Nearest Neighbors
from sklearn.impute import KNNImputer
# เติมค่าจากเพื่อนบ้าน 5 ตัวที่ใกล้ที่สุด
knn_imputer = KNNImputer(n_neighbors=5)
X_train_knn = knn_imputer.fit_transform(X_train)
X_test_knn = knn_imputer.transform(X_test)
KNNImputer จะหาแถวที่ "คล้ายกัน" ที่สุดจากคอลัมน์อื่นที่ไม่หายไป แล้วเอาค่าเฉลี่ยของเพื่อนบ้านมาเติม วิธีนี้ให้ผลดีมากเมื่อตัวแปรมีความสัมพันธ์กัน (ซึ่งในข้อมูลจริงมักจะเป็นแบบนั้น)
IterativeImputer — เติมแบบ Multivariate
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
# เติมค่าโดยใช้ตัวแปรอื่นทำนาย
iter_imputer = IterativeImputer(max_iter=10, random_state=42)
X_train_iter = iter_imputer.fit_transform(X_train)
X_test_iter = iter_imputer.transform(X_test)
IterativeImputer สร้างโมเดลทำนายค่าที่หายไปจากตัวแปรอื่นๆ ทำงานคล้าย MICE (Multiple Imputation by Chained Equations) ให้ผลดีที่สุดในสามตัวนี้ แต่ก็ช้าที่สุดเช่นกัน ต้องชั่งน้ำหนักดูว่าคุ้มไหมกับ dataset ของคุณ
pandas 3.0 — ฟีเจอร์ใหม่สำหรับจัดการ Missing Data
pandas 3.0 ที่ออกเมื่อต้นปี 2026 มาพร้อมการเปลี่ยนแปลงสำคัญหลายอย่างที่กระทบการจัดการ Missing Data โดยตรง มาดูกันทีละข้อ
1. String Dtype เป็นค่าเริ่มต้น
# pandas 3.0 — string columns ใช้ str dtype แทน object
df = pd.DataFrame({"name": ["สมชาย", None, "สมศักดิ์"]})
print(df["name"].dtype)
# string ← ไม่ใช่ object อีกต่อไป
# ค่าที่หายไปแสดงเป็น ไม่ใช่ NaN
print(df["name"])
# 0 สมชาย
# 1
# 2 สมศักดิ์
2. convert_dtypes() — อัปเกรดเป็น nullable dtypes
ฟังก์ชันนี้ช่วยแปลง DataFrame เดิมให้ใช้ nullable dtypes ได้ง่ายๆ:
# แปลง DataFrame เดิมให้ใช้ nullable dtypes
df_old = pd.DataFrame({
"id": [1, 2, np.nan],
"score": [85.5, np.nan, 92.0],
"name": ["A", None, "C"]
})
df_new = df_old.convert_dtypes()
print(df_new.dtypes)
# id Int64
# score Float64
# name string
# ตอนนี้ id เป็น Int64 (ไม่ถูกแปลงเป็น float อีกต่อไป!)
print(df_new["id"])
# 0 1
# 1 2
# 2
3. Copy-on-Write (CoW) — ปลอดภัยจากการแก้ไขโดยไม่ตั้งใจ
อันนี้เป็นการเปลี่ยนแปลงที่ดีมากสำหรับคนที่เคยโดน SettingWithCopyWarning มาก่อน:
# pandas 3.0 เปิด Copy-on-Write เป็นค่าเริ่มต้น
df = pd.DataFrame({"A": [1, 2, 3], "B": [4, np.nan, 6]})
# การ fillna สร้าง copy ใหม่เสมอ ไม่กระทบ df เดิม
df_filled = df.fillna(0)
df_filled.loc[0, "A"] = 999
print(df.loc[0, "A"])
# 1 ← df เดิมไม่ถูกแก้ไข (CoW ปกป้องไว้)
กลยุทธ์เลือกวิธีจัดการ Missing Data — Decision Framework
จากประสบการณ์ที่เจอมา ผมสรุปเป็น framework ง่ายๆ ให้เลือกใช้ได้ตามสถานการณ์
ขั้นตอนที่ 1: สำรวจขอบเขตความเสียหาย
# คำนวณเปอร์เซ็นต์ Missing ทุกคอลัมน์
missing_pct = (df.isna().sum() / len(df)) * 100
print(missing_pct.sort_values(ascending=False))
ดูตัวเลขพวกนี้ก่อน แล้วค่อยตัดสินใจ
ขั้นตอนที่ 2: เลือกกลยุทธ์ตามสถานการณ์
| สถานการณ์ | วิธีแนะนำ | เหตุผล |
|---|---|---|
| Missing < 5% แบบสุ่ม | dropna() | ข้อมูลน้อย ลบทิ้งไม่กระทบ |
| ข้อมูลตัวเลข แจกแจงปกติ | fillna(mean) | ค่าเฉลี่ยรักษาการแจกแจง |
| ข้อมูลตัวเลข เบ้มาก | fillna(median) | มัธยฐานไม่ไวต่อ outliers |
| ข้อมูล Categorical | fillna(mode) | ใช้ค่าที่พบบ่อยที่สุด |
| Time Series มี trend | interpolate() | ประมาณค่าจากแนวโน้ม |
| Time Series ค่าคงที่ | ffill() / bfill() | ค่าก่อนหน้ายังใช้ได้ |
| ตัวแปรมีความสัมพันธ์กัน | KNNImputer | ใช้ข้อมูลจากตัวแปรอื่นช่วย |
| ต้องการผลดีที่สุด | IterativeImputer | ทำนายค่าจากตัวแปรอื่น |
ขั้นตอนที่ 3: ตรวจสอบผลลัพธ์
อย่าลืมตรวจสอบหลังเติมค่าเสร็จ ขั้นตอนนี้ข้ามไม่ได้:
# ตรวจสอบว่าไม่มี Missing Data เหลือ
assert df.isna().sum().sum() == 0, "ยังมี Missing Data เหลืออยู่!"
# เปรียบเทียบการแจกแจงก่อน-หลังเติมค่า
print("ก่อนเติม:", df_original["salary"].describe())
print("หลังเติม:", df_filled["salary"].describe())
ตัวอย่างจริง: Data Cleaning Pipeline ครบวงจร
ว่าแล้ว มาดูตัวอย่างการเอาเทคนิคทั้งหมดมาใช้รวมกันใน pipeline จริงๆ กัน:
import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split
# 1. โหลดข้อมูล
df = pd.read_csv("employees.csv")
# 2. แปลงเป็น nullable dtypes (pandas 3.0)
df = df.convert_dtypes()
# 3. สำรวจ Missing Data
print("=== Missing Data Report ===")
missing = df.isna().sum()
missing_pct = (missing / len(df)) * 100
report = pd.DataFrame({"count": missing, "percent": missing_pct})
print(report[report["count"] > 0].sort_values("percent", ascending=False))
# 4. ลบคอลัมน์ที่ Missing > 70%
cols_to_drop = missing_pct[missing_pct > 70].index
df = df.drop(columns=cols_to_drop)
# 5. เติมค่า categorical ด้วย mode
cat_cols = df.select_dtypes(include=["string", "object"]).columns
for col in cat_cols:
df[col] = df[col].fillna(df[col].mode()[0])
# 6. เติมค่า numeric ด้วยค่าเฉลี่ยตามกลุ่ม
num_cols = df.select_dtypes(include=["Int64", "Float64", "float64"]).columns
for col in num_cols:
df[col] = df.groupby("department")[col].transform(
lambda x: x.fillna(x.median())
)
# 7. เติมค่าที่ยังเหลือ (ถ้ากลุ่มทั้งกลุ่มเป็น NaN)
df[num_cols] = df[num_cols].fillna(df[num_cols].median())
# 8. ตรวจสอบสุดท้าย
assert df.isna().sum().sum() == 0
print(f"\nDataFrame สะอาดแล้ว: {df.shape[0]} แถว x {df.shape[1]} คอลัมน์")
คำถามที่พบบ่อย (FAQ)
ควรลบ (drop) หรือเติม (fill) ค่าที่หายไปดีกว่ากัน?
ขึ้นอยู่กับปริมาณและรูปแบบของข้อมูลที่หายไป ถ้าหายไปน้อยกว่า 5% และเป็นแบบสุ่ม (MCAR) การลบทิ้งก็โอเค แต่ถ้าหายไปเยอะหรือมีรูปแบบชัดเจน ควรเติมค่าด้วย fillna() หรือ interpolate() เพราะการลบจะทำให้สูญเสียข้อมูลที่มีค่าและอาจทำให้ dataset เกิด bias
ใน pandas 3.0 ควรใช้ NaN หรือ pd.NA?
สำหรับโปรเจกต์ใหม่แนะนำ pd.NA กับ nullable dtypes (Int64, Float64, string) เลย เพราะไม่บังคับแปลง type และรองรับ three-valued logic ที่สอดคล้องกับ SQL ส่วนโค้ดเดิมที่ใช้ np.nan สามารถแปลงได้ง่ายๆ ด้วย df.convert_dtypes()
fillna(method="ffill") ยังใช้ได้ไหมใน pandas 3.0?
ยังใช้ได้แต่จะโดน FutureWarning เพราะพารามิเตอร์ method ใน fillna() ถูก deprecated แล้ว ใช้ df.ffill() กับ df.bfill() โดยตรงแทน ซึ่งอ่านง่ายกว่าด้วย
จะป้องกัน data leakage ตอนเติม Missing Data ได้อย่างไร?
ต้อง แยก train/test ก่อน แล้วค่อยเติมค่า คำนวณค่าสถิติอย่าง mean หรือ median จาก training set เท่านั้น แล้วใช้ค่าเดียวกันเติมใน test set ใช้ SimpleImputer จาก scikit-learn ร่วมกับ Pipeline จะช่วยป้องกันได้โดยอัตโนมัติ ตรงนี้เป็นจุดที่ต้องระวังจริงๆ
interpolate() ต่างจาก fillna() อย่างไร?
fillna() เติมค่าคงที่หรือค่าสถิติเดียวกันทุกจุดที่หายไป ส่วน interpolate() จะ คำนวณค่าต่างกัน สำหรับแต่ละจุดโดยอาศัยข้อมูลรอบข้าง เหมาะกับข้อมูลที่มีแนวโน้มหรือลำดับ เช่น time series ข้อมูลเซ็นเซอร์ หรือข้อมูลอุณหภูมิ ถ้าข้อมูลไม่มีลำดับ ใช้ fillna() ก็เพียงพอแล้ว