Lending Club 데이터 분석: 데이터 개요, EDA, 전처리, 그리고 파생 변수 설계
| 프로젝트 개요 | • P2P 대출 플랫폼인 Lending Club에서는 투자자가 대출 신청자들의 신용위험을 면밀히 검토해야 하며, 채무 불이행(부도)의 위험은 투자 원금 및 이자의 손실로 이어질 수 있기 때문에 중요한 고려 요소 • Lending Club 플랫폼자체에서도 신청자의 신용도를 바탕으로 대출 승인 여부를 결정하고 등급(grade)에 따라 이자율을 책정 |
| 프로젝트 목표 | • 포트폴리오의 위험 대비 초과수익을 최대화 = sharpe ratio 최대화 |
| Sharpe Ratio | (포트폴리오 수익률 - 무위험이자율) / 수익률 변동성 • LC에서 부도가 예측되어 애출을 승인하지 않았더라면, 해당 투자금은 투자 결정 당시의 3년/5년만기 미 국채에 투자하였다고 가정 |
| data shape | (1,755,295, 141) - 전체 데이터의 약 60% 정도 • 40% 정도는 Private 성능 확인하는 데 사용 |
| internal rate of return | • LC의 대출은 원리금 균등사환 구조로 매월 일정한 상환금이 발생 • 투자자는 대출 실행 시 원금을 지급 → 대출자는 고정 상환금(원금 + 이자) 납입 • 본 프로젝트에서 IRR은 대출 투자 수익률을 측정하는 데 적용 |
데이터 전처리
loan_status(target value) 정제
loan_status(y label) unique 값들
- 'Fully Paid'
- 'Current'
- 'Charged Off'
- 'Late (16-30 days)'
- 'Late (31-120 days)'
- 'In Grace Period'
- 'Issued’
- 'Does not meet the credit policy. Status:Fully Paid'
- 'Does not meet the credit policy. Status:Charged Off'
- 'Default'
- nan
→ Current/Late 제외해야함. 대출의 최종 상태가 결정된 데이터만 사용하여야 하기 때문에
| loan_status 값 | 의미 | 유지 여부 | 레이블 |
| fully paid | 대출 완납 | ✅ 유지 | 0(정상) |
| charged off | 상환 실패(부도) | ✅ 유지 | 1(부도) |
| Default | 공식적 부도 상태 | ✅ 유지 | 1(부도) |
| Does not meet the credit policy. Status:Fully Paid | 정책 미충족이지만 완납됨 | ✅ 유지 | 0(정상) |
| Does not meet the credit policy. Status:Charged Off | 정책 미충족이며 부도 | ✅ 유지 | 1(부도) |
대출 심사 시점에 얻을 수 없는 후행 정보가 포함된 변수들을 제거
대출 심사 시점에서 알 수 없는 컬럼 제외
- 총 32개의 컬럼 제외
url 컬럼 제거
- url 컬럼으로 획득할 수 있는 정보인 loan_id는 이미 id 컬럼으로 얻을 수 있는 정보이므로 제외
url 들어가봤을 때
policy_code 컬럼 제거
모든 컬럼이 다 하나의 값을 갖고 있기 때문에 제거
pymnt_plan 컬럼 제거
int_rate 타입 변경 (object → float)
term 타입 변경 (object → int)
annual_inc(연소득) 결측치 2개 drop
- 둘 다 정상상환 라벨이므로 drop
python code
범주형 변수 중 빈도가 2% 미만인 범주 → 기타로 통합
참고:
- 데이터가 적은 소수 범주에 과적합 되는 것을 방지
9 값인 사람은 모두 타겟 1(부도)라는 건데, 이는 9 값을 갖는 사람이 1명이기 때문.
acc_open_past_24mths
지난 24개월 내 개설 신용계좌 수계좌 개설이 많으면 신용카드를 적극적으로 만든 것으로 해석되며, 과도한 신규계좌는 신용위험 상승 신호일수도
빼야할까?
🔗 참고
- 다른 프로젝트를 참고해보면, 다른 조건이 모두 동일할 때, 최근 24개월 동안 신규로 개설한 계좌가 1개 늘어나면, 채무불이행이 일어날 가능성이 기존보다 약 5% 높아진다는 결과를 얻었다고 함.
→ 빼는 건 ❌
- 계좌 개설 0건은 흔하고, 두 자릿수 개설도 가능하나 20개 이상처럼 비정상적으로 큰 값은
이상치로 간주할 수 있을 듯
all_util
전체 신용한도 대비 사용률utilization rate: (잔액 / 한도) * 100 → 0% ~ 100% 값이어야함
- 결측치가 많아 단순 대체로는 한계가 있음. 동일한 정보를 담은 다른 변수(
revol_util등)을 활용
annual_inc & annual_inc_joint
- 두 컬럼 통합(
annual_inc_total)
python code
annual_inc_joint의 결측률은 96% 이상으로, 공동 신청이 드문 데이터 특성상 대부분 NA임
showfliers=False 시각화
대출액 대비 소득비율 등을 통해 상환 가능성을 평가하는데 활용
annual_inc_total > 60,000,000 이상인 눈에 띄는 outlier drop
대부분의 큰 값들이 Sourve Verified 에 있음
bc_open_to_buy
(리볼빙) 신용카드 사용 가능 한도 = 모든 신용카드 계좌의 총 한도 중 현재 남은 여유금액0에 가까우면 카드 한도를 모두 소진했음을 의미
all_util과 bc_open_to_buy의 관계
→
bc_util
리볼빙 계좌 이용률
bc_util의 결측치는 적은데 all_util과 상관관계가 높음
all_util과 bc_util의 상관계수 = 0.6
이것도 100% 넘는 값들이 있음
- bc_util의 계산식
bc_util = (revol_bal) / (revol_bal + bc_open_to_buy) * 100
→ 이렇게 직접 계산했을 때는 100을 넘는 값이 없음. 직접 계산해서 bc_util을 대체
계산식 검증
직접 계산한 bc_util - 데이터의 bc_util 차이 시각화
chargedoff_within_12_mths
최근 12개월 내 채무 불이행 건수당연히 과거 1년 내 부도 경험이 있으면 새로운 대출 부도 확률도 높아질 것으로 예상됨
정상상환에서 78개의 NaN, 연체/부실에서 14개의 NaN을 가짐
→ 기록상 부도 이력이 확인되지 않았을 가능성이 큼. 0으로 대체. 혹시 실제 부도자 일부를 놓칠 수 있지만, 다른 변수로 어느정도 보완 가능할 것으로 예상됨
- 1번 이상인 값들의 비율이 적으므로 이진 변수로 변경
- NaN → 0
- 0 → 0
- 1 이상 → 1
dti & dti_joint
부채비율 및 공동 신청 부채 비율
- 이것 또한 공동 대출, 개인 대출의 dti를 하나로 합친다.
- 공동 대출 → dti_joint 사용
- 개인 대출 → dti 사용
- 주 채무자의 dti와 채무자 두명의 dti 값의 차이와 배수 차이를 파생변수로 추가
- dti_dff = df[’dti_joint’] - df[’dti’]
- dti_ratio = df[’dti_joint’] / df[’dti’]
공동 대출이어도 주 채무자의 dti 값이 있음
python code
- 이상치 처리
- 승인심사 기준 이상의 값을 단순화해 “고위험” 범주로 묶어버림
금융기관에서 36~43% 이하를 권장, 50% 이상일 떈 “더는 허용하기 어려운 수준”으로 간주
즉, dti 이상치를 50으로 캡핑
earliest_cr_line
최초 신용거래 개설 날짜신용 이력의 길이를 측정하는 지표
일반적으로 신용이력 기간이 길수록 부도 확률이 낮아지는 경향이 있음
- 결측치 제거
- 극소수이기 때문에 제거
결측치 14개
정상/상환이 13개
- 데이터 예시
'Sep-2005’
→ 신용 이력 연수로 변환
emp_length
재직 기간
- 순서형인데, 원-핫 인코딩을 해도 될지?
- 원-핫 인코딩 할 경우 → NaN 처리를 따로 안하여
emp_length_UNKOWN으로 둘 수 있음. 하지만 순서형이 잘 반영이 될지.. - 순서형으로 두게 될 경우 → NaN 처리를 어떻게 할지
emp_title
신청자가 기재한 직업명/직함, 자유 입력 문자열
- 공통 키워드로 묶어 큰 범주(의료계, IT, 교육 등)로 분류해 사용하는 경우도 있다고 함. 그러나 일반적으로
emp_length,annual_inc등 다른 변수로 충분히 유추 가능하다고 보고 제외한 프로젝트가 많은 것 같음. 또, 비용 대비 효과가 낮아 제거하는 게 나을 듯함. - 여유 시간이 있다면 진행해보는 것이 좋을 듯
fico_range_high & fico_range_low
FICO 신용점수 구간 상/하한(300 ~ 850)P2P 대출에서는 이 FICO로 등급을 매겨 금리를 결정할 정도로 핵심 지표
- 둘의 상관이 매우 높기 때문에 high나 low 둘 중에 하나 사용하거나 평균값을 사용하는 방법이 괜찮을 듯
EDA
sub_grade로만 int_rate(이자율)이 결정되는지?
sub_grade로만 결정되는 건 아님
- sub_grade 별 int_rate 분포
application_type - Joint Application(공동 대출), Individual(개인 대출)
Joint Application(공동 대출)일 경우, fully paid 일 확률이 높을까?
오히려 Joint Application 이 정상상환 비율이 적음
- 공동 신청 자체가
위험군일수도 있음 - 공동 신청은 한 명만으로는 대출이 안 되거나, 신용이 낮은 사람이 포함되어 있을 가능성 높음
- 두 명의 소득/신용을 합쳐서 겨우 조건을 맞춘 경우라면, 오히려 위험도가 높은 “구제성” 대출일 수도 있음
- 공동 신청은 대출 한도가 더 크기 때문에 연체 위험이 오히려 클 수도 이음
실제로 공동 대출일수록 대출 금액이 크다.
application_type 별 대출 목적 차이
- 두 신청 유형 모두 부채통합/신용카드 상환이 대다수 목적
- 공동신청은 주택개선, 의료 등 가족 중심의 고액 목적 비율이 조금 더 높음
- 개인신청은 신용카드, 자동차, 휴가 등 비중이 더 높음
issue_d(대출 승인월) - 2007/6 ~ 2020/9
Initial List Status - W(Whole, 단일 투자자), F(Factional, 여러 투자자)
- 두 그룹 모두 정상상환이 약 80% 정도
- 대출의 투자 방식과 상관없이 연체/부실 비율, 정상상환 비율이 거의 똑같음
addr_state
- 50개 주 + WA → 총 51개
- 서로 인접한 저부실/고부실 주들이 군집(cluster) 형태로 분포함
dti(debt-to-income ratio)
- outlier 포함
- outlier 제외
annual_inc
- outlier 포함
- outlier 제외
funded_amnt, funded_amnt_inv
funded_amnt, funded_amnt_inv 의 값 차이가 나는 이유가 뭘까..
파생변수
소득 대비 대출금 비율
채무자의 상환 부담을 나타내는 지표
loan_amnt / annual_incdti 와 다른 점
- dti에는 타 부채까지 포함되므로 신규 대출에 한정한 부채비율을 별도로 보기위함
월 납입액 대비 소득비율
매달 갚아야 할 금액 기준으로 부담을 측정
installment / (annual_inc/12)
- 월 소득 중 몇 %가 해당 대출 상환에 쓰이는지 파악, 이 비율이 높으면 생활비 대비 상환 부담이 커서 부도 확률을 높일 수도 있음
신용기간(신용이력 길이)
채무자의 신용거래 시작 시점부터 대출 신청 시점까지의 기간(연수)
earliest_cr_line과 issue_d을 이용해 년수 차이를 계산
- 신용기간이 길수록 오랜 신용기록을 보유한 것으로, 일반적으로 신용도가 안정적일 가능성이 높음
연체 이력 플래그
과거에 연체나 불량 기록이 있는지를 나타내는 이진 변수
delinq_2yrs > 0
- 과거에 연체가 한 번이라도 있었다면 부도 확률이 높아질 수 있으므로 모델에 중요한 신호가 됨
최근 신용조회 빈도 플래그
최근 6개월 내 신용조회 수가 높으면 여러 곳에서 대출을 시도한 흔적으로 볼 수 잇음
inq_last_6mths
- 신용공급 접근 난이도를 보여주는 지표로 간접적인 위험 신호
대출 등급/금리 파생
등급별로 분석도 해야함..