개요
박스콕스 변환(Box-Cox transformation)과 여존슨 변환(Yeo-Johnson transformation)은 데이터를 정규분포에 가깝게 변환하는 통계 기법이다. 데이터에 적용시킬 통계 모형이 정규분포를 가정하고 있고, 예측 모델의 예측 성능을 향상 시킬 수 있기 때문에 데이터를 정규화한다. 정규분포를 가정하는 통계기법은 선형회귀 , 로지스틱 회귀 등이 있다.
데이터의 값이 양수라면 해당 범위를 포괄하는 박스콕스 변환을 사용하며, 데이터가 0또는 음수값이 존재할 경우 이를 보안한 여존슨 변환을 사용한다. 해당 변환은 sklearn 패키지에서 사용할 수 있다.
from sklearn.preprocessing import power_transform
#여존슨 변환
df[['여존슨 변환한 컬럼']]=power_transform(df[['변환할 컬럼']], standardize=False)
#BOXcox 변환
df[['박스 콕스 변환한 컬럼']]=power_transform(df[['변환할 컬럼']], method='box-cox',standardize=False)
박스콕스 변환(Box-Cox transformation)
양수인 데이터를 정규분포에 가깝게 만드는 방법
λ ≠ 0 , 데이터 를 만큼 거듭제곱한 후 1을 뺀 뒤, λ로 나눈다.
박스콕스 변환 과정
1. 변환 파라미터() 추정: 데이터에 대해서 변환 파라미터()를 최적화한다. 보통 최대우도추정법이나 교차검증을 사용한다.
2. 데이터 변환
박스콕스 변환 한계점
- 데이터가 0 또는 음수 값을 가질 때 적용하기 어려움 👉 여존슨 변환(Yeo-Johnson transformation) 사용
여존슨 변환(Yeo-Johnson transformation)
박스콕스 변환을 보완하고 음수 값을 가지는 데이터에도 적용할 수 있도록 확장된 변환이다.
데이터가 0 또는 양수일 경우,
- λ= 0 인 경우: 데이터 에 1을 더하고 만큼 거듭제곱한 후 1을 뺀 뒤, 로 나눈다.
- λ ≠ 0 인 경우: 데이터 에 1을 더한 후 로그를 취한다.
데이터가 음수일 경우,
- λ ≠ 2 인 경우: 데이터 에 1을 빼고 2−λ 만큼 거듭제곱한 후 1을 뺀 뒤, 로 나눈다.
- λ = 2 인 경우: 데이터 에 1을 빼고 로그를 취한다.
적용하기 - 예시
Kaggle의 bike_sharing_daily 데이터를 가지고, 간단한 선형회귀 모델을 생성하고 정규화를 통해 성능에 차이가 있는지 몇개의 변수만 가지고 확인해보았다.
1. 분포 확인(distplot)
cols = ['temp', 'atemp', 'hum', 'windspeed',
'casual', 'registered']
fig, axs = plt.subplots(nrows = len(cols) , figsize = (20,20))
for i,col in enumerate(cols):
sns.distplot(df[col], ax = axs[i])

registered 정규분포에 가까워보이고, hum,windspeed,casual은 분포가 한쪽으로 치우쳐져 있다는 것을 확인했다.
더 정확한 확인을 위해, shapiro 검정을 실시했다.
Shapiro 검정
# shapiro 검정
from scipy.stats import shapiro
import numpy as np
for col in cols:
stat, p = shapiro(df_bs[col])
alpha = 0.05
print(col , 'shapiro test result:')
if p > alpha:
print('Fail to reject H0')
else:
print('reject H0') # 귀무가설 기각 - 정규성을 따르지 않음
print('------------------')
temp shapiro test result:
reject H0
------------------
atemp shapiro test result:
reject H0
------------------
hum shapiro test result:
reject H0
------------------
windspeed shapiro test result:
reject H0
------------------
casual shapiro test result:
reject H0
------------------
registered shapiro test result:
reject H0
------------------
가장 정규분포와 비슷한 컬럼조차도 모두 정규성 가정을 만족시키지 못했다.
Box-cox, Yeo-Johnson 정규화 후 성능비교
- windspeed (Yeo-Johnson)
from sklearn.preprocessing import power_transform
#여존슨변환
df_bs[['windspeed(yeo-johnson)']] = power_transform(df_bs[['windspeed']], standardize = False)
# 데이터 준비
df_X = np.array(df_bs[['casual', 'windspeed(yeo-johnson)']]).reshape(-1, 2) # casual 및 registered 열 선택
y = df_bs['cnt'] # 종속 변수 선택
# 데이터를 훈련 세트와 테스트 세트로 분할
X_train, X_test, y_train, y_test = train_test_split(df_X, df_bs['cnt'], test_size=0.3, random_state=111)
model3 = LinearRegression()
model3.fit(X_train,y_train)
y_pred = model3.predict(X_test)
rmse3 = sqrt(mean_squared_error(y_test, y_pred))
print('model3:',rmse3)
model3: 1392.714416346799
- windspeed (Box-cox)
#박스콕스 변환
df_bs[['windspeed(box-cox)']] = power_transform(df_bs[['windspeed']],method = 'box-cox', standardize = False)
# 데이터 준비
df_X = np.array(df_bs[['casual', 'windspeed(box-cox)']]).reshape(-1, 2) # casual 및 registered 열 선택
y = df_bs['cnt'] # 종속 변수 선택
# 데이터를 훈련 세트와 테스트 세트로 분할
X_train, X_test, y_train, y_test = train_test_split(df_X, df_bs['cnt'], test_size=0.3, random_state=111)
model4 = LinearRegression()
model4.fit(X_train,y_train)
y_pred = model4.predict(X_test)
rmse4 = sqrt(mean_squared_error(y_test, y_pred))
print('model4:',rmse4)
model4: 1392.498762752565
- 결과 요약
0. casual만 사용한 버전 : 1389.867711745627
1. casual, registered(정규분포에 근사한 피처) 추가 : 3.5267025006432875
2. casual, windspeed(정규분포에 근사하지 않은 피처) 추가 : 1393.0310307604834
3. casual, windspeed(정규화,yeo-johnson) 추가 : 1392.714416346799
4. casual, windspeed(정규화,box-cox) 추가 : 1392.498762752565
0 > 1 : 1389 > 3 으로 급격한 성능개선이 이루어진 이유를 알아보기 위해 상관계수를 확인해봤다.

