Machine Learning › Feature engineering
Encoding ข้อมูลหมวดหมู่
ประมาณ 30 นาที
เป้าหมายของบท
- แปลงข้อมูลหมวดหมู่เป็นตัวเลขด้วย one-hot และเลือกใช้ ordinal เมื่อมีลำดับจริงเท่านั้น
- ใช้ OneHotEncoder ที่ fit บน train เพื่อให้คอลัมน์ตรงกันและรับหมวดใหม่ได้
- รู้ว่า target encoding ต้องคำนวณจาก train เท่านั้น
โมเดลกินได้แต่ตัวเลข
คอลัมน์ข้อความอย่าง city, channel, first_payment_method ต้องแปลงเป็นตัวเลขก่อนเข้าโมเดล วิธีที่ใช้บ่อยที่สุดคือ one-hot encoding:
สร้างคอลัมน์ 0/1 หนึ่งคอลัมน์ต่อหนึ่งหมวด
pd.get_dummies(df['city']) # 8 เมือง → 8 คอลัมน์
pd.get_dummies(df['city'], drop_first=True) # ทิ้งหมวดแรกไป → 7 คอลัมน์
drop_first=True ตัดคอลัมน์ที่คำนวณได้จากคอลัมน์อื่น (ถ้า 7 เมืองเป็น 0 หมดก็ต้องเป็นเมืองที่ 8) ช่วยลดความซ้ำซ้อนโดยเฉพาะกับโมเดลเส้นตรง
get_dummies vs OneHotEncoder
pd.get_dummies สะดวกสำหรับสำรวจ แต่ จำหมวดจากข้อมูลที่ส่งเข้าไปเท่านั้น — ถ้าแปลง train กับ test แยกกัน คอลัมน์อาจไม่ตรงกัน (เช่น เมืองที่ไม่มีใน test)
OneHotEncoder ของ scikit-learn ต้อง .fit บนข้อมูล train แล้ว .transform ทั้ง train และ test จึงได้คอลัมน์ชุดเดียวกันเสมอ
และตั้ง handle_unknown='ignore' เพื่อให้หมวดใหม่ที่ไม่เคยเห็นตอนเทรนกลายเป็นแถวที่เป็น 0 ทั้งหมด แทนที่จะ error
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(handle_unknown='ignore').fit(train[['city']])
encoder.transform(new_rows[['city']])
ordinal encoding: ระวังลำดับปลอม
OrdinalEncoder แปลงหมวดเป็น 0, 1, 2, … เหมาะกับข้อมูลที่ มีลำดับจริง (เช่น น้อย/กลาง/มาก) ถ้าใช้กับข้อมูลที่ไม่มีลำดับ (เช่น วิธีชำระเงิน) โมเดลเส้นตรงจะเข้าใจว่า
promptpay (3) “มากกว่า” cod (1) ซึ่งไม่มีความหมาย — ใช้ one-hot แทน
target encoding: ระวัง leakage
การแทนหมวดด้วยค่าเฉลี่ยของ target ในหมวดนั้น (เช่น อัตราซื้อซ้ำเฉลี่ยของแต่ละช่องทาง) อาจมีประโยชน์ แต่ ต้องคำนวณจากชุด train เท่านั้น ถ้าคำนวณจากข้อมูลทั้งชุด ค่าเฉลี่ยจะมี target ของแถว test ปนอยู่ — เป็น leakage ที่ทำให้ผลทดสอบดีเกินจริง
ตัวอย่างแก้โจทย์
กด "ขั้นต่อไป" ทีละขั้น ลองรันและแก้โค้ดในแต่ละขั้นได้
one-hot ด้วย get_dummies
แปลงคอลัมน์เมืองให้โมเดลใช้ได้
เมืองมี 8 หมวด → get_dummies ได้ 8 คอลัมน์
OneHotEncoder กับหมวดที่ไม่เคยเห็น
ลูกค้าจากเมืองที่ไม่มีในข้อมูลเทรนจะเป็นอย่างไร
fit encoder บนข้อมูลที่ไม่มี Hat Yai — ได้ 7 หมวด
เติมช่องว่าง
แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ
fit OneHotEncoder บนข้อมูล train แล้วนับจำนวนหมวดที่รู้จัก เก็บไว้ในตัวแปร n_cats เติม ___ ให้ครบ
แบบฝึกท้ายบท
บทนี้จะนับว่าเสร็จเมื่อผ่านข้อพื้นฐานและประยุกต์ครบ ข้อท้าทายได้ดาวเพิ่ม
🟢 พื้นฐาน
คอลัมน์ city มีกี่หมวด เก็บไว้ในตัวแปร n_cities
pd.get_dummies(df["city"]) ได้กี่คอลัมน์ เก็บไว้ในตัวแปร n_dummy_cols
ถ้าใส่ drop_first=True จะได้กี่คอลัมน์ เก็บไว้ในตัวแปร n_dummy_drop
เมืองที่มีลูกค้ามากที่สุด (ชื่อเมืองเป็น string) เก็บไว้ในตัวแปร top_city
🟡 ประยุกต์
fit OneHotEncoder(handle_unknown="ignore") บนลูกค้าที่ไม่ใช่ Hat Yai แล้ว transform ลูกค้า Hat Yai: เก็บจำนวนลูกค้า Hat Yai และผลรวมของค่าทั้งหมดหลัง transform ไว้ในตัวแปร n_hatyai, unseen_sum
แปลง train (ไม่มี Hat Yai) กับชุดที่มีแต่ Hat Yai ด้วย pd.get_dummies แยกกัน แล้วเก็บจำนวนคอลัมน์ของแต่ละชุดไว้ในตัวแปร train_cols, hatyai_cols
หมวดทั้งหมดของ first_payment_method ตามที่ OrdinalEncoder รู้จัก (เรียงตามตัวอักษร เก็บเป็น list) เก็บไว้ในตัวแปร payment_cats
🔴 ท้าทาย
target encoding แบบถูกวิธี: อัตราซื้อซ้ำเฉลี่ยของช่องทาง promo คำนวณจากชุด train เท่านั้น ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร te_train_promo
ถ้าคำนวณอัตราซื้อซ้ำของ promo จากข้อมูลทั้งชุด (รวมแถว test) แล้วลบด้วยค่าจากชุด train เพียงอย่างเดียว ผลต่างเท่าไร (ปัด 4 ตำแหน่ง) — นี่คือส่วนที่ target ของแถว test รั่วเข้ามา เก็บไว้ในตัวแปร te_gap
Quiz
สรุป
- one-hot: หนึ่งหมวดต่อหนึ่งคอลัมน์ 0/1 (drop_first ตัดคอลัมน์ซ้ำซ้อน) — OrdinalEncoder ใช้เฉพาะข้อมูลที่มีลำดับจริง
- fit encoder บน train แล้ว transform ทั้ง train/test และตั้ง handle_unknown="ignore" เพื่อรับหมวดใหม่โดยไม่ error
- target encoding ต้องคำนวณจาก train เท่านั้น — ถ้าใช้ข้อมูลทั้งชุด ค่าเฉลี่ยของ promo เพี้ยนไปจากค่าที่ควรเป็น (ตัวอย่างในบทต่างกัน ~0.004) เพราะ target ของ test รั่วเข้ามา
คำถามสัมภาษณ์
one-hot กับ ordinal encoding ต่างกันยังไง และเลือกใช้เมื่อไร
one-hot สร้างคอลัมน์ 0/1 ต่อหนึ่งหมวด ไม่สร้างลำดับ จึงเหมาะกับข้อมูลที่ไม่มีลำดับ (เมือง ช่องทาง วิธีชำระเงิน) ส่วน ordinal แปลงเป็นเลข 0,1,2 ตามลำดับหมวด เหมาะเมื่อหมวดมีลำดับจริงและระยะห่างพอสมเหตุสมผล (เช่น ระดับความพึงพอใจ) ถ้าใช้ ordinal กับข้อมูลที่ไม่มีลำดับ โมเดลเส้นตรงอาจตีความเป็นมาก-น้อยที่ไม่มีอยู่จริง
เจอคอลัมน์ที่มีหมวดเป็นพันๆ ค่า (high cardinality) เช่น รหัสสินค้า จะจัดการอย่างไร
one-hot ตรงๆ จะสร้างคอลัมน์มหาศาลและกระจัดกระจาย ทางเลือกได้แก่ รวมหมวดที่พบน้อยเป็น "อื่นๆ", ใช้ target encoding (คำนวณจาก train เท่านั้น หรือทำแบบ cross-fitted เพื่อกัน leakage), ใช้ embedding หรือโมเดลที่รับหมวดได้โดยตรง และเลือกวิธีด้วย cross-validation