Part 2: Survival analysis in Credit Risk Model
A middle way of survival analysis in credit risk model.
มาถึงตอนที่ 2 ของการทำ Survival analysis เพื่อใช้งานในเชิง Credit Risk แพลนที่วางไว้คือ ตอนที่ 2 ควรเป็นตอนจบของ Analysis นี้แล้ว แต่รู้สึกเหมือนอยากลง Details ให้มากกว่านี้ เลยคิดว่าควรต้องมีต่ออีกตอน เพื่อแบ่งเนื้อหาในแต่ละตอน ไม่ให้เยอะจนเกินไป สำหรับตอนที่ 1 สามารถอ่านได้จาก Link ด้านล่าง
Default rate calculation
ตอนก่อนหน้านี้ จบกันที่ Data table ที่สร้าง Flag ที่ต้องการเรียบร้อยแล้ว ขั้นตอนต่อไปเป็นการ “นับ” หรือการ Calculation เพื่อให้เกิด Default rate เพื่อหา PD ตามที่ต้องการ แต่ก่อนลงไปถึงการเขียน Code ควรมีความเข้าใจ Concept ก่อน เพราะอาจนำไปปรับใช้ใน Code เพื่อเพิ่ม Performance ได้อีกต่อไป
ขอสร้าง Data sample ใหม่ เพื่อให้เห็นภาพในการคำนวณมากขึ้น แต่ Data structure ยังเหมือนเดิมจากที่สร้างไว้ตอนที่แล้ว
จากข้อมูลตัวอย่างที่สร้างไว้เป็น Sample ของ 1 Account ในทุก ๆ Transaction ที่มีข้อมูล โดยที่ข้อมูลมีทั้งหมด 20 เดือนที่ทำการ Lagging default status ไว้แล้วในหน่วยเดือน
การ Observe transaction ที่ Default (= 1) เป็นการเริ่มจาก Starting point ของ Transaction นั้น ๆ แล้วหาระยะเวลาจากจุดที่ Observe ไปจุดถึง Default ครั้งแรก (First default) ว่ามีระยะเท่าไหร่ จากรูปด้านบนจะเห็นได้ว่าจาก Transaction แรกจะไป Default ที่เดือนที่ 11 เช่นเดียวกันกับ Transaction ที่สอง จะไป Default ที่เดือนที่ 10 ข้อสังเกตคือ Default trigger ยังอยู่เดิม แต่ที่เปลี่ยนไปคือ จุดของ Transaction ที่เริ่ม Observe
ต่อมาเป็นการ Observe transaction ที่ไม่ Default หมายถึงไม่เคยเกิดการ Default ขึ้นนับจากจุดที่ Observe จากตัวอย่างจะเห็นได้ว่า กา Observe transaction ที่ไม่มี Default status คือการหาระยะเวลาจากจุดที่เริ่ม Observe ไปจนกระทั่งจบอายุสัญญาของ Transaction นั้น ๆ
สุดท้ายคือส่วนที่เป็นค่า Missing ที่มีการ Flag ที่ dfFlag
ไว้ตั้งแต่ตอนก่อนหน้านี้ ต้อง Apply code อย่างระมัดระวังเพื่อไม่ให้ การนับระยะเวลาจากจุดที่ Observe เลยจนไปถึง Missing status
เมื่อได้ Logic ทั้งหมดแล้ว ก็สามารถนำทั้งหมดไป Apply เพื่อสร้าง Code ขึ้นมาใน Section ถัดไป
Code
จาก Logic ทั้งหมดยาว ๆ เพื่อนำมาเขียนเป็น Code ได้ 1 Chuck สั้น ๆ เท่านั้น ขอแกะ Logic ออกเป็นข้อ ๆ เพื่อให้เห็นภาพเป็นขั้นเป็นตอน
Import ข้อมูลชุดเดียวกันจาก Section ด้านบน ต่อมาเป็นการสร้างเงื่อนไข หรือ Condition โดยที่ต้องการ Index ที่มีค่าเท่ากับ 1 จาก Array ทั้งหมด ที่ต้อง -1
เพราะตัวแปร lag จากตอนที่แล้ว มีค่าเท่ากับ Length of data แต่ข้อมูลเดือนสุดท้าย ไม่สามารถใช้งานได้เนื่องจาก ต้องการข้อมูลอย่างน้อย 1 เดือนในการ Observe
เนื่องจากไม่ต้องการให้ Missing value หรือ nan
มาเป็นเงื่อนไข เพราะอาจส่งผลต่อ Transaction ที่ปกติ ดังนั้นจึงใช้ np.ma.masked_invalid()
เพื่อ Mask หรือปิด Array ที่มีค่าเป็น nan
ไว้
เมื่อสร้าง Condition ได้แล้ว จะได้เป็น Array ที่เก็บค่าเป็น True, False และ Masked ข้อมูลที่ Missing ไว้เรียบร้อยแล้ว
np.where(np.any(condition, 1), 1, 0)
ต่อมาเป็นการสร้าง Columns ผลลัพธ์เก็บเข้าไปใน DataFrame เดิมที่มีอยู่ โดยใช้ np.where()
เพื่อสร้าง Columns จาก Condition ที่เขียนไว้ด้านบน เริ่มจาก ‘lifetimeFlag’
เป็นการสร้าง Flag (0, 1) เพื่อเป็นตัวกำหนดว่า Transaction ที่กำลัง Observe มี Default stage เกิดขึ้นหรือไม่ ซึ่งค่อนข้างตรงไปตรงมาคือ เมื่อ np.any()
ในแต่ละ Column axis = 1
เข้าเงื่อนไขในตัวแปร condition
ให้ตอบ 1
ไม่เข้าเงื่อนไขให้ตอบ 0
np.where(np.any(condition, 1), condition.argmax(1) + 1, np.ma.MaskedArray.count(condition, 1))
อีก Column ที่ต้องสร้างขึ้นมาคือ ‘times’
หรือระยะเวลาที่ Observe จาก Logic ที่ได้อธิบายไปใน Section ด้านบน ซึ่งเงื่อนไข้ยังเหมือนเดิมคือ np.any()
ในแต่ละ Column ว่าเข้าเงื่อนไขในตัวแปร condition
หรือไม่
ถ้าเข้าเงื่อนไขให้ Return ค่า Index แรกที่มีค่ามากที่สุดด้วย .argmax(axis = 1)
ซึ่งใน condition
มีการเปรียบเทียบแค่ 0 กับ 1 เท่านั้น ดังนั้นเมื่อเจอ Index ที่มีค่ามากกว่า 0 จึง Return ค่ากลับมาเป็น Index นั้น ๆ หรือ Index แรกที่เจอค่า 1 นั่นเอง และต้องทำการ +1
เพราะใน Array มีค่าเริ่มที่ 0 แต่ Lagging status ที่สร้างไว้มีค่าเป็น 1
ถ้าไม่เข้าเงื่อนไขให้ทำการนับ โดยต้องไม่นับ Missing value ที่ได้ทำการ Masked ไว้แล้ว ให้ใช้ np.ma.MaskedArray.count()
และผ่านตัวแปร condition
ที่สร้างเงื่อนไขเป็น Array ไว้ และผ่านแกนที่ axis = 1
เมื่อรัน Code เสร็จแล้ว จะได้เป็น DataFrame ที่มีการสร้าง Flag ตามเงื่อนไขที่ต้องการ การเขียน Code แบบที่นำเสนออยู่เป็นเขียนในเชิง Vectorizer ซึ่งมีความเร็วมากขึ้นในการใช้งานร่วมกับข้อมูลขนาดใหญ่ แต่หากข้อมูลมีขนาดไม่ใหญ่มาก การใช้ Iteration ปกติ ก็สามารถสร้าง Flag ลักษณะนี้ได้เช่นกัน
Next step
Blog ตอนนี้เขียนอธิบายไว้ค่อนข้างละเอียด เนื้อหา Code อาจมี Chuck เดียวสั้น ๆ แต่ภายในมีการทำงานของ Survival function อยู่ ในตอนต่อไปเป็นการสร้าง Summary table เพื่อ Construct เป็น Survival curves ต่าง Key indicators ที่ต้องการ ขอจบเนื้อหาตอนนี้ไว้เพียงเท่านี้ก่อน