데이터 분석/머신러닝

[ML] 2. 데이터 전처리

eunnys 2023. 11. 17. 17:49

One-hot Encoding
- 입력 값으로 2차원 데이터가 필요하다.
- 인코딩 결과가 밀집행렬(Dense matrix)이기 때문에 다시 희소행렬(Parse matrix)로 변환해야 한다.
 

from sklearn.preprocessing import OneHotEncoder
import numpy as np

maker = ['Samsung', 'LG', 'Apple', 'SK']
maker = np.array(maker).reshape(-1,1) # 1차원 벡터를 2차원 행렬로 변환
print(maker)

encoder = OneHotEncoder()
encoder.fit(maker)
one_hot = encoder.transform(maker) # transform : 변환하고자하는 데이터를 넣어줌

print('원-핫 인코딩 결과(밀집행렬)')
print(one_hot)
print()
print('원-핫 인코딩 결과(희소행렬)')
print(one_hot.toarray()) # 배열로 변환
[['Samsung']
 ['LG']
 ['Apple']
 ['SK']]
원-핫 인코딩 결과(밀집행렬)
  (0, 3)	1.0
  (1, 1)	1.0
  (2, 0)	1.0
  (3, 2)	1.0

원-핫 인코딩 결과(희소행렬)
[[0. 0. 0. 1.]
 [0. 1. 0. 0.]
 [1. 0. 0. 0.]
 [0. 0. 1. 0.]]

 
   
   ▷ OneHotEncoder 데이터프레임에 적용
 

import pandas as pd

df = pd.DataFrame({'maker':['Samsung', 'LG', 'Apple', 'SK']})
encoder = OneHotEncoder()
encoded = encoder.fit_transform(df['maker'].values.reshape(-1,1)).toarray().astype(int) # 준비와 실제 변환을 동시에 수행
print(encoded)
print(np.sort(df['maker']))
df[['maker-apple', 'maker-lg', 'maker-sk', 'maker-samsung']] = encoded
df

 

 

import pandas as pd

df = pd.DataFrame({'maker':['Samsung', 'LG', 'Apple', 'SK']})
pd.get_dummies(df, columns=['maker'])


 
Label Encoding
 

from sklearn.preprocessing import LabelEncoder

language = ['Java','Python','C#','Pascal']
df = pd.DataFrame({'Language': language})
print('라벨 인코딩 전')
display(df)
encoded = LabelEncoder().fit_transform(language)
# print(encoded)
df['language'] = encoded
print('라벨 인코딩 후')
df

 

language = ['Java','Python','C#','Pascal']
df = pd.DataFrame({'language': language})

# language 값 정렬 (i는 인덱스, v는 값), v:i -> 값이 키가 되고 인덱스는 벨류
map_data = {v:i for i,v in enumerate(df['language'].sort_values())} 
df['language'] = df['language'].map(map_data)
df


 
데이터 스케일링
 
    ▷ 표준화 (Standardization)
 

from sklearn.datasets import load_iris
import pandas as pd

iris = load_iris()
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
# iris_df['target'] = iris.target # 정답
# print(iris_df['target'].unique()) # [0 1 2]
display(iris_df.head())

print('컬럼 평균')
print(iris_df.mean())

print('컬럼 분산')
print(iris_df.var())

 

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
scaler.fit(iris_df) 
iris_scaled = scaler.transform(iris_df)

# 또는 iris_scaled = scaler.fit_transform(iris_df)

iris_scaled_df = pd.DataFrame(iris_scaled, columns=iris.feature_names)
display(iris_scaled_df.head())

print('표준화된 컬럼 평균')
print(iris_scaled_df.mean())
# -1.690315e-15 : -1.69 * 10(-15승) 

print('표준화된 컬럼 분산')
print(iris_scaled_df.var())

print('표준화된 컬럼 표준편차')
print(iris_scaled_df.std())
# 평균은 0에 근사한 값, 표준편차는 1에 근사한 값

 
   
    ▷ 정규화 (Normalization)

from sklearn.preprocessing import MinMaxScaler

# 데이터를 0에서 1사이의 값으로 변환
iris_scaled = MinMaxScaler().fit_transform(iris_df)
iris_scaled_df = pd.DataFrame(iris_scaled, columns=iris.feature_names)
display(iris_scaled_df.head()) 

print('정규화된 컬럼 최소값')
print(iris_scaled_df.min())

print('정규화된 컬럼 최대값')
print(iris_scaled_df.max())


 
불균형 데이터 처리
 
    ▷ imbalanced-learn 모듈 설치
 

!pip install imbalanced-learn

!pip install imblearn

!pip install scikit-learn==1.2.2 --user
import numpy as np
import pandas as pd

from sklearn.datasets import make_classification
from collections import Counter

import matplotlib.pyplot as plt
import seaborn as sns

from imblearn.under_sampling import RandomUnderSampler, TomekLinks
from imblearn.over_sampling import RandomOverSampler, SMOTE

 
   
    ▷ 불균형 데이터 생성
 

x, y = make_classification(n_samples=10000, n_features=5, weights=[0.99], flip_y=0, random_state=10) 
# n_features는 독립변수(컬럼)의 개수, weights는 종속변수의 비율, y는 0과 1, random_state=10 seed값 고정
# flip_y : y값이 임의로 변경되는 비율 (default:0.01)

