01 데이터 분석 과정
-01 예측 모델링 과정 (회귀, 분류)
-02 EDA
-03 전처리
-04 모델링
02 로지스틱 회귀
-01 분류 모델
-02 장점
-03 단점
-04 로지스틱 회귀 모형 (**)
03 교호작용 (상호작용 : Interaction) 효과
01 데이터 분석 과정
-01 예측 모델링 과정 (회귀, 분류)
- 분석기획 (목적설정) -> 데이터 수집 -> EDA -> 전처리 -> 모델링 -> 평가 -> 전재
-02 EDA (데이터 탐색)
- 노이즈 확인 (결측치, 이상치)
| 수치형 | 문자형 |
| 히스토그램 box plot (이상치 탐색) |
빈도수 최빈값 |
- 각 변수의 분포 확인 : 정규성 검정, 정규화를 통해 정규분포로 만드는 로그변환 등
- 각 변수의 타입 확인 (수치형, 범주형 -> 인코딩 필요)
> 너무 많은 클래스를 가진 경우에는 원핫인코딩은 적합하지 않다. (차원의 저주)
> 로지스틱 회귀, SVM의 경우 라벨 인코딩이 적합하지 않다.
> 인코딩이 어려운 경우 random forest regression model 사용 등을 고려
-03 전처리
- 인코딩 : 라벨인코딩, 원핫인코딩
> sklearn, pandas 사용
- 스케일링 : 표준화, 정규화
> standard, minmax, robust (robust는 잘 사용하지 않는다)
- 교호작용 (상호작용) **
> 변수 하나하나의 의미보다 특정 변수의 조합이 보다 의미있을 때
> 모든 모델에 필요한 전처리
- 변수 변환 (수치형 데이터 -> 범주형 데이터 (binning), 로그 변환 등) **
- 파생변수 생성 **
-04 모델링
1) 분류
- knn
> 비교적 고차원 데이터여도 성능이 발휘되는 편
> 사전 변수 선택이 필요
- 변수의 선택이 어려운 상황이기에 모든 변수를 사용해야 하는 상황 (검정 데이터들이 많은 경우)이라면 knn 사용은 부적합
> 거리 기반 모델로 스케일링, 이상치 여부 확인 등이 중요
- SVM
> 사전 변수 선택이 필요
> 스케일링이 필요
> 범주형 데이터 사용 시 one-hot encoding이 필요
(원 데이터 자체가 one-hot encoding 시 의미가 없는 상황이라면 고차원이 되기 때문에 (30차원 이상은 부적합) knn 사용 등을 고려해야 함)
> 과적합에 대한 위험성은 낮으나 모델 설명력이 다소 떨어지는 문제가 있어 C, gamma 튜닝이 필요함
- Decision tree, random forest, boosting tree
> 라벨인코딩, 스케일링에 관계가 없다
> 최적의 분류규칙을 알아서 만들어 줌
> 별도의 파라미터 세팅을 하지 않아도 적절한 수준의 모델을 만들어주나, 과적합 방지를 위해 진행해주는 것이 권장
- logistic regressor
> 수치형, 범주형 데이터에 구애받지 않음
> 그러나 범주형 데이터의 경우 one-hot encoding이 필요
> 스케일링 필요
> 사전에 변수 선택 필요
2) 회귀
- lm, ridge, lasso
- 분류모델의 회귀모형 (*)
-05 평가
- 그리드 서치 (매개변수 최적화)
> C, gamma의 경우 C의 지점에 따라 gamma의 지점이 변화하고, gamma의 지점에 따라 C의 지점이 변화
> 따라서 그리드 서치를 통해 최적의 조합을 찾음
- 여러가지 평가 척도 활용
02 로지스틱 회귀
-01 분류 모델
- 독립변수의 선형결합으로 로그오즈 추정
- 인과관계 해석 가능 (특정 변수가 1단위 증가함에 따라 실패 대비 성공 확률이 얼마나 증가하는지 확인 가능)
- 회귀식 추정 : 최대우도추정법 (MLE)
- 가중합의 시도를 0과 1로 나누는 함수가 시그모이드 함수, 이를 위해 y를 로짓변환 (연속형 -> 범주형) 필요
- 소수 클래스 (관심있어하는 대상)을 1, 이외를 0으로 둠
- 로짓변환은 실패 확률 분의 성공 확률 log(p(y=1) / 1-p(y=1))
- 회귀계수는 로그 오즈에 대한 변화량 (게임시간이 한 단위 감소함에 따라 비이탈대비 이탈확률이 얼마나 증가하는가?)
- '어떤 변수가, 얼마나 (몇 배) 변하는가' 에 대한 질문
- 예측을 잘 하는 것과는 별개로 매우 중요하다.
-02 장점
- 통계적 유의성 검정 확인 가능 (각 변수의 유의성 검정)
> 각각의 회귀계수에 대한 검정통계량, p-value가 제공됨
> H0 : β = 0 , H1 : β != 0
- 인과관계 분석 가능 (각 변수에서 추정된 회귀계수로부터 odds값 확인 가능)
-03 단점
- 이상치에 민감
- 변수 선택 중요
- 가급적 스케일링 권고
-04 로지스틱 회귀 모형 (**)
log(odds) = b0 + b1x1
odds = P(Y=1) / (1 - P(Y=1))
P(Y=1) / (1 - P(Y=1)) = b0 + b1x1
성공확률 등이 어떻게 되는지 추정
위 수식을 P(Y=1)에 대해 풀면
P(Y=1) = (1 - P(Y=1)) * exp(b0 + b1x1)
= exp (b0 + b1x1) - P(Y=1) * exp (b0 + b1x1)
P(Y=1) + P(Y=1) * exp(b0 + b1x1) = exp(b0 + b1x1)
P(Y=1)(1 + exp(b0 + b1x1) = exp (b0 + b1x1)
따라서 P(Y=1) = exp(b0 + b1x1) / (1 + exp(b0 + b1x1))
= 1 / (1 + exp(-(b0 + b1x1))) <----- 시그모이드 함수 식
[ 예제 - iris 품종 분류 (2개 클래스 분류 -> 이항 로지스틱 회귀) ]
# 1 . 데이터 로딩
from sklearn.datasets import load_iris
import pandas as pd
iris = load_iris()
X, y = iris.data, iris.target
X = X[y != 0, :]
y = y[y != 0]
y = pd.Series(y).map({1:0,2:1}).values # 클래스를 각각 0, 1로 변환
pd.Series(y).value_counts() # 클래스별 빈도 확인
# 2. 스케일링 진행
# 데이터를 나누기 전 스케일링을 진행한다.
from sklearn.preprocessing import MinMaxScaler # 모두 양수로 출력되는 minmax scaler 선택
m_sc = MinMaxScaler()
X_sc = m_sc.fit_transform(X)
# 3. train / test 분리
from sklearn.model_selection import train_test_split
train_x, test_x, train_x_sc, test_x_sc, train_y, test_y = train_test_split(X, X_sc, y, random_state=0, stratify=y)
[ stratify = y 옵션에 대해 ]
원본 데이터 자체의 소수 클래스가 9 : 1 의 극단적인 비율을 가지고 있다고 가정하자.
이 중 일부는 train, 일부는 test로 나누는 과정에 있어 랜덤하게 추출할 시 클래스의 비율을 어느 정도 유지한다고 가정한다.
(랜덤샘플링의 특징)
랜덤샘플링은 수집이 무작위로 잘 되었을 때 표본집단이 모집단의 특성 혹은 클래스 비율 등을 반영하는 특성을 가지는데, y의 75%를 train으로, 25%를 test로 '섞어놓은' 원 데이터에서 순서대로 선택하기 때문에 train과 test 모두 y 내의 클래스 비율을 동일하게 가진다.
그러나 섞은 후 train / test 데이터를 분리하는 과정에서 소수 클래스의 비율이 편향될 수 있는 만약의 가능성이 있기 때문에 (홀수인 경우 더더욱) stratify 옵션을 설정함으로써 각 데이터의 다수 : 소수 비율을 유지시킨다.
현업에서는 대체로 해당 옵션을 설정해주는 편이다.
최대우도추정법 (MLE)은 '확률'인데, 최대반복수를 제한(maxiter = 200) 해주지 않으면 수렴 지점까지 계속해서 발산을 하며 회귀계수가 증가하는 문제가 발생한다.
예를 들어 iris 데이터와 같이 데이터의 분류가 매우 명확하게 되는 경우 성공 확률이 높아지며 문제가 발생한다.
랜덤포레스트의 경우 어느 정도 분류가 되면 트리의 생성이 중단되나, 회귀의 경우 계속하여 확률을 발생하며 최댓값인 1에 수렴하려고 하며 계속하여 발산하는 경향이 있다.
(확률을 조금이라도 높일 수 있다면 지속적으로 계수를 높인다. proba = 1.0 인 경우가 이에 해당된다.)
* 클래스 분류가 잘 되지 않는 경우 혼합경계이기 때문에 0일 클래스의 확률, 1일 클래스의 확률이 둘 다 높아 확률 자체가 1에 가까워질 수가 없다. (더 이상 회귀계수가 커질 수 없다.)
따라서 iris 데이터와 같이 명확한 분류가 존재하는 경우 MLE를 사용하는 로지스틱 회귀의 사용이 적합하지 않을 수 있다.
( + 물론 현업에서 이와 같이 극단적인 분류가 발생하는 데이터는 흔하지 않은 편이나 데이터의 경향에 따라 해당 내용을 참고할 필요가 있다.)
ex)
print(model.summary())
Logit Regression Results
==============================================================================
Dep. Variable: y No. Observations: 75
Model: Logit Df Residuals: 70
Method: MLE Df Model: 4
Date: Mon, 08 Jun 2026 Pseudo R-squ.: 1.000
Time: 11:50:54 Log-Likelihood: -8.9664e-06
converged: False LL-Null: -51.979
Covariance Type: nonrobust LLR p-value: 1.412e-21
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
const -437.7812 2.02e+06 -0.000 1.000 -3.96e+06 3.96e+06
x1 -309.7354 1.49e+07 -2.08e-05 1.000 -2.92e+07 2.92e+07
x2 -404.4884 4.48e+06 -9.02e-05 1.000 -8.79e+06 8.78e+06
x3 1114.9896 1.29e+07 8.62e-05 1.000 -2.54e+07 2.54e+07
x4 528.9591 5.35e+05 0.001 0.999 -1.05e+06 1.05e+06
==============================================================================
Complete Separation: The results show that there iscomplete separation or perfect prediction.
In this case the Maximum Likelihood Estimator does not exist and the parameters
are not identified.
▲ 회귀계수가 계속하여 커지면 우도가 커지면서 발산하게 된다.
따라서 이와 같은 경우 반복수를 제한하거나, 그럼에도 불구하고 해당 문제가 반복되는 경우 로지스틱 회귀 사용을 재검토해야 한다.
# 4. 모델링
로지스틱 회귀 패키지는 2개가 있다.
1) sklearn
> 빠른 적합, 코드의 간결이라는 장점이 있다.
> 유의성 검정 결과를 확인할 수 없다.
> 오직 fitting, predict에만 초점을 맞추고 있다.
> L2 패널티를 적용한다. : 회귀계수가 커져서 과적합이 되는 것을 방지한다. 정규화라고도 한다.
> C가 커지면 모델이 복잡해지며, C가 작아지면 모델이 단순해진다. (C 자체가 패널티에 대한 강도이기 때문)
> 상수항(b0 : 모든 x가 0일 때 기본으로 값는 y의 설정 값. 절편)이 기본적으로 적용되어 있다.
2) statmodels
> 코드가 복잡하다.
> 유의성 검정 결과를 확인할 수 있다.
> L2 패널티를 적용하지 않는다. : 순수하게 MLE (최대우도추정법)에 의해 추정된 결과만을 적용한다.
> 상수항이 기본으로 추가되지 않기 때문에 직접 상수항을 추가해야 한다.
따라서 두 모델의 회귀계수 추정 결과는 다를 수밖에 없다.
# 4-1) sklearn
LogisticRegression()

