6.1 로지스틱 회귀분석#
로지스틱(Logistic) 회귀분석은 회귀분석이라는 명칭과 달리 회귀분석 문제와 분류문제 모두에 사용할 수 있다. 로지스틱 회귀분석 모형에서는 종속변수가 이항분포를 따르고 그 모수 \(\mu\)가 독립변수 \(x\)에 의존한다고 가정한다.
위 식에서 보듯이 로지스틱 함수는 \(y\)의 값이 특정한 구간내의 값(\(0 \sim N\))만 가질 수 있기 때문에 종속변수가 이러한 특성을 가진 경우에 회귀분석 방법으로 쓸 수 있다.
또는 이항 분포의 특별한 경우(\(N=1\))로 \(y\)가 베르누이 확률분포인 경우도 있을 수 있다. 여기에서는 베르누이 확률분포를 따르는 로지스틱 회귀분석만 고려하기로 한다.
종속변수 \(y\)가 0또는 1인 분류 예측 문제를 풀 때는 \(x\) 값을 이용하여 \(\mu(x)\)를 예측한 후 다음 기준에 따라 \(\hat{y}\)값을 출력한다.
회귀분석을 할 때는 \(\hat{y}\)으로 \(y=1\)이 될 확률값 \(\mu(x)\)를 직접 사용한다.
시그모이드함수#
로지스틱 회귀모형에서는 베르누이 확률분포의 모수 \(\mu\)가 \(x\)의 함수라고 가정한다. \(\mu(x)\)는 \(x\)에 대한 함수를 0부터 1사이의 값만 나올 수 있도록 **시그모이드함수(sigmoid function)**라는 함수를 사용하여 변형한 것을 사용한다.
시그모이드함수는 종속변수의 모든 실수 값에 대해
유한한 구간 \((a,b)\) 사이의 한정된(bounded) 값을 가지고 $\( a < f(x) < b \)$
항상 양의 기울기를 가지는 단조증가하는 $\( a > b \; \rightarrow \; f(a) > f(b) \)$
함수의 집합을 말한다. 실제로는 다음과 같은 함수들이 주로 사용된다.
로지스틱(Logistic)함수
하이퍼볼릭탄젠트(Hyperbolic tangent)함수
오차(Error)함수
하이퍼볼릭탄젠트함수는 로지스틱함수를 위아래 방향으로 2배 늘리고 좌우 방향으로 1/2로 축소한 것과 같다.
xx = np.linspace(-5, 5, 1000)
plt.plot(xx, 1/(1+np.exp(-xx)), 'r-', label="로지스틱함수")
plt.plot(xx, sp.special.erf(0.5*np.sqrt(np.pi)*xx), 'g:', label="오차함수")
plt.plot(xx, np.tanh(xx), 'b--', label="하이퍼볼릭탄젠트함수")
plt.ylim([-1.1, 1.1])
plt.legend(loc=2)
plt.xlabel("x")
plt.show()