급격한 성능개선이 피처가 정규분포인 이유보다,y 변수와의 상관계수가 0.9로 매우 높기 때문인 것으로 예상됨
그렇다면 cnt 와 높은 상관관계를 보인 registered 변수를 정규화하면 어떨까
#여존슨변환
df_bs[['registered(yeo-johnson)']] = power_transform(df_bs[['registered']], standardize = False)
# 데이터 준비
df_X = np.array(df_bs[['casual', 'registered(yeo-johnson)']]).reshape(-1, 2) # casual 및 registered 열 선택
y = df_bs['cnt'] # 종속 변수 선택
# 데이터를 훈련 세트와 테스트 세트로 분할
X_train, X_test, y_train, y_test = train_test_split(df_X, df_bs['cnt'], test_size=0.3, random_state=111)
model5 = LinearRegression()
model5.fit(X_train,y_train)
y_pred = model5.predict(X_test)
rmse5 = sqrt(mean_squared_error(y_test, y_pred))
print('model5:',rmse5)
model5 : 67.xxxx
y 변수와 높은 상관관계를 보인 registered 피처는 정규화 하기 이전 결과의 모델 성능이 더 좋다
> 다중공선성 및 정보손실이 의심됨 > summary 확인이 필요
X = sm.add_constant(df_X)
model_ols = sm.OLS(df_bs['cnt'], df_bs[['casual','registered(yeo-johnson)']]).fit()
summary = model_ols.summary()


(왼쪽부터, resigtered 정규화하기 전,후)
중간 결론
registered 피처를 정규화 하기 전 모델이 더 좋은 성능을 보인다.(단, Omnibus, JB 값이 매우 높아 잔차의 정규성에 대한 확인이 필요)
- 잔차가 정규분포를 따르는지를 확인하는 통계적 검정 방법 > Omnibus
- 잔차의 왜도와 첨도가 정규분포와 일치하는지를 검정 > JB > 과적합 확인 필요
당연히, 이번 모델링에서는 피처를 두개 밖에 사용하지 않았고, 그 중 하나는 타겟변수와 매우 높은 상관관계를 보이므로 당연한 결과라고 할 수 있다.
데이터가 이미 정규분포에 가까운 경우나, 정규화가 필요하지 않은 다른 형태의 전처리가 필요한 경우에는 정규화가 모델의 성능을 개선하지 않을 수 있다. 따라서 정규화를 사용하여 모델의 성능을 개선하기 전에 데이터와 모델의 특성을 신중하게 고려해야 한다.
'Data Science & AI > Data Analysis' 카테고리의 다른 글
| ANOVA(Analysis of Variance , 분산 분석) (0) | 2024.06.01 |
|---|---|
| Apriori 연관 규칙 알고리즘 - 개념, 전처리 방법, 모델 적용 (1) | 2024.03.24 |
| 데이터 비닝과 WOE(Weight of Evidence), IV(Information Value) (0) | 2024.02.29 |
| [텍스트 분석] konlpy 한글 형태소 분석 (0) | 2024.02.22 |
| [텍스트 분석] nltk 영어 형태소 분석 - 토큰화/정규화/품사 태깅 (0) | 2024.02.21 |