print(x[:2,:])
print(x.shape, y.shape) # x는 10000행 5열, y는 10000개
print(np.unique(y)) # [0 1]
print(Counter(y)) # {0: 9839, 1: 161}, flip_y = 0 지정하면 {0: 9900, 1: 100}
[[ 0.38201186 -0.10502417 -1.65203303 -0.0260045   0.80085356]
 [ 0.03174755  0.65503326  0.94612307  0.32881832 -0.80422025]]
(10000, 5) (10000,)
[0 1]
Counter({0: 9900, 1: 100})

 

# x는 3번째 컬럼, y는 4번째 컬럼
sns.scatterplot(x=x[:,3], y=x[:,4], hue=y)
plt.show()

 
 
Under Sampling
 
    ▷ Random Under Sampling
 

# sampling_strategy='majority' : 다수 집단에서 언더 샘플링하여 소수 집단의 수와 동일하게 맞춘다
under_sample = RandomUnderSampler(sampling_strategy='majority')
x_under, y_under = under_sample.fit_resample(x, y)
print(Counter(y_under)) # 소수 집단의 1에 맞춰 100으로 맞춰줌
Counter({0: 100, 1: 100})

 

sns.scatterplot(x=x_under[:,3], y=x_under[:,4], hue=y_under)
plt.show()

 

# sampling_strategy의 값을 0~1 사이의 값으로 지정하면 비율 값(소수집단 데이터수/다수집단 데이터 수)으로 다수 집단 데이터를 샘플링 한다. (0.4 = 100/250    250은 다수집단 수)
under_sample = RandomUnderSampler(sampling_strategy=0.4)
x_under, y_under = under_sample.fit_resample(x, y)
print(Counter(y_under)) # {0: 250, 1: 100}
Counter({0: 250, 1: 100})

 

sns.scatterplot(x=x_under[:,3], y=x_under[:,4], hue=y_under)
plt.show()

 
 
Tomek Link
<sampling strategy>
  - majority : 다수의 집단 데이터만 샘플링
  - not minority : 소수 집단을 제외한 나머지 범주 데이터를 리샘플링
  - mot majority : 다수 집단을 제외한 나머지 범주 데이터를 리샘플링
  - all : 모든 범주 데이터를 리샘플링
  - auto : not minority와 동일
 

tomek_link = TomekLinks(sampling_strategy='auto')
x_under, y_under = tomek_link.fit_resample(x, y)
print(Counter(y_under)) # {0: 9854, 1: 100}
Counter({0: 9854, 1: 100})

 

sns.scatterplot(x=x_under[:,3], y=x_under[:,4], hue=y_under)
plt.show()

 
 
Over Sampling
 
    ▷ Random Over Sampling
 

over_sample = RandomOverSampler(sampling_strategy='minority')
x_over, y_over = over_sample.fit_resample(x, y)
print(Counter(y_over)) # {0: 9900, 1: 9900}
Counter({0: 9900, 1: 9900})

 
 

# 동일 데이터에 대해 반복적으로 복제해서 만들었기 때문에 산점도가 원본 데이터와 같아 보인다
sns.scatterplot(x=x_over[:,3], y=x_over[:,4], hue=y_over)
plt.show()

 
 
    ▷ SMOTE

smote_sample = SMOTE(sampling_strategy=0.4)
x_sm, y_sm = smote_sample.fit_resample(x, y)
print(Counter(y_sm)) # {0: 9900, 1: 3960}
Counter({0: 9900, 1: 3960})

 

sns.scatterplot(x=x_sm[:,3], y=x_sm[:,4], hue=y_sm)
plt.show()

 
 
Cost Sensitive Learning
- 라벨 가중치 옵션 사용
 

from sklearn.model_selection import RepeatedStratifiedKFold, cross_val_score # 교차검증, 평가점수
from sklearn.ensemble import RandomForestClassifier

def evaluate_model(x, y, model):
    cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=3, random_state=1) # n_splits 5등분, 3회 반복
    scores = cross_val_score(model, x, y, scoring='precision', cv=cv, n_jobs=-1) # cv는 교차검증 객체,  n_jobs=-1 가동 가능한 모든 코어를 이용해서 교차검증을 수행
    return scores

model = RandomForestClassifier(n_estimators=100) # n_estimators 의사 결정나무 100개
scores = evaluate_model(x, y, model)
print(f'Mean Precision No weight: {np.mean(scores):.3f}') # 가중치를 주지 않은 상태에서의 평균 정밀도

# class_weight: 종속변수 label별 가중치
# balanced: 각 클래스에 대해 [전체 샘플 수/(클래스 수 * 클래스별 빈도)]로 계산된 가중치 부여
# 10000/(2*9900), 10000/(2*100)
weights = {0:1.0, 1:10} # 1대10 비율 가중치
model = RandomForestClassifier(n_estimators=100, class_weight=weights)#'balanced') # n_estimators 의사 결정나무 100개
scores = evaluate_model(x, y, model)
print(f'Mean Precision weight: {np.mean(scores):.3f}')
Mean Precision No weight: 0.292
Mean Precision weight: 0.329