로지스틱함수#
로지스틱함수는 음의 무한대부터 양의 무한대까지의 실수값을 0부터 1사이의 실수값으로 1 대 1 대응시키는 시그모이드함수다. 보통 시그모이드함수라고 하면 로지스틱함수를 가리킨다. 로지스틱함수는 다음 과정을 통해 정의되었다.
베르누이 시도에서 1이 나올 확률 \(\mu\)와 0이 나올 확률 \(1-\mu\)의 비율(ratio)을 승산비(odds ratio)라고 한다.
0부터 1사이의 값만 가지는 \(\mu\)를 승산비로 변환하면 0부터 양의 무한대까지의 값을 가질 수 있다.
승산비를 로그 변환한 것이 로지트함수(Logit function)다.
로지트함수의 값은 로그 변환에 의해 음의 무한대(\(-\infty\))부터 양의 무한대(\(\infty\))까지의 값을 가질 수 있다.
로지스틱함수(Logistic function)는 로지트함수의 역함수이다. 즉 음의 무한대(\(-\infty\))부터 양의 무한대(\(\infty\))까지의 값을 가지는 입력변수를 0부터 1사의 값을 가지는 출력변수로 변환한 것이다.
선형 판별함수#
로지스틱함수 \(\sigma(z)\)를 사용하는 경우에는 \(z\)값과 \(\mu\)값은 다음과 같은 관계가 있다.
\(z = 0\)일 때 \(\mu = 0.5\)
\(z > 0\)일 때 \(\mu > 0.5 \; \rightarrow \hat{y} = 1\)
\(z < 0\)일 때 \(\mu < 0.5 \; \rightarrow \hat{y} = 0\)
즉 \(z\)가 분류 모형의 판별함수(decision function)의 역할을 한다. 로지스틱 회귀분석에서는 판별함수 수식으로 선형함수를 사용한다.
따라서 판별 경계면도 선형이 된다.
로지스틱 회귀분석 모형의 모수 추정#
로지스틱 회귀분석 모형의 모수 \(w\)는 최대가능도(Maximum Likelihood Estimation, MLE)방법으로 추정할 수 있다.
우선 베르누이분포의 확률밀도함수는 다음과 같다.
\(\mu\)는 \(w^Tx\)에 로지스틱함수를 적용한 값이다.
이 식을 대입하면 조건부 확률은 다음과 같다.
데이터 표본이 \(\{ x_i, y_i \}_{1:N}\)로 여러 개 있는 경우 전체 데이터의 로그가능도 \({LL}\)를 구하면 다음과 같다.
베르누이 확률분포의 정의에서
가 된다.
로그가능도를 최대화하는 \(w\) 값을 구하기 위해 모수로 미분한다.
\(LL\)을 \(\mu\)로 미분하면
\(\mu\)를 \(w\)로 미분하면
두 식을 곱하면 그레디언트 벡터의 수식을 구할 수 있다.
그레디언트 벡터가 영벡터가 되는 모수의 값이 로그가능도를 최대화하는 값이다. 하지만 그레디언트 벡터 수식이 \(w\)에 대한 비선형 함수이므로 선형 모형과 같이 간단하게 그레디언트가 0이 되는 모수 \(w\) 값에 대한 수식을 구할 수 없으며 수치적인 최적화 방법(numerical optimization)을 통해 반복적으로 최적 모수 \(w\)의 값을 구해야 한다.
수치적 최적화#
로그가능도함수 \(LL\)을 최대화하는 것은 다음 목적함수를 최소화하는 것과 같다.
최대경사도(Steepest Gradient Descent)방법을 사용하자.
그레디언트 벡터는 $\( g_k = \dfrac{d}{dw}(-LL) \)$
이고, 이 방향으로 스텝사이즈 \(\eta_k\)만큼 이동한다.
StatsModels 패키지의 로지스틱 회귀#
다음과 같은 1차원 독립변수를 가지는 분류문제를 풀어보자.
from sklearn.datasets import make_classification
X0, y = make_classification(n_features=1, n_redundant=0, n_informative=1,
n_clusters_per_class=1, random_state=4)
plt.scatter(X0, y, c=y, s=100, edgecolor="k", linewidth=2)
sns.distplot(X0[y == 0, :], label="y = 0", hist=False)
sns.distplot(X0[y == 1, :], label="y = 1", hist=False)
plt.ylim(-0.2, 1.2)
plt.show()