▲ penalty='l2'는 회귀계수들의 제곱합에 대한 패널티를 적용한다. 패널티의 강도는 C로 조절하며, C가 작을수록 강한 정규화(패널티가 약하게 적용)가 적용되고 C가 클수록 약한 정규화(패널티가 강하게 적용)가 적용된다.
from sklearn.linear_model import LogisticRegression
m_lr = LogisticRegression()
m_lr.fit(train_x, train_y)
m_lr.score(train_x, train_y) # score는 accuracy, 0.92
m_lr.score(test_x, test_y) # 1.0
m_lr.score(train_x, train_y) # score는 accuracy, 0.92
Out[31]: 0.92
m_lr.score(test_x, test_y) # 1.0
Out[32]: 1.0
# 과소적합으로 판단되며 모형이 단순하기 때문에 C가 커질 필요가 있다.
# train set ( n = 100 ) 이 적었거나, 모형이 단순했거나 ... 등의 이유로 과소적합이 발생
# 따라서 튜닝의 여지가 존재한다.
m_lr.predict(test_x) # 예측 클래스 확인
m_lr.predict(test_x) # 예측 클래스
Out[34]:
array([1, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2,
1, 2, 1])
m_lr.predict_proba(test_x) # 각 클래스별 확률 확인
m_lr.predict_proba(test_x)
Out[33]:
array([[0.59743447, 0.40256553],
[0.13065309, 0.86934691],
[0.53381603, 0.46618397],
[0.09348863, 0.90651137],
# 결과 확인 (pvalue 확인 불가)
m_lr.intercept_ # 절편항, -2.70154559
m_lr.intercept_
Out[36]: array([-2.70154559])
m_lr.coef_ # 회귀계수, 0.81385627, -0.16627437, 2.29993539, 3.01390373
m_lr.coef_
Out[35]: array([[ 0.81385627, -0.16627437, 2.29993539, 3.01390373]])
▲ log ( P(Y=2) / (1 - P(Y=2) ) = b0 + b1x1 + b2x2 + b3x3 + b4x4
log(odds) = b0 + b1x1 + b2x2 + b3x3 + b4x4
# 4-2) statmodels
- 유의성 검정 결과를 제시함
- 상수항의 별도 추가가 필요함
- 해당 과정에서 코드가 sklearn에 비해 복잡한 경향이 있음
- 회귀 계수 추정 시 별도의 정규화 (패널티)를 진행하지 않음
- 회귀 계수가 순수 MLE에 의해 결정됨
import statsmodels.api as sm
# 데이터 변경 (array -> DataFrame)
df_train_x = pd.DataFrame(train_x, columns = iris.feature_names)
df_test_x = pd.DataFrame(test_x, columns = iris.feature_names)
# 상수항 추가
import statsmodels.api as sm
train_x_const = sm.add_constant(df_train_x)
test_x_const = sm.add_constant(df_test_x)
# 모형 적합
model = sm.Logit(train_y, train_x_const).fit(maxiter = 200, disp=False) # 너무 잘 분류되는 데이터의 경우 경고 발생(무시)
print(model.summary())
print(model.summary()) # 회귀계수가 계속 커지면 우도가 커지면서 발산
Logit Regression Results
==============================================================================
Dep. Variable: y No. Observations: 75
Model: Logit Df Residuals: 70
Method: MLE Df Model: 4
Date: Mon, 08 Jun 2026 Pseudo R-squ.: 0.8953
Time: 14:11:57 Log-Likelihood: -5.4427
converged: True LL-Null: -51.979
Covariance Type: nonrobust LLR p-value: 2.927e-19
=====================================================================================
coef std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------------
const -41.5488 24.673 -1.684 0.092 -89.908 6.810
sepal length (cm) -2.3467 2.333 -1.006 0.315 -6.920 2.226
sepal width (cm) -6.9300 4.499 -1.540 0.123 -15.748 1.888
petal length (cm) 9.5390 4.870 1.959 0.050 -0.006 19.084
petal width (cm) 17.1598 9.324 1.840 0.066 -1.115 35.434
=====================================================================================
Possibly complete quasi-separation: A fraction 0.53 of observations can be
perfectly predicted. This might indicate that there is complete
quasi-separation. In this case some parameters will not be identified.
# warning이 가끔 발생한다.
# 데이터들이 잘 분류가 되는 경우에는 정확한 회귀계수의 추정이 어려울 수 있다.
# 따라서 우도를 추정함에 있어 추정 수를 200으로 설정해준다.
model.pvalues # 유의확률
model.params # 회귀계수
model.pvalues # 유의확률
Out[88]:
const 0.092191
sepal length (cm) 0.314535
sepal width (cm) 0.123481
petal length (cm) 0.050143
petal width (cm) 0.065709
dtype: float64
model.params # 회귀계수
Out[89]:
const -41.548774
sepal length (cm) -2.346668
sepal width (cm) -6.929972
petal length (cm) 9.539045
petal width (cm) 17.159779
dtype: float64
# ** 해석
# petal width (cm) 1 증가됨에 따라 odds (0 대비 1의 확률) 확인
model.pvalues.drop('const')
Out[95]:
sepal length (cm) 0.314535
sepal width (cm) 0.123481
petal length (cm) 0.050143
petal width (cm) 0.065709
dtype: float64
import numpy as np
np.exp(model.params['petal width (cm)'])
np.exp(model.params['petal width (cm)'])
Out[92]: np.float64(28339838.91024128)
# 가장 유의한 변수에 대한 회귀계수 출력
cname = model.pvalues.drop('const').idxmin()
model.params[cname]
cname = model.pvalues.drop('const').idxmin()
model.params[cname]
Out[93]: np.float64(9.539045048622691)
[ 예제 - iris 품종 분류 (3개 클래스 분류 -> 다항 로지스틱 회귀) ]
# 1. 데이터 로딩
from sklearn.datasets import load_iris
iris = load_iris()
X,y = iris.data, iris.target
ex) 클래스 A,B,C -> 오름차순 정렬 후 가장 앞에 있는 클래스를 기준 클래스로 정의
(만약 기준클래스 변경을 원하는 경우는 기준클래스 값을 0으로 직접 인코딩)
모형1)
log(P(Y=B)/P(Y=A)) = b0 + b1x1
모형2)
log(P(Y=C)/P(Y=A)) = b0 + b1x1
오즈에 대한 해석을 진행하려면 sklearn이 아닌 statmodels를,
해석과 상관없이 predict만 필요하다면 sklearn을 사용한다.
# 1 . 데이터 로딩
from sklearn.datasets import load_iris
import pandas as pd
iris = load_iris()
X, y = iris.data, iris.target
X = X[y != 0, :]
y = y[y != 0]
y = pd.Series(y).map({1:0,2:1}).values # 클래스를 각각 0, 1로 변환
pd.Series(y).value_counts() # 클래스별 빈도 확인
pd.Series(y).value_counts() # 클래스별 빈도 확인
Out[267]:
0 50
1 50
Name: count, dtype: int64
# 2. 스케일링 진행
# 데이터를 나누기 전 스케일링을 진행한다.
from sklearn.preprocessing import MinMaxScaler # 모두 양수로 출력되는 minmax scaler 선택
m_sc = MinMaxScaler()
X_sc = m_sc.fit_transform(X)
# 3. train / test 분리
from sklearn.model_selection import train_test_split
train_x, test_x, train_x_sc, test_x_sc, train_y, test_y = train_test_split(X, X_sc, y, random_state=0, stratify=y)
# 4. 모델링
# 4-1) sklearn
from sklearn.linear_model import LogisticRegression
m_lr = LogisticRegression(random_state = 0)
m_lr.fit(train_x, train_y)
m_lr.score(train_x, train_y)
m_lr.score(test_x, test_y)
m_lr.score(train_x, train_y)
Out[280]: 0.96
m_lr.score(test_x, test_y)
Out[281]: 1.0
m_lr.predict(test_x_sc) # 예측 클래스 확인
m_lr.predict_proba(test_x_sc) # 각 클래스별 확률 확인
m_lr.predict(test_x_sc) # 예측 클래스 확인
Out[282]:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0])
m_lr.predict_proba(test_x_sc) # 각 클래스별 확률 확인
Out[283]:
array([[9.99962687e-01, 3.73125502e-05],
[9.99955028e-01, 4.49717197e-05],
[9.99996154e-01, 3.84572631e-06],
각 확률이 주어졌을 때 가장 확률이 큰 쪽의 값을 가져가는 방식 -> 소프트맥스
# 결과 확인 (pvalue 확인 불가)
m_lr.intercept_
m_lr.coef_
m_lr.intercept_
Out[284]: array([-13.70571886])
m_lr.coef_
Out[285]: array([[-0.21809641, -0.49547801, 2.65164442, 2.11618419]])
# 4-2) statsmodel
# 데이터 변경 (array -> DataFrame)
df_train_x = pd.DataFrame(train_x, columns = iris.feature_names)
df_test_x = pd.DataFrame(test_x, columns = iris.feature_names)
# 상수항 추가
import statsmodels.api as sm
train_x_const = sm.add_constant(df_train_x)
test_x_const = sm.add_constant(df_test_x)
# 모형 적합
model = sm.MNLogit(train_y, train_x_const).fit(maxiter = 10000, disp=False) # 너무 잘 분류되는 데이터의 경우 경고 발생(무시)
print(model.summary())
print(model.summary())
MNLogit Regression Results
==============================================================================
Dep. Variable: y No. Observations: 75
Model: MNLogit Df Residuals: 70
Method: MLE Df Model: 4
Date: Mon, 08 Jun 2026 Pseudo R-squ.: 0.8953
Time: 16:22:48 Log-Likelihood: -5.4427
converged: True LL-Null: -51.979
Covariance Type: nonrobust LLR p-value: 2.927e-19
=====================================================================================
y=1 coef std err z P>|z| [0.025 0.975]
-------------------------------------------------------------------------------------
const -41.5488 24.673 -1.684 0.092 -89.908 6.810
sepal length (cm) -2.3467 2.333 -1.006 0.315 -6.920 2.226
sepal width (cm) -6.9300 4.499 -1.540 0.123 -15.748 1.888
petal length (cm) 9.5390 4.870 1.959 0.050 -0.006 19.084
petal width (cm) 17.1598 9.324 1.840 0.066 -1.115 35.434
=====================================================================================
model.pvalues
model.params
model.pvalues
Out[293]:
0
const 0.092191
sepal length (cm) 0.314535
sepal width (cm) 0.123481
petal length (cm) 0.050143
petal width (cm) 0.065709
model.params
Out[294]:
0
const -41.548774
sepal length (cm) -2.346668
sepal width (cm) -6.929972
petal length (cm) 9.539045
petal width (cm) 17.159779
03 교호작용 (상호작용 : Interaction) 효과
- 각 변수를 하나씩 학습시켰을 때 변수 중요도가 낮더라도 다른 변수와 결합하여 변수 중요도가 올라가는 경우에 발생함
- 실제 분석 시 교호작용 효과를 찾아내고 검증하는 것이 핵심 과제
1) 직접 생성
df['X1 X X2'] = df['X'] * df['X2']
2) 모든 발생 가능한 교호작용 생성 -> 중요한 교호작용 확인
랜덤포레스트의 변수 중요도(Feature Importance)를 절대적으로 신뢰해서는 안 된다.
예를 들어, 특정 변수가 랜덤포레스트에서 낮은 중요도를 보였다고 해서 곧바로 불필요한 변수라고 판단하기는 어렵다.
해당 변수가 목표변수의 각 범주(Yes/No)에 대해 비슷한 수준의 영향을 미친다면 중요도가 낮게 산출될 수 있기 때문이다.
또한 변수 간 상호작용 효과(Interaction Effect)를 고려해야 한다.
예를 들어, 국제전화 요금제에 가입하지 않은 고객은 관심 대상이 아니지만, 국제전화 요금제에 가입한 고객 중 국제전화 사용 요금이 높은 고객은 통신사를 이탈할 가능성이 높을 수 있다.
이처럼 어떤 변수는 단독으로는 큰 영향력이 없어 보이더라도 다른 변수와 결합되었을 때 예측 성능에 중요한 역할을 할 수 있다.
따라서 변수 선택 과정에서는 단순히 랜덤포레스트의 변수 중요도만을 기준으로 판단하기보다, 변수 간 상호작용과 도메인 지식을 함께 고려해야 한다.
만약 스스로 적절한 변수 선택이 어렵다면, 변수 선택을 지원하는 통계적 기법이나 알고리즘을 활용하는 것이 바람직하다.
과제 : 국제전화 요금제 가입 여부 (international plan) * 국제전화 총 요금 (total intl charge)를 새 컬럼에 넣기
기본 모델에 대한 베이스 라인 확인 vs 새 변수 추가가 된 라인 모델링 비교
state, area code, phone number는 제거
통합 : total day minutes, total day calls, total day charge 더하기
total eve minutes, total eve calls, total eve charge 더하기
뭔가 제거해서 94점 나온 모델 기준으로 해서 랜덤포레스트, svl, 로지스틱 회귀를 돌림으로써
interaction effect 입증
[ 예제 - 암 종양 분류 분석 (교호작용 효과) ]
# 1. 데이터 로딩
df = pd.read_csv('cancer.csv')
y = df['diagnosis']
X = df.drop(columns = ['id','diagnosis'])
# 2. 스케일링
from sklearn.preprocessing import MinMaxScaler
m_sc = MinMaxScaler()
X_sc = m_sc.fit_transform(X)
# 3. 모든 교호작용 출력 (2차항)
from sklearn.preprocessing import PolynomialFeatures
# 다항의 독립변수를 유도한다.
PolynomialFeatures(degree = 2, # 추가할 변수 형태 (기본 : 2차원)
interaction_only = False, # 교호작용만 출력할지 여부
include_bias = True) # 상수항 출력 여부
degree 3을 선택 시 1,2값을 포함하여 3차항까지 알려준다.
poly = PolynomialFeatures(degree = 2, interaction_only = False,include_bias = True)
X_sc_poly = poly.fit_transform(X_sc)
X_sc_poly.shape
X_sc_poly.shape
Out[171]: (569, 496)
poly.get_feature_names_out()
poly.get_feature_names_out()
Out[172]:
array(['1', 'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9',
'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18',
'x19', 'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27',
poly_cols = poly.get_feature_names_out(X.columns)
poly.get_feature_names_out(X.columns)
Out[173]:
array(['1', 'radius_mean', 'texture_mean', 'perimeter_mean', 'area_mean',
'smoothness_mean', 'compactness_mean', 'concavity_mean',
'concave_points_mean', 'symmetry_mean', 'fractal_dimension_mean',
poly_cols = [i.replace(' ', ' X ') for i in poly_cols]
[i.replace(' ', ' X ') for i in poly_cols]
Out[176]:
['1',
'radius_mean',
'texture_mean',
'perimeter_mean',
'area_mean',
'smoothness_mean',
'compactness_mean',
'concavity_mean',
'concave_points_mean',
'symmetry_mean',
'fractal_dimension_mean',
'radius_se',
'texture_se',
'perimeter_se',
'area_se',
'smoothness_se',
'compactness_se',
'concavity_se',
'concave_points_se',
'symmetry_se',
'fractal_dimension_se',
'radius_worst',
'texture_worst',
'perimeter_worst',
'area_worst',
'smoothness_worst',
'compactness_worst',
'concavity_worst',
'concave_points_worst',
'symmetry_worst',
'fractal_dimension_worst',
'radius_mean^2',
'radius_mean X texture_mean',
'radius_mean X perimeter_mean',
'radius_mean X area_mean',
'radius_mean X smoothness_mean',
imp = pd.Series(m_rf.feature_importances_, index = poly_cols).sort_values(ascending = False)
imp
imp
Out[204]:
area_worst X concave_points_worst 0.052270
perimeter_worst X concave_points_worst 0.051085
concavity_mean X radius_worst 0.048060
area_worst X smoothness_worst 0.041162
perimeter_worst X smoothness_worst 0.036315
perimeter_se X concave_points_se 0.000000
perimeter_se X symmetry_se 0.000000
perimeter_se X fractal_dimension_se 0.000000
perimeter_se X area_worst 0.000000
fractal_dimension_worst^2 0.000000
Length: 496, dtype: float64
▲ 결과에 있어 변수의 중요도가 높게 나왔을지라도 변수의 의미 하나하나에 대해 검토를 진행해야 한다.
두 변수 모두 다 중요하기 때문에 중요한 변수 x 중요한 변수의 조합이 더욱 중요한 것처럼 보이기도 한다.
따라서 해당 변수의 중요성을 알아보기 위해서는 해당 변수의 포함 여부를 기준으로 결과를 비교하면 되며, 포함 시 설명력이 오른다면 보다 중요한 변수라고 말할 수 있다.
[ 과제5. 교호작용 효과 검증 ]
# 1. 데이터 로딩 및 설명
import pandas as pd
pd.set_option('display.max_column', None)
df = pd.read_csv('telecom_churn.csv')
# X, y 분리 / 노이즈 변수 제거
X = df.drop(columns=['churn','phone number'])
y = df['churn']
# 변수 추가
X['total minutes'] = X.loc[:, X.columns.str.contains('minutes')].sum(axis=1) # 총 통화시간
X['total calls'] = X.loc[:, X.columns.str.contains('calls')].sum(axis=1) # 총 통화건수
X['total charge'] = X.loc[:, X.columns.str.contains('charge')].sum(axis=1) # 총 요금
# 노이즈 변수 제거
X = X.drop(columns=['state','area code','total eve calls','total calls'])
# 2. 라벨 인코딩
obj_cols = X.select_dtypes(include='object').columns # 문자형 컬럼만 선택 -> 라벨인코딩
num_cols = X.select_dtypes(exclude='object').columns # 숫자형 컬럼만 선택 -> 스케일링
from sklearn.preprocessing import LabelEncoder
for colname in obj_cols:
m_ec = LabelEncoder()
X[colname] = m_ec.fit_transform(X[colname]) # X에서 문자형 컬럼을 라벨인코딩 변환 후 바로 덮어쓰기
# 3. 스케일링
from sklearn.preprocessing import MinMaxScaler
X_sc = X.copy() # X와 동일한 X_sc copy 데이터 생성
for colname in num_cols:
m_sc = MinMaxScaler()
X_sc[colname] = m_sc.fit_transform(X_sc[[colname]])
# 4. 교호작용 추가
X_poly = X.copy()
X_sc_poly = X_sc.copy()
X_poly['intl_plan_x_charge'] = X['international plan'] * X['total intl charge']
X_sc_poly['intl_plan_x_charge'] = X['international plan'] * X['total intl charge']
m_sc = MinMaxScaler()
X_sc_poly['intl_plan_x_charge'] = m_sc.fit_transform(X_sc_poly[['intl_plan_x_charge']])
# 5. train/test 분리
from sklearn.model_selection import train_test_split
train_x, test_x, train_x_sc, test_x_sc, train_x_poly, test_x_poly, train_x_sc_poly, test_x_sc_poly, train_y, test_y = train_test_split(X, X_sc, X_poly, X_sc_poly, y, random_state=0)
# 6. 모델링 비교
# 5-1) 랜덤포레스트
from sklearn.ensemble import RandomForestClassifier
m_rf = RandomForestClassifier(random_state=0)
m_rf.fit(train_x,train_y)
print('RF 훈련 원본 점수 :', m_rf.score(train_x,train_y))
print('RF 평가 원본 점수 :', m_rf.score(test_x,test_y))
m_rf = RandomForestClassifier(random_state=0)
m_rf.fit(train_x_poly,train_y)
print('RF 훈련(교호작용) 원본 점수 :', m_rf.score(train_x_poly,train_y))
print('RF 평가(교호작용) 원본 점수 :', m_rf.score(test_x_poly,test_y))
# 5-2) SVM
from sklearn.svm import SVC
m_svm = SVC()
m_svm.fit(train_x_sc,train_y)
print('SVM 훈련 원본 점수 :', m_svm.score(train_x_sc,train_y))
print('SVM 평가 원본 점수 :', m_svm.score(test_x_sc,test_y))
m_svm = SVC()
m_svm.fit(train_x_sc_poly,train_y)
print('RF 훈련(교호작용) 원본 점수 :', m_svm.score(train_x_sc_poly,train_y))
print('RF 평가(교호작용) 원본 점수 :', m_svm.score(test_x_sc_poly,test_y))
'아이티윌_데이터 분석 55기 > 강의내용 필기_분석(Python)' 카테고리의 다른 글
| #7 7일차_독립성 검정, 적합도 검정, 클래스 불균등 처리 (0) | 2026.06.11 |
|---|---|
| #6 교호작용 및 효과 검증 (0) | 2026.06.09 |
| #4 4일차_복사 / SVM·RF 실습 / 모델링 파이프 라인 구축 / 분류 모델 / knn (0) | 2026.06.05 |
| #3 3일차_SVL 실습, 독립변수 스케일링 + 연습문제 및 문제풀이 (0) | 2026.06.04 |
| #2 2일차_랜덤포레스트 (분류), SVM (Support Vector Machine) (0) | 2026.06.02 |