Part 1: Survival analysis in Credit Risk Model
A story about survival analysis applying for credit risk model.
ช่วงหลังมานี้… ได้เห็นการใช้งานโมเดล Survival analysis ในเชิงของ Credit risk มากขึ้น เพราะว่ากันตามความเป็นจริงแล้ว Credit risk model ส่วนมาก เกิดขึ้นมาเพื่อหา Probability ของเหตุการณ์ใดเหตุการณ์หนึ่งที่อาจเกิดขึ้น ดังนั้น Concept ของ Survival analysis จึงสามารถนำมาประยุกต์ใช้ได้
Survival analysis
Survival analysis มีชื่อเรียกอย่างเป็นทางการในภาษาไทยว่า การวิเคราะห์ระยะปลอดเหตุการณ์ เป็นการหาระยะเวลาของ Time to event รูปแบบนึง โดยที่ Event เป็นสิ่งที่สนใจในโมเดล หรือ Target ที่ต้องการให้โมเดลมาช่วยแก้ปัญหา ซึ่ง Survival analysis สามารถแบ่งออกได้เป็นหลายรูปแบบตามสิ่งที่ต้องการ Estimate และตาม Assumption ที่ตั้งขึ้นมา เมื่อเกิด Assumption ที่แตกต่างกัน ทำให้ชื่อเรียกการวิเคราะห์ ชนิดนี้มีความแตกต่างกันออกไป เช่น Vintage analysis หรือ Cohort analysis รวมไปถึงมีการใช้แบบจำลองเข้ามาช่วย เช่น Kaplan-Meier estimator และ Cox hazard model เป็นต้น
พูดถึง Survival analysis ในเชิงของ Credit risk model นิยมใช้กันอย่างแพร่หลายในลักษณะการทำ Credit scoring model เพื่อใช้วัดพฤติกรรมของลูกหนี้ และหาโอกาสที่อาจเกิดหนี้เสีย แต่ Scoring model ยังไม่ใช้เป็นเนื้อหาหลักของ Blog ตอนนี้ หากมีโอกาสคงได้เขียนถึงในตอนต่อ ๆ ไป โดยเนื้อหาหลักของตอนนี้เป็นพื้นฐานการใช้งาน Survival analysis เพื่อใช้หา Probability of Default (PD Model)
Model assumption
Assumption ที่ใช้ในการพัฒนา PD Model คือการหา Time to event ซึ่ง Event ในที่นี้คือ Default stage จากรูปข้างบนเป็นตัวอย่างที่ค่อนข้างชัดเจนว่า เมื่อ Given observation time period หนึ่งให้มีการศึกษาพฤติกรรมของลูกหนี้ ที่อาจเกิด Default ขึ้น จากนั้นให้นับระยะเวลาจากจุดที่ Observe จนถึงไปถึง Event ว่ามีความยาวเท่าไหร่จนถึง Default stage หรือในทางกลับกันหากไม่มี Event (Default) เกิดขึ้นลูกหนี้สามารถ Perform ใน Portfolio ได้เป็นระยะเวลาเท่าไหร่จนจบช่วง Observation time
โดยการ Observe สามารถทำได้ทั้งระดับ Account level หรือ Transaction level อยู่ที่ Assumption ว่าความเสี่ยงของลูกหนี้เกิดขึ้นเป็นรายตัวบุคคล หรือความเสี่ยงเปลี่ยนไปตามแต่ละช่วงเวลาที่ Observe
Dataset
2 Section ด้านบนเป็นการปูพื้นฐานให้เห็นหลักการทำ Survival analysis รวมถึงไป Assumption ที่ใช้ในการพัฒนาแบบจำลอง Section นี้คือการนำ Concept เหล่านั้นมาใช้กับข้อมูลจริง
Dataset ที่ใช้ในครั้งนี้ไม่สามารถลงรายละเอียดได้มาก แต่เป็นลักษณะของ Transaction รายเดือนที่เก็บพฤติกรรมของลูกหนี้เอาไว้ใน Table แบบ Long format และ Dataset ผ่านการ Cleansing เพื่อให้พร้อมสำหรับการใช้งานมาแล้ว
Code
Libraries ที่ใช้งานเป็น Libraries พื้นฐานสำหรับการทำ Data table ทั่วไป แต่มี Library ที่ใช้ทำ Survival analysis โดยเฉพาะชื่อว่า Lifelines ซึ่งมีหลาย Module ให้เลือกใช้งาน สามารถทดลองใช้งานกันดูได้ แต่ใน Blog ตอนนี้ใช้งานเฉพาะตัวพื้นฐานปกติก็เพียงพอแล้ว
Total rows: 12244923
Total columns: 7
เมื่ออ่าน Dataset แล้วพบว่าข้อมูลมีขนาดค่อนข้างใหญ่ สเปคเครื่องที่ใช้งานอยู่ ณ ตอนนี้มี Ram อยู่ 8GB และยังสามารถจัดการข้อมูลชุดนี้ได้ แต่หากว่ามีข้อมูลที่มากขึ้นกว่านี้ อาจต้องเป็นจำต้องจัดเก็บข้อมูลในรูปแบบ Sparse data type เพื่อลดการจอง Memory ของเครื่อง
ทำการสร้าง Event flag หรือ Default stage ให้แต่ละ Transaction ซึ่งเงื่อนไขคือ Aging ≥ 4
ให้ถือว่าเป็น Default transaction สำหรับ Flag อื่น ๆ เช่น Status ที่เป็น Inactive transaction หรือ Write-off (ปิดบัญชี) อาจขึ้นกับลักษณะการเก็บ Data ที่แตกต่างกันออกไป โดย Dataset ที่มีอยู่ข้อมูลที่ Inactive และ Write-off มีการเก็บ Default = 0
ดังนั้นหากไม่ทำการแก้ไขจะทำให้มีคนดี (ไม่ Default) ติดเข้ามามากเกินไป ดังนั้นจึงทำการแก้ไข dfFlag
ใหม่ให้มีค่าเป็น np.nan
(Missing value)
ในการสร้างแบบจำลองนี้ ต้องการให้ Risk segment เป็นปีที่ใช้ในการ Observe ดังนั้นจึงมีการสร้าง Column Year
จาก As of month
ให้เป็น Aggregation สำหรับการ .groupby()
จากนั้นสร้าง Table ด้วยการ Lag ข้อมูลก่อนหน้าเพื่อดู Status ที่เกิดขึ้นว่าเป็น Event ที่ต้องการหรือไม่ โดยการ Observe เกิดขึ้นในลักษณะ Monthly basis หรือการเป็นการ Observe เดือนต่อเดือน จากข้อมูลทั้งหมดมีทั้งหมด 71 เดือน ดังนั้นข้อมูลที่สามารถ Observe ได้ทั้งหมดจึงอยู่ที่ 70 เดือน เพราะเดือนที่ 71 ที่เป็นเดือนสุดท้ายจะไม่มี Status เดือนต่อไปให้ Observe แล้ว
แม้ว่าการ Observe เกิดขึ้นในระดับ Monthly basis แต่ต้องพิจารณาในระดับรายลูกหนี้ด้วยเช่นกัน เพื่อไม่ให้พฤติกรรมของลูกหนี้เกิดความซ้อนทับกัน ดังนั้นจำเป็นต้อง .groupby([‘AccID’])
ก่อน [‘dfFlag’].shift(-i)
เพื่อเป็นการ Lagging
Column: dfFlag1 created
Column: dfFlag2 created
...
Column: dfFlag70 created
Code chunk ข้างบนนี้อาจไม่จำเป็นต้องสร้าง Flag นี้ก็ได้ แต่เนื่องจากแบบจำลองนี้อาจนำไปใช้ต่อกับ IFRS 9 Model ดังนั้นจึงขอสร้าง 12-months default แบบ Ever default concept เก็บไว้ แต่ขอไม่ลงรายละเอียด เนื่องจากเนื้อหาอาจมีความยาวมากเกินไป
มาถึง Code chunk สุดท้ายก่อนจบ Blog ตอนนี้คือการ Exclusion ข้อมูลทั้งหมดที่ไม่ Valid ใน Assumption ของ Model ออกไป ซึ่งขั้นตอนนี้ต้องเกิดขึ้นเป็นอย่างสุดท้าย (ไม่นับรวมการ Cleansing) เพราะหากเกิดขึ้นก่อน อาจทำให้บาง Information ขาดหายไป Exclusion ที่เกิดขึ้นทั้งหมดคือ
- เลือกเฉพาะ Active transaction (ไม่รวม Inactive และ Write-off)
- ตัด Transaction ที่ Observation ไม่ถึง 12 เดือน เพราะต้องการนำไปใช้งานในแบบจำลอง IFRS 9 ที่สร้าง Flag
everDefault12
ไว้ ดังนั้นข้อมูลที่ Observe ได้จึงควรมีอย่างน้อย 12 เดือน - ตัด Defaulted ที่จุด Observe ออกไปทั้งหมด เพราะต้องการ Probability of Default ดังนั้น Defaulted ที่จุด Observe จึงไม่มี Probability ที่อาจเกิดขึ้นแล้ว
- ตัดข้อมูลที่ไม่สามารถ Observe เดือนต่อไปได้ เช่น ข้อมูล Transaction สุดท้ายของลูหนี้ ไม่สามารถหาพฤติกรรมของ Transaction ต่อไปได้ จึงต้อง Exclude ออกไป
การตัดข้อมูลใน Pandas DataFrame อาจทำได้หลายวิธีเช่นการใช้ df[df[‘columnName’] == condition]
หรือการใช้งาน .loc[]
, iloc[]
หรือ .at[]
แต่หากต้องทำงานกับข้อมูลที่ค่อนข้างเยอะ การใช้ df.query()
สามารถช่วยลด Runtime และ Reduce memory ได้มาก เพราะไม่ได้มีการสร้าง Temporary variable อยู่เบื้องหลังการทำงาน
รายละเอียดการเพิ่ม Efficient operations สามารถอ่านเพิ่มเติมได้จาก Link ด้านล่างมีประโยชน์มากสำหรับการทำงานกับข้อมูลประเภท Table ที่มีขนาดใหญ่
Next step
จบ Blog ตอนนี้ด้วยข้อมูลที่พร้อมสำหรับการสร้าง Survival analysis แล้ว Blog ตอนนี้เป็นเพียงไม่กี่ตอนที่แพลนเอาไว้ว่า อยากเขียนแยกเป็น 2 ตอน (หรือมากกว่า) จากปกติที่ Section สุดท้ายต้องเป็น Conclusion ในตอนนี้จึงเป็น Next step แทน เพราะต้องมีการทำ Table เพื่อให้ได้ผลลัพธ์ Survival analysis ซึ่งวิธีการนั้นจะอยู่ใน Blog ตอนต่อไป