StatsModels 패키지는 베르누이 분포를 따르는 로지스틱 회귀 모형 Logit
를 제공한다. 사용방법은 OLS
클래스 사용법과 동일하다. 종속변수와 독립변수 데이터를 넣어 모형을 만들고 fit
메서드로 학습을 시킨다. fit
메서드의 disp=0
인수는 최적화 과정에서 문자열 메세지를 나타내지 않는 역할을 한다.
X = sm.add_constant(X0)
logit_mod = sm.Logit(y, X)
logit_res = logit_mod.fit(disp=0)
print(logit_res.summary())
Logit Regression Results
==============================================================================
Dep. Variable: y No. Observations: 100
Model: Logit Df Residuals: 98
Method: MLE Df Model: 1
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.7679
Time: 10:01:05 Log-Likelihood: -16.084
converged: True LL-Null: -69.295
Covariance Type: nonrobust LLR p-value: 5.963e-25
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
const 0.2515 0.477 0.527 0.598 -0.683 1.186
x1 4.2382 0.902 4.699 0.000 2.470 6.006
==============================================================================
결과 객체에서 summary
메서드를 사용하여 리포트를 출력할 수 있다. 결과 리포트에서 판별함수의 수식이 다음과 같다는 것을 알 수 있다.
따라서 \(z\)값의 부호를 나누는 기준값은 \(4.2382x + 0.2515 = 0.5\)가 되는 \(x\)값 즉, \((0.5-0.2515)/4.2382\)다.
predict
메서드를 사용하면 \(\mu(x)\)값을 출력한다.
유의확률을 감안하면 상수항의 값은 0과 마찬가지이므로 \(\mu(x)\)가 다음과 같다고 볼 수도 있다.
이렇게 생각하면 \(z\)값의 부호를 나누는 기준값은 실질적으로는 \(0.5/4.2382=0.118\)이다.
xx = np.linspace(-3, 3, 100)
mu = logit_res.predict(sm.add_constant(xx))
plt.plot(xx, mu, lw=3)
plt.scatter(X0, y, c=y, s=100, edgecolor="k", lw=2)
plt.scatter(X0, logit_res.predict(X), label=r"$\hat{y}$", marker='s', c=y,
s=100, edgecolor="k", lw=1)
plt.xlim(-3, 3)
plt.xlabel("x")
plt.ylabel(r"$\mu$")
plt.title(r"$\hat{y} = \mu(x)$")
plt.legend()
plt.show()

판별함수#
Logit
모형의 결과 객체에는 fittedvalues
라는 속성으로 판별함수 \(z=w^Tx\) 값이 들어가 있다. 이 값을 이용하여 분류문제를 풀 수도 있다.
plt.scatter(X0, y, c=y, s=100, edgecolor="k", lw=2, label="데이터")
plt.plot(X0, logit_res.fittedvalues * 0.1, label="판별함수값")
plt.legend()
plt.show()

로지스틱 회귀 성능 측정#
로지스틱 회귀 성능은 맥파든 의사결정계수(McFadden pseudo R square)값으로 측정한다.
\(G^2\)는 이탈도(deviance)라고 하는 양으로 다음과 같이 정의된다.
여기에서 \(\hat{y}\)는 \(y=1\)일 확률 \(\mu\)를 뜻한다.
이탈도는 모형이 100% 정확한 경우에는 0이 되고 모형의 성능이 나빠질수록 값이 커진다.
또한 이탈도는 로그 가능도에 음수를 취한 값과 같다.
\(G^2\)는 현재 이탈도이고 \(G^2_0\)는 귀무모형(null model)으로 측정한 이탈도다.
귀무모형이란 모든 \(x\)가 \(y\)를 예측하는데 전혀 영향을 미치지 않는 모형을 말한다. 즉, 무조건부 확률 \(p(y)\)에 따라 \(x\)에 상관없이 동일하게 \(y\)를 예측하는 모형을 말한다. 결국 우리가 만들 수 있는 가장 성능이 나쁜 모형이 된다.
따라서 맥파든 의사결정계수는 가장 성능이 좋을 때는 1이 되고 가장 성능이 나쁠 때는 0이 된다.
scikit-learn 패키지의 metric 서브패키지에는 로그 손실을 계산하는 log_loss
함수가 있다. normalize=False
로 놓으면 이탈도와 같은 값을 구한다
위 예제에서 최적 모형의 로그 손실은 약 16.08로 계산된다.
from sklearn.metrics import log_loss
y_hat = logit_res.predict(X)
log_loss(y, y_hat, normalize=False)
16.084355200413036
귀무 모형의 모수값을 구하면 0.51이고 이 값으로 로그 손실을 계산하면 약 69이다.
mu_null = np.sum(y) / len(y)
mu_null
0.51
y_null = np.ones_like(y) * mu_null
log_loss(y, y_null, normalize=False)
69.29471672244784
두 값을 이용하여 맥파든 의사 결정계수 값을 계산할 수 있다.
1 - (log_loss(y, y_hat) / log_loss(y, y_null))
0.7678848264170398
Scikit-Learn 패키지의 로지스틱 회귀#
Scikit-Learn 패키지는 로지스틱 회귀 모형 LogisticRegression
를 제공한다.
from sklearn.linear_model import LogisticRegression
model_sk = LogisticRegression().fit(X0, y)
xx = np.linspace(-3, 3, 100)
mu = 1.0/(1 + np.exp(-model_sk.coef_[0][0]*xx - model_sk.intercept_[0]))
plt.plot(xx, mu)
plt.scatter(X0, y, c=y, s=100, edgecolor="k", lw=2)
plt.scatter(X0, model_sk.predict(X0), label=r"$\hat{y}$", marker='s', c=y,
s=100, edgecolor="k", lw=1, alpha=0.5)
plt.xlim(-3, 3)
plt.xlabel("x")
plt.ylabel(r"$\mu$")
plt.title(r"$\hat{y}$ = sign $\mu(x)$")
plt.legend()
plt.show()

