
실세계의 데이터는 복잡하다. 완벽하지도 않다. 그렇기 때문에 일부 학습 데이터셋은 모델링에 사용되기 전에 전처리가 필요하다.
Linear regression 모델을 예로 들어 보자.
- Linearity : 선형성. 예측 변수와 목표 변수 간의 관계가 선형이라고 가정한다.
- No noise : 특이치(outlier)가 없어야 한다.
- No collinearity : 상관관계가 높은 예측 변수가 있는 경우 과적합(overfit)이 될 가능성이 높다.
- Normal distribution : 예측 변수와 목표 변수가 정규 분포를 따를 때 더 신뢰할 수 있는 예측이 이루어진다.
- Scale : 거리 기반의 알고리즘이므로 표준 scaler처럼 모델을 스케일링해야 한다.
오늘은 네 번째 요점에 초점을 맞추고자 한다. 예측 변수와 목표 변수가 가우스 분포(혹은 정규분포)를 따라야 한다는 것이다. 이게 무조건 가능한 건 아니다. 어떤 분포도 완벽한 정규 분포로 변환할 수는 없지만, 그렇다고 해서 시도하지 말아야 한다는 뜻도 아니다.
간단한 데이터 세트를 통해 몇 가지 마술을 시전 해보겠다.
데이터셋
내가 사용할 데이터는 Kaggle에 공개된 주택 가격 데이터이다.
sklearn을 통해 간단하게 가져올 수도 있겠지만, 이왕 하는 거 Kaggle 데이터로 분석해보자.(데이터 위치)
#필요 모듈 임포트 | |
import pandas as pd | |
import matplotlib.pyplot as plt | |
import seaborn as sns | |
import numpy as np | |
from scipy.stats import norm | |
from sklearn.preprocessing import StandardScaler | |
from scipy import stats | |
import warnings | |
warnings.filterwarnings('ignore') | |
%matplotlib inline | |
#데이터를 읽어온다. | |
df_train = pd.read_csv('../data/train.csv') | |
#데이터를 출력한다. | |
df_train.head() |
일단 캐글 데이터를 다운로드하고, 읽은 후 출력하면 다음과 같이 나온다.

나는 이 데이터로 몇 가지 테스트를 해본 적이 있고 LotArea 컬럼이 충분히 높은 skew를 가지고 있다고 알고 있기 때문에 해당 변수만 확인하겠다.
skewness를 확인하는 명령어와 seaborn 라이브러리를 사용해서 KDE plot을 확인해보자.
# skewness 확인 | |
df_train['LotArea'].skew() | |
# KDE plot 확인 | |
sns.distplot(df_train['LotArea']) |

한눈에 봐도 정규분포를 따르지 않는다,
skewness 값이 매우 높으며 그래프는 좌측으로 편향되어 있는 것을 확인할 수 있다.
이것을 positive skewness 혹은 left skewness라고 한다.
자 이제 skewed 데이터를 처리하는 몇 가지 방법을 살펴보자.
1. Log Transform / 로그 변환
로그 변환은 skewness(왜도)를 제거하기 위해 가장 먼저 해볼 수 있는 방법이다.
Numpy를 이용해 원하는 컬럼에 log() 함수를 호출하는 것만으로 쉽게 할 수 있다. 그런 다음 skew를 확인해보자.
# 최솟값 확인 | |
df_train['LotArea'].describe() | |
# 로그 변환 | |
df_log = np.log(df_train['LotArea']) | |
# skewness 확인 | |
print(df_log.skew()) | |
# KDE plot 출력 | |
sns.distplot(df_log); |

로그 변환 전에 describe() 함수를 통해 최솟값을 확인해주었다. 만약 0보다 작다면 (최솟값 + 1)을 모든 값에 더해주는 것이 보편적이다.

이후 로그를 취해 변환하고 skew를 확인하니 12.x 에서 -0.1x 정도로 0에 가까운 왜도(skewness)가 나온다.
성급한 결론을 내리기 전에 KDE plot도 확인해보았다.

정규분포는 아니지만 꽤나 괜찮은 그래프가 나온다.
로그 변환만 사용할 수 있는 건 아니니 다른 방법도 알아보자.
2. Square Root Transform / 루트(제곱근) 변환
Numpy의 sqrt() 함수를 호출하여 Numpy를 통해 루트 변환을 취할 수 있다. 루트 변환도 데이터의 최솟값이 0보다 커야 한다.
# 루트 변환 | |
df_root = np.sqrt(df_train['LotArea']) | |
# skewness 확인 | |
print(df_root.skew()) | |
# KDE plot 출력 | |
sns.distplot(df_root) |
왜도가 12.x에서 4.x까지 떨어졌다. 하지만 로그 변환보다는 효과가 덜한듯하다.
그래프도 확인해보자.

확실히 log보다는 효과가 덜하다.
로그 변환을 승자로 선언하기 전에 한 가지 더 살펴보자.
3. Box-Cox Transform / Box-Cox 변환
오늘 마지막으로 해 볼 변환 방법이다. 수식에 대해 깊이 파고들고 싶진 않으므로, 궁금한 사람들은 이 글을 참고하기 바란다.
다른 변환들과 마찬가지로 데이터를 변환하기 위해서는 데이터가 양수여야 한다.
scipy 라이브러리를 통해서 사용할 수 있다.
# box-cox 변환 | |
df_boxcox = pd.Series(stats.boxcox(df_train['LotArea'])[0]) | |
# skewness 확인 | |
print(df_boxcox.skew()) | |
# KDE plot 출력 | |
sns.distplot(df_boxcox); |
skew값을 확인해보자.

대박! 12.x에서 0.02까지 떨어졌다. 그래프는 어떨지 확인해보자.

log 변환과 꽤나 비슷하지만, 수치로는 Box-Cox가 더 좋다.
Negative(Right) Skewed Data는 어떻게 하지?
생각보다 간단하다. 그럴경우 Positive(Left) Skewed Data로 변형해 준후 동일한 변환을 진행해주면 된다.
어떻게 하느냐? -부호를 바꿔준 후 양수가 될 수 있게 적당한 값을 더해주자.
y'= -y - min(-y)
y'= -y - min(-y) + c(상수)
마무리하며
비대칭(skewed) 데이터는 머신러닝 모델을 엉망으로 만들 수 있다. 그렇기에 원본 데이터를 변환하는 과정이 필수적이다.
당연한 말이겠지만, 어떤 특성에 대해서 어떤 변환을 수행했는가를 기억해야 하는데, 예측을 할 때 동일한 절차를 진행해야 하기 때문에 이에 대해서 명심해야 한다.
Reference
- https://towardsdatascience.com/top-3-methods-for-handling-skewed-data-1334e0debf45
- https://www.statisticshowto.com/box-cox-transformation/
'ML | DL | Big data > Data Science' 카테고리의 다른 글
통계적 가설 검정 방법 (0) | 2023.02.28 |
---|---|
빅데이터 개요 (0) | 2023.02.28 |
불균형 클래스 분류(Imbalanced Classification)를 위한 4가지 방법 (2) | 2020.09.04 |
누락 데이터(Missing value)를 처리하는 7가지 방법 / Data Imputation (1) | 2020.08.14 |
Skew(왜도) 와 Kurtosis(첨도) - 데이터 과학에서 알아야 할 두가지 중요한 통계 용어 (0) | 2020.08.12 |
데이터 과학자가 꼭 알아야 할 5분 통계학 (0) | 2020.08.11 |
댓글