데이터 분석의 첫걸음인 전처리 단계에서 결측치 처리는 필수적입니다. 이 글에서는 sklearn 라이브러리를 사용한 결측치 대체 전략과 실용적인 팁을 제공합니다. 데이터 전처리를 통해 모델의 성능을 최적화하는 방법을 배워보세요.

주요내용

  • 🛠 sklearn을 사용한 데이터 전처리 기법 소개
  • 📊 결측치 처리를 위한 다양한 전략실용적인 팁 제공
  • 📈 모델 성능 최적화를 위한 전처리 모델 적용 방법 안내

단순한 케이스: 모든 특징의 타입이 같은 경우



단계별로 생각해 봅시다.

sklearn을 이용한 전처리 모델


1) 전처리 모델(Preprocessing model)을 생성

2) 생성된 모델을 train data에 대해서 fitting(각 변수의 결측을 어떻게 처리할지 계산- 방식은 세부 모델별로 상이)

3) transform으로 앞서 계산한 방식으로 train data의 결측을 처리(replace)

4) train data에 적용한 것과 동일한 전처리 모델을 test data에도 적용 (test 데이터에는 fitting이 되지 않은 것이 차이점)


image


  • 간혹 다른 분석 결과 코드를 보다 보면, 전체 데이터를 전처리 모델에 fitting하는 경우가 있다. 이는 더 나은 성능을 기대할 수 있다.

  • 하지만 이것은 test data를 알고 있는 상태에서 검증(test)를 하는 것이나 마찬가지여서 객관적인 결과라 보기 어렵다.

이 코드는 필요한 라이브러리를 불러오고, 불필요한 경고 메시지를 숨기며, 현재 작업 경로를 파일 로드 경로로 설정합니다. ospandas 라이브러리를 임포트하여 파일 시스템 작업과 데이터 처리 기능을 사용할 수 있습니다. warnings.filterwarnings(action = 'ignore')를 사용하여 경고 메시지를 무시하고, %pwd 매직 커맨드로 현재 작업 경로를 얻은 후 os.chdir() 함수를 사용하여 이 경로를 작업 디렉토리로 설정합니다.

1
2
3
4
5
6
7
8
9
import os
import pandas as pd

# 불필요한 경고 표시를 생략합니다.
import warnings
warnings.filterwarnings(action = 'ignore')

a=%pwd # 현재 작업 경로를 변수 a에 할당합니다.
os.chdir(a) # 현재 작업 경로로 파일 로드 경로를 설정합니다.

판다스의 read_csv 함수를 사용하여 “cleveland.csv” 파일을 읽고, 이를 DataFrame 객체로 변환합니다. 이 과정은 데이터 분석에서 초기 데이터 로딩 단계에 해당하며, pd는 판다스 라이브러리의 일반적인 축약형입니다.

1
2
# cleveland.csv 파일을 읽어서 DataFrame 객체로 변환합니다.
df = pd.read_csv("cleveland.csv")

본 코드는 데이터프레임 df에서 특징(features)과 라벨(labels)을 분리하는 과정을 보여줍니다. df.drop('Output', axis=1)을 사용하여 ‘Output’ 열을 제외한 모든 열을 변수 X에 할당합니다. 이는 모델 학습에 사용될 특징 집합을 의미합니다. 반면, df['Output']을 사용하여 ‘Output’ 열만을 변수 Y에 할당함으로써, 예측 대상인 라벨 집합을 구성합니다.

1
2
3
# 특징과 라벨 분리
X = df.drop('Output', axis = 1)  # 'Output' 열을 제외한 모든 열을 X에 할당
Y = df['Output']  # 'Output' 열만 Y에 할당

이 함수는 train_test_split을 사용하여 데이터를 학습 데이터와 평가 데이터로 분리합니다. train_test_split 함수는 sklearn.model_selection 모듈에 포함되어 있으며, 데이터셋을 학습용과 테스트용으로 나누는 데 사용됩니다. 이 과정에서 XY 데이터셋이 각각 학습용 입력 데이터(Train_X), 테스트용 입력 데이터(Test_X), 학습용 출력 데이터(Train_Y), 테스트용 출력 데이터(Test_Y)로 분리됩니다.

1
2
3
# 학습 데이터와 평가 데이터로 분리
from sklearn.model_selection import train_test_split
Train_X, Test_X, Train_Y, Test_Y = train_test_split(X, Y)

이 함수는 데이터 프레임 Train_X에서 결측치의 합계를 계산합니다. 결측치가 많지 않은 경우, 데이터에서 해당 결측치를 제거하는 것이 가능하지만, 도메인 지식에 따라 새로운 데이터에 결측치가 존재할 가능성을 고려해야 합니다.