연습 문제 1#
붓꽃 분류문제에서 클래스가 세토사와 베르시칼라 데이터만 사용하고 (setosa=0, versicolor=1) 독립변수로는 꽃받침 길이(Sepal Length)와 상수항만 사용하여 StatsModels 패키지의 로지스틱 회귀모형으로 결과를 예측하고 보고서를 출력한다. 이 보고서에서 어떤 값이 세토사와 베르시칼라를 구분하는 기준값(threshold)으로 사용되고 있는가?
위 결과를 분류결과표(confusion matrix)와 분류결과보고서(classification report)로 나타내라.
이 모형에 대해 ROC커브를 그리고 AUC를 구한다. 이 때 Scikit-Learn의
LogisticRegression
을 사용하지 않고 위에서 StatsModels로 구한 모형을 사용한다.
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data
y = iris.target
dfX = pd.DataFrame(X, columns=iris.feature_names)
dfy = pd.DataFrame(y, columns=["species"])
df = pd.concat([dfX, dfy], axis=1)
df = df[["sepal length (cm)", "species"]]
df = df[df.species.isin([0, 1])]
df = df.rename(columns={"sepal length (cm)": "sepal_length" })
import statsmodels.api as sm
model = sm.Logit.from_formula("species ~ sepal_length", data=df)
result = model.fit()
print(result.summary())
Optimization terminated successfully.
Current function value: 0.321056
Iterations 8
Logit Regression Results
==============================================================================
Dep. Variable: species No. Observations: 100
Model: Logit Df Residuals: 98
Method: MLE Df Model: 1
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.5368
Time: 10:01:22 Log-Likelihood: -32.106
converged: True LL-Null: -69.315
Covariance Type: nonrobust LLR p-value: 6.320e-18
================================================================================
coef std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
Intercept -27.8315 5.434 -5.122 0.000 -38.481 -17.182
sepal_length 5.1403 1.007 5.107 0.000 3.168 7.113
================================================================================
# 기준값
(0.5 + 27.8315) / 5.1403
5.511643289302181
y_pred = result.predict(df.sepal_length) >= 0.5
from sklearn.metrics import confusion_matrix
confusion_matrix(df.species, y_pred)
array([[45, 5],
[ 6, 44]])
from sklearn.metrics import classification_report
print(classification_report(df.species, y_pred))
precision recall f1-score support
0 0.88 0.90 0.89 50
1 0.90 0.88 0.89 50
accuracy 0.89 100
macro avg 0.89 0.89 0.89 100
weighted avg 0.89 0.89 0.89 100
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(df.species, result.predict(df.sepal_length))
plt.plot(fpr, tpr)
plt.show()

