05단계 · 3유형 — 검정과 해석
로지스틱 회귀와 오즈비
0/1 을 예측하는 회귀를 세우고 계수에 `exp` 를 씌워 오즈비로 읽는다.
먼저 알아볼게요
코드로 이동 ↓종속변수가 0/1 이면 선형 회귀가 아니라 로지스틱 회귀를 쓴다. from statsmodels.formula.api import logit 이고 쓰는 법은 ols 와 같다.
.fit() 은 기본적으로 최적화 과정을 화면에 찍는다. 출력을 깔끔하게 하려면 .fit(disp=0) 을 준다.
오즈는 p / (1 - p) 다 — 일어날 확률을 안 일어날 확률로 나눈 값이지 확률 자체가 아니다. 로지스틱의 계수는 이 오즈에 로그를 씌운 값의 변화량이라 그대로는 읽기 어렵고, np.exp(계수) 를 씌우면 오즈비(odds ratio) 가 되어 "배수" 로 읽힌다.
오즈비가 0.9461 이면 "모형의 다른 변수를 고정했을 때 그 변수가 1 늘면 이탈의 오즈가 0.9461 배" 라는 뜻이다. 확률이 0.9461 배가 된다는 뜻이 아니다 — 오즈비와 확률비는 다른 값이고, 확률이 낮을 때만 둘이 비슷해진다. 1 보다 크면 오즈 증가, 작으면 감소다.
계수의 유의성은 ols 와 같이 .pvalues 로 본다. 오즈비가 1 에서 멀어 보여도 p-value 가 크면 "그 방향이라고 말할 근거가 부족" 한 것이다.
import numpy as np
import pandas as pd
from statsmodels.formula.api import logit
df = pd.read_csv("churn_train.csv")
m = logit("이탈 ~ 가입개월", data=df).fit(disp=0)
print(m.params.round(4))
print(np.exp(m.params).round(4))오즈는 p / (1 - p). 로지스틱 계수에 np.exp 를 씌우면 오즈비이고, 확률의 배수가 아니다.
churn_train.csv 에 기본요금제 열(요금제가 basic 이면 1)을 만들고 이탈 ~ 가입개월 + 부가서비스 + 기본요금제 로 로지스틱 회귀를 적합하세요. ① 계수(소수 4) ② 오즈비(소수 4) ③ 가입개월 의 p-value 를 소수 8자리 문자열로 ④ 오즈비가 1 보다 큰 항의 개수를 출력하세요.
제공 파일 1개 · 내용 보기
파일은 준비되어 있어요. 코드에서 이름으로 불러오세요.
고객ID,성별,요금제,가입개월,월요금,부가서비스,이탈 C1001,남,standard,22,44300,3,1 C1002,여,premium,1,62000,1,1 C1003,남,premium,45,64000,0,1 C1004,남,basic,10,32200,1,1 C1005,남,premium,5,69500,0,1 C1006,여,basic,56,29500,0,0 C1007,여,premium,48,62800,2,0 C1008,여,standard,5,56700,1,1 C1009,여,basic,49,30600,3,0 C1010,여,premium,52,74800,2,1 C1011,여,basic,19,32800,2,1 C1012,여,standard,18,42200,3,0 C1013,남,standard,37,47900,1,0 C1014,남,basic,1,30500,4,1 C1015,여,standard,52,43200,0,0 C1016,남,premium,31,75300,3,1 C1017,여,basic,22,29100,1,1 C1018,남,standard,39,48400,1,1 C1019,남,premium,35,82500,0,0 C1020,남,standard,19,42600,3,0 C1021,남,basic,22,27300,4,1 C1022,여,basic,38,27800,1,1 C1023,남,standard,5,54200,0,1 C1024,여,premium,36,77200,3,0 C1025,여,standard,14,46500,1,1 C1026,남,premium,15,64500,2,1 C1027,남,basic,45,28100,4,0 C1028,남,standard,49,40600,1,1 C1029,남,standard,59,50800,4,0 C1030,여,basic,16,27800,3,1 C1031,남,basic,36,24900,0,1 C1032,남,basic,17,30200,4,0 C1033,여,basic,36,29900,3,1 C1034,남,basic,36,23800,2,0 C1035,여,standard,9,50200,1,1 C1036,여,premium,45,60800,2,0 C1037,여,standard,37,42900,3,0 C1038,여,basic,37,24800,2,0 C1039,남,standard,42,46300,0,0 C1040,여,basic,34,28400,2,1 C1041,남,premium,11,84200,3,1 C1042,남,standard,9,57000,4,1 C1043,여,standard,11,43500,3,1 C1044,여,basic,42,29200,2,1 C1045,여,premium,10,66800,4,1 C1046,남,premium,56,62700,2,0 C1047,남,standard,19,44100,3,1 C1048,여,basic,14,33700,2,0 C1049,여,basic,44,28800,0,1 C1050,여,premium,32,69800,0,1 C1051,남,basic,52,24100,3,0 C1052,남,standard,48,35900,1,0 C1053,여,basic,41,26300,0,0 C1054,여,basic,36,25500,1,1 C1055,여,basic,7,29000,3,0 C1056,여,basic,34,32700,3,1 C1057,남,standard,42,48900,4,1 C1058,여,basic,32,25800,1,1 C1059,남,standard,56,50100,3,0 C1060,여,standard,28,41500,1,0 C1061,여,premium,47,73900,0,0 C1062,남,premium,4,53900,3,1 C1063,여,premium,4,72600,0,1 C1064,여,basic,42,29100,4,1 C1065,남,basic,59,29800,4,0 C1066,남,premium,28,75100,1,0 C1067,남,basic,27,27200,0,0 C1068,남,basic,60,30100,2,1 C1069,여,premium,4,67400,1,1 C1070,여,basic,4,34100,0,1 C1071,남,standard,42,40800,4,0 C1072,여,premium,53,72900,4,0 C1073,남,standard,36,37500,1,0 C1074,남,standard,44,49800,0,0 C1075,남,basic,5,29700,3,1 C1076,남,premium,1,75600,3,1 C1077,남,standard,44,40600,1,1 C1078,여,basic,7,23800,1,0 C1079,여,premium,47,66100,4,1 C1080,남,standard,32,41500,1,0 C1081,남,premium,37,78500,4,1 C1082,남,premium,52,68500,1,0 C1083,남,premium,51,61900,3,1 C1084,남,basic,38,30000,2,1 C1085,남,basic,12,30900,0,0 C1086,남,premium,25,74900,1,1 C1087,여,standard,40,47300,1,0 C1088,남,standard,6,41600,1,1 C1089,남,premium,57,64200,4,0 C1090,여,basic,42,28700,0,1 C1091,여,standard,27,37300,0,1 C1092,남,basic,27,25900,2,1 C1093,여,basic,41,27900,0,0 C1094,남,standard,60,44200,0,0 C1095,남,premium,12,67100,2,1 C1096,남,standard,46,45700,1,0 C1097,여,standard,23,48800,1,1 C1098,남,premium,57,75300,4,0 C1099,남,premium,13,53900,0,0 C1100,여,premium,29,61600,3,1 C1101,남,standard,2,47300,3,1 C1102,남,standard,10,55900,1,1 C1103,남,premium,3,71800,1,1 C1104,남,premium,53,70000,1,0 C1105,남,premium,32,71300,2,1 C1106,남,premium,9,66000,0,1 C1107,여,premium,30,66100,2,1 C1108,남,standard,43,53500,3,0 C1109,남,basic,26,29400,4,0 C1110,여,standard,21,48500,4,0 C1111,남,standard,51,42400,0,1 C1112,남,premium,21,74100,4,0 C1113,남,premium,56,56600,0,0 C1114,남,standard,46,45300,4,0 C1115,여,standard,48,42300,4,0 C1116,여,basic,35,24700,4,1 C1117,여,premium,22,42700,1,0 C1118,남,standard,9,39900,4,1 C1119,남,premium,36,73100,1,0 C1120,여,premium,38,73800,0,1 C1121,여,standard,13,49300,4,0 C1122,남,basic,48,30300,0,0 C1123,남,standard,16,43800,0,1 C1124,남,premium,15,74000,2,1 C1125,남,basic,25,30200,0,1 C1126,남,basic,44,33000,2,1 C1127,남,basic,56,21800,3,0 C1128,남,standard,48,38600,1,0 C1129,여,premium,15,68200,0,1 C1130,여,basic,28,27400,3,0 C1131,남,standard,54,40800,4,0 C1132,남,basic,45,26900,3,0 C1133,여,premium,21,66200,0,1 C1134,여,premium,39,58900,0,0 C1135,남,basic,43,29000,3,0 C1136,남,standard,9,48600,4,1 C1137,남,basic,28,30300,1,0 C1138,남,premium,30,78600,4,0 C1139,남,basic,18,23900,3,0 C1140,남,standard,46,40900,2,1 C1141,남,premium,6,70300,0,1 C1142,남,basic,46,29100,1,0 C1143,남,standard,18,41600,3,0 C1144,여,basic,53,27800,3,1 C1145,여,basic,7,25400,0,1 C1146,남,basic,27,28800,0,1 C1147,여,premium,51,64300,4,0 C1148,남,premium,25,53100,2,0 C1149,남,standard,54,43800,1,1 C1150,남,premium,40,57700,0,1 C1151,여,standard,24,42900,3,1 C1152,남,premium,21,63300,1,1 C1153,여,standard,44,34800,3,0 C1154,여,basic,29,27400,2,1 C1155,여,premium,38,61500,1,0 C1156,여,premium,48,62600,4,0 C1157,남,basic,27,26900,1,1 C1158,남,standard,20,38000,4,0 C1159,여,premium,22,63700,2,1 C1160,여,standard,20,42800,1,0
들여쓰기: Tab · 편집기에서 나가기: Esc 다음 Tab
실행 결과
실행하면 코드가 출력한 내용이 여기에 나타나요.
막혔을 때
풀이와 비교하기
내 코드와 한 줄씩 비교해 보세요. 풀이를 보는 것만으로 완료되지는 않아요.
import numpy as np
import pandas as pd
from statsmodels.formula.api import logit
train = pd.read_csv("churn_train.csv")
train["기본요금제"] = (train["요금제"] == "basic").astype(int)
model = logit("이탈 ~ 가입개월 + 부가서비스 + 기본요금제", data=train).fit(disp=0)
print(model.params.round(4))
odds = np.exp(model.params)
print(odds.round(4))
print(f"{float(model.pvalues['가입개월']):.8f}")
print(int((odds > 1).sum()))