1
2
3
4
# 결측치 확인
Train_X.isnull().sum()
# 결측치가 많지 않다.
# 지워도 무방한 수치이지만, 새로 들어온 데이터에 결측이 있을 수도 있다는 도메인 지식이 있다고 가정

이 코드는 데이터 프레임 Train_X 내의 모든 변수들 사이의 평균 상관 계수를 계산합니다. 상관 계수는 변수들 사이의 선형 관계의 강도를 측정하며, 이 값이 -1 혹은 1에 가까울수록 변수들 사이에 강한 상관관계가 있다는 것을 의미합니다. 계산된 평균 상관 계수가 낮다는 것은 변수들 사이에 강한 선형 관계가 없다는 것을 나타내며, 이는 변수들 간의 관계가 크지 않음을 의미합니다. 이러한 결과는 변수들 사이에 다중공선성이 낮음을 시사하며, 이 경우 변수들의 대표값을 대체하는 데 있어 문제가 없음을 판단할 수 있는 근거가 됩니다. 추가적으로, 더 확실한 다중공선성 검증을 위해 VIF(분산팽창요인) 검증을 고려할 수 있습니다.

1
2
3
4
5
6
7
# 평균 상관 계수 확인 (모든 변수가 연속형이므로 상관계수로 판단할 수 있는 것)
# -1 혹은 1에 가까울 수록 강한 상관관계를 갖는다.
# 좀 더 확실한 확인을 하고 싶다면 VIF 검증 등을 이용할 수 도 있다.

Train_X.corr().sum() / (len(Train_X.columns) - 1)

# 수치가 높지 않다고 판단 => 특징 간 관계가 크지 않음 => 대표값 대체 활용 가능 판단

대표값으로 대체 (Simpleimpute)


  • 가장 일반적으로 사용되는 결측치 대체법

  • 다만, 사용하기 부적절한 경우가 2가지 있다.

    • 1) 소수 feature에 결측이 쏠린 경우

    • 2) 특징 간 상관성 (corr)이 큰 경우

image

관련 문법 : sklearn.impute.SimpleImputer

  • 결측이 있는 변수의 대표값으로 결측을 대체하는 함수

  • 변수 타입에 따라 두 개의 대표 통계량을 같이 적용해야 할 때도 있다.

파라미터

  • strategy : 대표 통계량을 지정 (‘mean’, ‘most_frequent’, ‘median’ 등)

이 코드는 결측치를 처리하기 위해 sklearnSimpleImputer 클래스를 사용합니다. SimpleImputer는 결측치를 특정 값으로 대체하는 전처리 작업을 수행하는 데 사용됩니다. 여기서는 mean 전략을 사용하여 결측치를 각 열의 평균값으로 대체합니다. 먼저 SimpleImputer 인스턴스를 생성하고, 학습 데이터(Train_X)에 대해 fit 메소드를 호출하여 평균값을 계산합니다. 그런 다음, transform 메소드를 사용하여 학습 및 테스트 데이터(Train_X, Test_X)의 결측치를 해당 평균값으로 대체합니다. transform 메소드의 결과는 NumPy 배열이므로, 이를 다시 pandas의 DataFrame으로 변환하여 원래 데이터의 구조를 유지합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 결측치를 대체하기 위한 대표값 활용
from sklearn.impute import SimpleImputer

# SimpleImputer 인스턴스 생성(전처리 모델 생성)
# 평균값(mean)을 대표값으로 사용
SI = SimpleImputer(strategy = 'mean')

# 학습
SI.fit(Train_X)

# sklearn 인스턴스의 출력은 ndarray이므로 DataFrame으로 변환 필요
# 학습 및 테스트 데이터에 동일한 모델 적용
Train_X = pd.DataFrame(SI.transform(Train_X), columns = Train_X.columns)
Test_X = pd.DataFrame(SI.transform(Test_X), columns = Test_X.columns)

이 함수는 Train_X 데이터프레임에서 각 열별로 결측값의 총합을 계산합니다. isnull() 메서드를 사용하여 각 셀이 결측값인지 여부를 불리언 값으로 반환하고, sum() 메서드를 통해 각 열별로 True(결측값이 있는 경우)의 개수를 합산합니다. 이를 통해 데이터 전처리 단계에서 결측값을 처리하기 전에 어느 열에 결측값이 몇 개 있는지 빠르게 파악할 수 있습니다.

1
Train_X.isnull().sum() # Train_X 데이터프레임에서 결측값의 합을 계산합니다.

복잡한 케이스: 다른 타입의 특징이 있는 경우