from sklearn.metrics import auc
auc(fpr, tpr)
0.9326
로지스틱 회귀를 사용한 이진 분류의 예#
다음 데이터는 미국 의대생의 입학관련 데이터이다. 데이터의 의미는 다음과 같다.
Acceptance
: 0이면 불합격, 1이면 합격BCPM
: Bio/Chem/Physics/Math 과목의 학점 평균GPA
: 전체과목 학점 평균VR
: MCAT Verbal reasoning 과목 점수PS
: MCAT Physical sciences 과목 점수WS
: MCAT Writing sample 과목 점수BS
: MCAT Biological sciences 과목 점수MCAT
: MCAT 촘점Apps
: 의대 지원 횟수
data_med = sm.datasets.get_rdataset("MedGPA", package="Stat2Data")
df_med = data_med.data
df_med.tail()
Accept | Acceptance | Sex | BCPM | GPA | VR | PS | WS | BS | MCAT | Apps | |
---|---|---|---|---|---|---|---|---|---|---|---|
50 | D | 0 | M | 2.41 | 2.72 | 8 | 8 | 8.0 | 8 | 32 | 7 |
51 | D | 0 | M | 3.51 | 3.56 | 11 | 8 | 6.0 | 9 | 34 | 6 |
52 | A | 1 | F | 3.43 | 3.48 | 7 | 10 | 7.0 | 10 | 34 | 14 |
53 | D | 0 | M | 2.61 | 2.80 | 7 | 5 | NaN | 6 | 18 | 6 |
54 | D | 0 | M | 3.36 | 3.44 | 11 | 11 | 8.0 | 9 | 39 | 1 |
일단 학점(GPA)과 합격여부의 관계를 살펴보자.
sns.stripplot(x="GPA", y="Acceptance", data=df_med,
jitter=True, orient='h', order=[1, 0])
plt.grid(True)
plt.show()

로지스틱 회귀분석을 실시한다. MCAT = VR + PS + WS + BS
이므로 이 MCAT
은 독립 변수에서 제외해야 한다.
model_med = sm.Logit.from_formula("Acceptance ~ Sex + BCPM + GPA + VR + PS + WS + BS + Apps", df_med)
result_med = model_med.fit()
print(result_med.summary())
Optimization terminated successfully.
Current function value: 0.280736
Iterations 9
Logit Regression Results
==============================================================================
Dep. Variable: Acceptance No. Observations: 54
Model: Logit Df Residuals: 45
Method: MLE Df Model: 8
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.5913
Time: 10:01:33 Log-Likelihood: -15.160
converged: True LL-Null: -37.096
Covariance Type: nonrobust LLR p-value: 6.014e-07
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
Intercept -46.6414 15.600 -2.990 0.003 -77.216 -16.067
Sex[T.M] -2.2835 1.429 -1.597 0.110 -5.085 0.518
BCPM -6.1633 6.963 -0.885 0.376 -19.811 7.484
GPA 12.3973 8.611 1.440 0.150 -4.479 29.274
VR 0.0790 0.311 0.254 0.799 -0.530 0.688
PS 1.1673 0.539 2.164 0.030 0.110 2.225
WS -0.7784 0.396 -1.968 0.049 -1.554 -0.003
BS 1.9184 0.682 2.814 0.005 0.582 3.255
Apps 0.0512 0.147 0.348 0.728 -0.237 0.340
==============================================================================
예측 결과와 실제 결과를 비교하면 다음과 같다.
df_med["Prediction"] = result_med.predict(df_med)
sns.boxplot(x="Acceptance", y="Prediction", data=df_med)
plt.show()

위 분석 결과를 토대로 유의하지 않은 변수들을 제외하고 PS와 BS 점수만을 이용하여 다시 회귀분석하면 다음과 같다.
model_med = sm.Logit.from_formula("Acceptance ~ PS + BS", df_med)
result_med = model_med.fit()
print(result_med.summary())
Optimization terminated successfully.
Current function value: 0.460609
Iterations 7
Logit Regression Results
==============================================================================
Dep. Variable: Acceptance No. Observations: 55
Model: Logit Df Residuals: 52
Method: MLE Df Model: 2
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.3315
Time: 10:01:36 Log-Likelihood: -25.333
converged: True LL-Null: -37.896
Covariance Type: nonrobust LLR p-value: 3.503e-06
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
Intercept -15.5427 4.684 -3.318 0.001 -24.723 -6.362
PS 0.4798 0.316 1.518 0.129 -0.140 1.099
BS 1.1464 0.387 2.959 0.003 0.387 1.906
==============================================================================
위 결과를 바탕으로 다음 점수가 \(15.5427+0.5\)보다 크면 합격이라고 예측할 수 있다.
연습 문제 2#
붓꽃 분류문제에서 클래스가 베르시칼라(versicolor)와 버지니카(virginica) 데이터만 사용하여(versicolor=1, virginica=2) 로지스틱 회귀모형으로 결과를 예측하고 보고서를 출력한다. 독립변수는 모두 사용한다. 이 보고서에서 버지니카와 베르시칼라를 구분하는 경계면의 방정식을 찾아라.
위 결과를 분류결과표와 분류결과보고서로 나타내라.
이 모형에 대해 ROC커브를 그리고 AUC를 구하라. 이 때 Scikit-Learn의
LogisticRegression
을 사용하지 않고 위에서 StatsModels로 구한 모형을 사용한다.
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data
y = iris.target
dfX = pd.DataFrame(X, columns=iris.feature_names)
dfy = pd.DataFrame(y, columns=["species"])
df = pd.concat([dfX, dfy], axis=1)
df = df[df.species.isin([1, 2])]
df["species"] -= 1
df = df.rename(
columns={
"sepal length (cm)": "sepal_length",
"sepal width (cm)": "sepal_width",
"petal length (cm)": "petal_length",
"petal width (cm)": "petal_width",
}
)
import statsmodels.api as sm
model = sm.Logit.from_formula(
"species ~ sepal_length + sepal_width + petal_length + petal_width",
data=df)
result = model.fit()
print(result.summary())
Optimization terminated successfully.
Current function value: 0.059493
Iterations 12
Logit Regression Results
==============================================================================
Dep. Variable: species No. Observations: 100
Model: Logit Df Residuals: 95
Method: MLE Df Model: 4
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.9142
Time: 10:01:37 Log-Likelihood: -5.9493
converged: True LL-Null: -69.315
Covariance Type: nonrobust LLR p-value: 1.947e-26
================================================================================
coef std err z P>|z| [0.025 0.975]
--------------------------------------------------------------------------------
Intercept -42.6378 25.708 -1.659 0.097 -93.024 7.748
sepal_length -2.4652 2.394 -1.030 0.303 -7.158 2.228
sepal_width -6.6809 4.480 -1.491 0.136 -15.461 2.099
petal_length 9.4294 4.737 1.990 0.047 0.145 18.714
petal_width 18.2861 9.743 1.877 0.061 -0.809 37.381
================================================================================
Possibly complete quasi-separation: A fraction 0.60 of observations can be
perfectly predicted. This might indicate that there is complete
quasi-separation. In this case some parameters will not be identified.
y_pred = result.predict(df) >= 0.5
from sklearn.metrics import confusion_matrix
confusion_matrix(df.species, y_pred)
array([[49, 1],
[ 1, 49]])
from sklearn.metrics import classification_report
print(classification_report(df.species, y_pred))
precision recall f1-score support
0 0.98 0.98 0.98 50
1 0.98 0.98 0.98 50
accuracy 0.98 100
macro avg 0.98 0.98 0.98 100
weighted avg 0.98 0.98 0.98 100
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(df.species, result.predict(df))
plt.plot(fpr, tpr)
plt.show()