단계별로 생각해 봅시다.

Pandas의 read_csv 함수를 사용하여 "saheart.csv" 파일을 읽고, 이를 DataFrame 객체로 변환합니다. 이 과정을 통해 데이터 분석을 위한 초기 단계인 데이터 로딩이 수행됩니다.

1
df = pd.read_csv("saheart.csv") # saheart.csv 파일을 읽어서 DataFrame으로 변환

본 코드는 데이터프레임 df에서 특정 열('Chd')을 제외한 나머지 열을 변수 X에 할당하고, 해당 열만을 변수 Y에 할당하는 과정을 수행합니다. 이는 일반적으로 머신러닝 모델에서 입력 특징(X)과 예측 대상 라벨(Y)을 분리할 때 사용되는 방식입니다.

1
2
3
# 특징과 라벨 분리
X = df.drop('Chd', axis = 1)  # 'Chd' 열을 제외한 모든 열을 X에 할당
Y = df['Chd']  # 'Chd' 열만 Y에 할당

이 함수는 train_test_split을 사용하여 데이터를 학습 데이터와 평가 데이터로 분리합니다. train_test_split 함수는 sklearn.model_selection 모듈에 포함되어 있으며, 데이터셋을 학습용과 테스트용으로 나누는 데 사용됩니다. 이 과정에서 XY 데이터셋이 입력으로 제공되며, 결과적으로 학습용 입력 데이터(Train_X), 테스트용 입력 데이터(Test_X), 학습용 출력 데이터(Train_Y), 테스트용 출력 데이터(Test_Y)로 나누어집니다.

1
2
3
# 학습 데이터와 평가 데이터로 분리
from sklearn.model_selection import train_test_split
Train_X, Test_X, Train_Y, Test_Y = train_test_split(X, Y)

이 함수는 Train_X 데이터셋에서 결측치의 합계를 계산합니다. 결측치가 많지 않아 제거해도 되지만, 새로운 데이터에 결측치가 있을 가능성을 고려하여 도메인 지식을 적용하는 것을 권장합니다.

1
2
3
4
# 결측치 확인
Train_X.isnull().sum()
# 결측치가 많지 않음
# 지워도 무방한 수치이지만, 새로 들어온 데이터에 결측이 있을 수도 있다는 도메인 지식이 있다고 가정

이 코드는 Train_X 데이터 프레임의 모든 변수 간 평균 상관 계수를 계산하여, 변수들 사이의 관계를 평가합니다. 상관 계수의 합을 변수의 수에서 1을 뺀 값으로 나누어 평균을 구합니다. 이를 통해, 변수 간의 상관 관계가 높지 않다고 판단되면, 변수들 사이에 강한 관계가 없다고 가정하고, 대표값으로 대체하는 방법이 가능하다고 결론지을 수 있습니다. 그러나, 실제 데이터 분석에서는 데이터가 연속형과 범주형이 섞여 있을 수 있으므로, 단순히 상관 계수(corr)만으로는 충분한 판단을 내릴 수 없습니다. 따라서, ANOVA 검증이나 피어슨 검정과 같은 통계적 분석을 통해 변수 간의 관계를 보다 정확하게 평가해야 합니다. 그럼에도 불구하고, 이 코드는 교육적 목적으로 단순화된 접근 방식을 사용합니다.

1
2
3
4
5
6
7
# 평균 상관 계수 확인 (주의: 모든 변수가 연속형이므로 가능한 접근)
Train_X.corr().sum() / (len(Train_X.columns) - 1)

# 수치가 높지 않다고 판단 => 특징 간 관계가 크지 않음 => 대표값 대체 활용 가능 판단
# 실제로는 데이터가 연속형 / 범주형이 섞여 있기 때문에 corr만 가지고 판단할 수는 없다.
# 통계분석(ANOVA검증, 피어슨 검정 등)을 통해 관계에 대한 판단을 해야 한다.
# 하지만 실습을 위해 일단 그냥 진행

두 개의 대표값을 동시에 적용하여 대체


단계별로 생각해 봅시다.

본 코드는 데이터셋을 범주형 변수와 연속형 변수로 분리하는 과정을 설명합니다. Famhist 변수는 범주형 변수로 분류되며, 나머지 변수들은 연속형 변수로 처리됩니다. 데이터셋을 처리할 때 대표값으로 평균과 최빈값 중 선택이 필요한 상황에서, 두 가지 방법을 모두 적용하기 위해 데이터를 분할하는 방법을 채택합니다. 이를 위해, Train_XTest_X 데이터셋에서 Famhist 변수만을 추출하여 범주형 데이터셋(Train_X_cate, Test_X_cate)을 생성하고, Famhist를 제외한 나머지 변수들로 구성된 연속형 데이터셋(Train_X_cont, Test_X_cont)을 생성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
# Famhist: 범주형 변수
# 그 외 변수: 연속형 변수