from sklearn.metrics import auc
auc(fpr, tpr)
0.9972000000000001
로지스틱 회귀를 사용한 회귀분석#
로지스틱 회귀는 분류문제뿐만 아니라 종속변수 \(y\)가 0부터 1까지 막혀있는 회귀분석 문제에도 사용할 수 있다. 이때는 다음처럼 \(\mu\) 값을 종속변수 y의 예측값으로 사용한다.
만약 실제 y의 범위가 0부터 1이 아니면 스케일링을 통해 바꿔야 한다.
예제#
다음 데이터는 1974년도에 “여성은 가정을 보살피고 국가를 운영하는 일은 남자에게 맡겨두어야 한다.”라는 주장에 대한 찬성, 반대 입장을 조사한 결과이다. 각 열은 다음을 뜻한다.
education
: 교육 기간sex
: 성별agree
: 찬성 인원disagree
: 반대 인원ratio
: 찬성 비율
data_wrole = sm.datasets.get_rdataset("womensrole", package="HSAUR")
df_wrole = data_wrole.data
df_wrole["ratio"] = df_wrole.agree / (df_wrole.agree + df_wrole.disagree)
df_wrole.tail()
education | sex | agree | disagree | ratio | |
---|---|---|---|---|---|
37 | 16 | Female | 13 | 115 | 0.101562 |
38 | 17 | Female | 3 | 28 | 0.096774 |
39 | 18 | Female | 0 | 21 | 0.000000 |
40 | 19 | Female | 1 | 2 | 0.333333 |
41 | 20 | Female | 2 | 4 | 0.333333 |
교육을 많이 받은 사람일수록 찬성 비율이 감소하는 것을 볼 수 있다.
sns.scatterplot(x="education", y="ratio", style="sex", data=df_wrole)
plt.grid(True)
plt.show()

분석 결과는 다음과 같다.
model_wrole = sm.Logit.from_formula("ratio ~ education + sex", df_wrole)
result_wrole = model_wrole.fit()
print(result_wrole.summary())
Optimization terminated successfully.
Current function value: 0.448292
Iterations 6
Logit Regression Results
==============================================================================
Dep. Variable: ratio No. Observations: 41
Model: Logit Df Residuals: 38
Method: MLE Df Model: 2
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.3435
Time: 10:01:57 Log-Likelihood: -18.380
converged: True LL-Null: -27.997
Covariance Type: nonrobust LLR p-value: 6.660e-05
===============================================================================
coef std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------
Intercept 2.0442 0.889 2.299 0.022 0.302 3.787
sex[T.Male] -0.1968 0.736 -0.267 0.789 -1.640 1.247
education -0.2127 0.071 -2.987 0.003 -0.352 -0.073
===============================================================================
성별은 유의하지 않다는 것을 알게되었으므로 성별을 제외하고 다시 모형을 구한다.
model_wrole2 = sm.Logit.from_formula("ratio ~ education", df_wrole)
result_wrole2 = model_wrole2.fit()
print(result_wrole2.summary())
Optimization terminated successfully.
Current function value: 0.449186
Iterations 6
Logit Regression Results
==============================================================================
Dep. Variable: ratio No. Observations: 41
Model: Logit Df Residuals: 39
Method: MLE Df Model: 1
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.3422
Time: 10:01:58 Log-Likelihood: -18.417
converged: True LL-Null: -27.997
Covariance Type: nonrobust LLR p-value: 1.202e-05
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
Intercept 1.9345 0.781 2.478 0.013 0.405 3.464
education -0.2117 0.071 -2.983 0.003 -0.351 -0.073
==============================================================================
sns.scatterplot(x="education", y="ratio", data=df_wrole)
xx = np.linspace(0, 20, 100)
df_wrole_p = pd.DataFrame({"education": xx})
plt.plot(xx, result_wrole2.predict(df_wrole_p), "r-", lw=4, label="예측")
plt.legend()
plt.show()

# 연습문제 1 답
from sklearn.datasets import load_iris
iris = load_iris()
idx = np.in1d(iris.target, [0, 1])
X0 = iris.data[idx, :1]
X = sm.add_constant(X0)
y = iris.target[idx]
logit_mod = sm.Logit(y, X)
logit_res = logit_mod.fit(disp=0)
print(logit_res.summary())
Logit Regression Results
==============================================================================
Dep. Variable: y No. Observations: 100
Model: Logit Df Residuals: 98
Method: MLE Df Model: 1
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.5368
Time: 10:02:05 Log-Likelihood: -32.106
converged: True LL-Null: -69.315
Covariance Type: nonrobust LLR p-value: 6.320e-18
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
const -27.8315 5.434 -5.122 0.000 -38.481 -17.182
x1 5.1403 1.007 5.107 0.000 3.168 7.113
==============================================================================
logit_res.params
array([-27.83145099, 5.14033614])
-logit_res.params[0] / logit_res.params[1]
5.41432510257189
y_pred = logit_res.predict(X) >= 0.5
from sklearn.metrics import confusion_matrix
confusion_matrix(y, y_pred)
array([[45, 5],
[ 6, 44]])
from sklearn.metrics import classification_report
print(classification_report(y, y_pred))
precision recall f1-score support
0 0.88 0.90 0.89 50
1 0.90 0.88 0.89 50
accuracy 0.89 100
macro avg 0.89 0.89 0.89 100
weighted avg 0.89 0.89 0.89 100
plt.plot(logit_res.fittedvalues, "ro-")
plt.plot(y_pred, "bs-")
[<matplotlib.lines.Line2D at 0x7f653c0d97f0>]

from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y, logit_res.fittedvalues)
plt.plot(fpr, tpr, 'o-')
plt.show()

# 연습문제 2 답
from sklearn.datasets import load_iris
iris = load_iris()
idx = np.in1d(iris.target, [1, 2])
X0 = pd.DataFrame(iris.data[idx, :], columns=iris.feature_names[:])
X = sm.add_constant(X0)
y = iris.target[idx] - 1
logit_mod = sm.Logit(y, X)
logit_res = logit_mod.fit(disp=1)
print(logit_res.summary())
Optimization terminated successfully.
Current function value: 0.059493
Iterations 12
Logit Regression Results
==============================================================================
Dep. Variable: y No. Observations: 100
Model: Logit Df Residuals: 95
Method: MLE Df Model: 4
Date: Sat, 06 Jun 2020 Pseudo R-squ.: 0.9142
Time: 10:02:15 Log-Likelihood: -5.9493
converged: True LL-Null: -69.315
Covariance Type: nonrobust LLR p-value: 1.947e-26
=====================================================================================
coef std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------------
const -42.6378 25.708 -1.659 0.097 -93.024 7.748
sepal length (cm) -2.4652 2.394 -1.030 0.303 -7.158 2.228
sepal width (cm) -6.6809 4.480 -1.491 0.136 -15.461 2.099
petal length (cm) 9.4294 4.737 1.990 0.047 0.145 18.714
petal width (cm) 18.2861 9.743 1.877 0.061 -0.809 37.381
=====================================================================================
Possibly complete quasi-separation: A fraction 0.60 of observations can be
perfectly predicted. This might indicate that there is complete
quasi-separation. In this case some parameters will not be identified.
y_pred = logit_res.predict(X) >= 0.5
from sklearn.metrics import confusion_matrix
confusion_matrix(y, y_pred)
array([[49, 1],
[ 1, 49]])
from sklearn.metrics import classification_report
print(classification_report(y, y_pred))
precision recall f1-score support
0 0.98 0.98 0.98 50
1 0.98 0.98 0.98 50
accuracy 0.98 100
macro avg 0.98 0.98 0.98 100
weighted avg 0.98 0.98 0.98 100