# 대표값을 평균을 사용할지, 최빈값을 사용할지 결정하기 어려우므로 둘 다 사용하기로 함
# 이를 위해 데이터를 분할해야 함

# 범주형 변수 분리
Train_X_cate = Train_X[['Famhist']]
Train_X_cont = Train_X.drop('Famhist', axis = 1)

Test_X_cate = Test_X[['Famhist']]
Test_X_cont = Test_X.drop('Famhist', axis = 1)

이 코드는 결측치를 처리하기 위해 sklearnSimpleImputer 클래스를 사용합니다. 두 가지 전략, 즉 most_frequent(최빈값)와 mean(평균값)을 사용하여 범주형 데이터와 연속형 데이터의 결측치를 각각 대체합니다. 먼저, SimpleImputer 인스턴스를 각 전략에 맞게 생성하고, 이를 훈련 데이터에 적합시킨 후(fit 메소드 사용), 변환된 데이터를 transform 메소드를 통해 얻습니다. 마지막으로, transform 메소드의 결과는 ndarray 형태이므로 이를 다시 DataFrame으로 변환하여 원래 데이터의 구조를 유지합니다. 이 과정은 훈련 데이터와 테스트 데이터 모두에 동일하게 적용됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 대표값을 활용한 결측치 대체
from sklearn.impute import SimpleImputer

# SimpleImputer 인스턴스화(전처리 모델 생성)
# 평균치와 최빈값에 대한 모델 각각 생성

SI_mode = SimpleImputer(strategy = 'most_frequent')
SI_mean = SimpleImputer(strategy = 'mean')

# 학습
SI_mode.fit(Train_X_cate)
SI_mean.fit(Train_X_cont)

# sklearn 인스턴스의 출력은 ndarray이므로 DataFrame으로 변환 필요
# Train / Test 모두에 동일한 모델 적용

# 최빈값 모델 적용
Train_X_cate = pd.DataFrame(SI_mode.transform(Train_X_cate),
                            columns = Train_X_cate.columns)

Test_X_cate = pd.DataFrame(SI_mode.transform(Test_X_cate),
                           columns = Test_X_cate.columns)

# 평균값 모델 적용
Train_X_cont = pd.DataFrame(SI_mean.transform(Train_X_cont),
                            columns = Train_X_cont.columns)

Test_X_cont = pd.DataFrame(SI_mean.transform(Test_X_cont),
                           columns = Test_X_cont.columns)

이 함수는 pd.concat을 사용하여 범주형 데이터(Train_X_cate, Test_X_cate)와 연속형 데이터(Train_X_cont, Test_X_cont)를 열 방향(axis=1)으로 병합합니다. ignore_index=False 옵션을 통해 원본 데이터의 컬럼명을 유지합니다. 이 방식은 데이터 전처리 과정에서 범주형과 연속형 변수를 분리한 뒤 다시 합칠 때 유용하게 사용됩니다.

1
2
3
4
5
# 두 데이터를 '열' 기준으로 이어 붙입니다. axis=1을 사용합니다.
# 컬럼명을 유지하기 위해 ignore_index=False를 사용합니다. 기본값입니다.

Train_X = pd.concat([Train_X_cate, Train_X_cont], axis = 1)
Test_X = pd.concat([Test_X_cate, Test_X_cont], axis = 1)

Train_X.isnull().sum() 함수는 Train_X 데이터프레임에서 각 열별로 결측치의 총합을 계산합니다. 이는 데이터 전처리 과정에서 결측치를 확인하고 처리하기 위한 중요한 단계입니다.

1
Train_X.isnull().sum() # Train_X 데이터프레임에서 결측치의 합을 계산합니다.

Tip.


  • 이진형 변수와 연속형 변수만 포함된 경우에는 SI_mean만 사용하여 결측치를 평균으로 대체한 뒤에, 이진형 변수에 대해서만 round 처리를 하면 하나의 전처리 모델만 사용할 수 있습니다.

  • 이진형 변수(0 또는 1)의 평균 = 1의 비율입니다.

  • 만약 이진형 변수의 평균이 0.4(예시)이면 -> “0이 더 많습니다”

  • 만약 이진형 변수의 평균이 0.7(예시)이면 -> “1이 더 많습니다”

  • 해당 값들을 round해주면 0.5 미만이면 0이 되고, 0.5 이상이면 1이 됩니다.

댓글남기기