그레이디언트 부스팅 의사 결정 나무(GBDT) 특징
신경망 특징
# 표준화
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
from sklearn.preprocessing import StandardScaler
# 학습 데이터를 기반으로 복수 열의 표준화를 정의(평균 0, 표준편차 1)
scaler = StandardScaler()
scaler.fit(train_x[num_cols])
# 표준화를 수행한 후 각 열을 치환
train_x[num_cols] = scaler.transform(train_x[num_cols])
test_x[num_cols] = scaler.transform(test_x[num_cols])
# Min-Max 스케일링
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
from sklearn.preprocessing import MinMaxScaler
# 학습 데이터를 기반으로 여러 열의 최소-최대 스케일링 정의
scaler = MinMaxScaler()
scaler.fit(train_x[num_cols])
# 정규화(0~1) 변환 후의 데이터로 각 열을 치환
train_x[num_cols] = scaler.transform(train_x[num_cols])
test_x[num_cols] = scaler.transform(test_x[num_cols])
표준화와 최소-최대 스케일링은 선형변환이므로 변수의 분포가 유동적일 뿐 형태 그 자체는 변하지 않는다. 한편으로는 비선형변환을 통해 변수의 분포형태를 바꾸는 편이 바람직한 경우도 있다.
변수의 분포는 보통 어느 한 쪽으로 지나치게 치우치지 않는 게 좋다.
로그 변환, log(x+1)변환, 절댓값 로그 변환
# 로그 변환
# -----------------------------------
x = np.array([1.0, 10.0, 100.0, 1000.0, 10000.0])
# 단순히 값에 로그를 취함
x1 = np.log(x)
# 1을 더한 뒤에 로그를 취함
x2 = np.log1p(x)
# 절댓값의 로그를 취한 후, 원래의 부호를 추가
x3 = np.sign(x) * np.log(np.abs(x))
# Box-Cox 변환
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
# 양의 정숫값만을 취하는 변수를 변환 대상으로 목록에 저장
# 또한, 결측값을 포함하는 경우는 (~(train_x[c] <= 0.0)).all() 등으로 해야 하므로 주의
pos_cols = [c for c in num_cols if (train_x[c] > 0.0).all() and (test_x[c] > 0.0).all()]
from sklearn.preprocessing import PowerTransformer
# 학습 데이터를 기반으로 복수 열의 박스-칵스 변환 정의
pt = PowerTransformer(method='box-cox')
pt.fit(train_x[pos_cols])
# 변환 후의 데이터로 각 열을 치환
train_x[pos_cols] = pt.transform(train_x[pos_cols])
test_x[pos_cols] = pt.transform(test_x[pos_cols])
# Yeo-Johnson변환
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
from sklearn.preprocessing import PowerTransformer
# 학습 데이터를 기반으로 복수 열의 여-존슨 변환 정의
pt = PowerTransformer(method='yeo-johnson')
pt.fit(train_x[num_cols])
# 변환 후의 데이터로 각 열을 치환
train_x[num_cols] = pt.transform(train_x[num_cols])
test_x[num_cols] = pt.transform(test_x[num_cols])
# binning
# -----------------------------------
x = [1, 7, 5, 4, 6, 3]
# 팬더스 라이브러리의 cut 함수로 구간분할 수행
# bin의 수를 지정할 경우
binned = pd.cut(x, 3, labels=False)
print(binned)
# [0 2 1 1 2 0] - 변환된 값은 세 구간(0, 1, 2)를 만들고 원본 x의 값이 어디에 해당되는지 나타냄
# bin의 범위를 지정할 경우(3.0 이하, 3.0보다 크고 5.0보다 이하, 5.0보다 큼)
bin_edges = [-float('inf'), 3.0, 5.0, float('inf')]
binned = pd.cut(x, bin_edges, labels=False)
print(binned)
# [0 2 1 1 2 0] - 변환된 값은 세 구간을 만들고 원본 x의 값이 어디에 해당되는지 나타냄
# 순위로 변환
# -----------------------------------
x = [10, 20, 30, 0, 40, 40]
# 팬더스의 rank 함수로 순위 변환
rank = pd.Series(x).rank()
print(rank.values)
# 시작이 1, 같은 순위가 있을 경우에는 평균 순위가 됨
# [2. 3. 4. 1. 5.5 5.5]
# 넘파이의 argsort 함수를 2회 적용하는 방법으로 순위 변환
order = np.argsort(x)
rank = np.argsort(order)
print(rank)
# 넘파이의 argsort 함수를 2회 적용하는 방법으로 순위 변환
# [1 2 3 0 4 5]
# RankGauss
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
from sklearn.preprocessing import QuantileTransformer
# 학습 데이터를 기반으로 복수 열의 RankGauss를 통한 변환 정의
transformer = QuantileTransformer(n_quantiles=100, random_state=0, output_distribution='normal')
transformer.fit(train_x[num_cols])
# 변환 후의 데이터로 각 열을 치환
train_x[num_cols] = transformer.transform(train_x[num_cols])
test_x[num_cols] = transformer.transform(test_x[num_cols])
GBDT와 같이 결정 트리에 기반을 두는 모델에서는 레이블 인코딩으로 범주형 변수를 변환하는게 가장 편리하지만, 타깃 인코딩이 더 효과적일 때가 많다. 다만 타깃 인코딩은 데이터 정보 누출의 위험이 존재한다.
그 외의 모델에서는 원-핫 인코딩이 가장 전통적인 방식이다. 신경망의 경우에는 임베딩 계층을 변수별로 구성하는게 조금 번거롭지만 어쨌든 임베딩도 유효하다. 참고링크
# one-hot encoding
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
# 학습 데이터와 테스트 데이터를 결합하여 get_dummies를 통한 원-핫 인코딩을 수행
all_x = pd.concat([train_x, test_x])
all_x = pd.get_dummies(all_x, columns=cat_cols)
# 학습 데이터와 테스트 데이터의 재분할
train_x = all_x.iloc[:train_x.shape[0], :].reset_index(drop=True)
test_x = all_x.iloc[train_x.shape[0]:, :].reset_index(drop=True)
from sklearn.preprocessing import OneHotEncoder
# OneHotEncoder로 인코딩
ohe = OneHotEncoder(sparse=False, categories='auto')
ohe.fit(train_x[cat_cols])
# 가변수의 컬럼명 생성
columns = []
for i, c in enumerate(cat_cols):
columns += [f'{c}_{v}' for v in ohe.categories_[i]]
# 생성된 가변수를 데이터 프레임으로 변환
dummy_vals_train = pd.DataFrame(ohe.transform(train_x[cat_cols]), columns=columns)
dummy_vals_test = pd.DataFrame(ohe.transform(test_x[cat_cols]), columns=columns)
# 나머지 변수와의 결합
train_x = pd.concat([train_x.drop(cat_cols, axis=1), dummy_vals_train], axis=1)
test_x = pd.concat([test_x.drop(cat_cols, axis=1), dummy_vals_test], axis=1)
# label encoding
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
from sklearn.preprocessing import LabelEncoder
# 범주형 변수를 for문 루프하여 반복적으로 레이블 인코딩 수행
for c in cat_cols:
# 학습 데이터에 근거하여 정의한 후에 데이터 변환
le = LabelEncoder()
le.fit(train_x[c])
train_x[c] = le.transform(train_x[c])
test_x[c] = le.transform(test_x[c])
# feature hashing
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
from sklearn.feature_extraction import FeatureHasher
# 범주형 변수를 반복적으로 특징 해싱 처리
for c in cat_cols:
# FeatureHasher의 사용법은 다른 encoder와 조금 달라짐
fh = FeatureHasher(n_features=5, input_type='string')
# 변수를 문자열로 변환한 후 FeatureHasher 적용
hash_train = fh.transform(train_x[[c]].astype(str).values)
hash_test = fh.transform(test_x[[c]].astype(str).values)
# 데이터 프레임으로 변환
hash_train = pd.DataFrame(hash_train.todense(), columns=[f'{c}_{i}' for i in range(5)])
hash_test = pd.DataFrame(hash_test.todense(), columns=[f'{c}_{i}' for i in range(5)])
# 원래의 데이터 프레임과 결합
train_x = pd.concat([train_x, hash_train], axis=1)
test_x = pd.concat([test_x, hash_test], axis=1)
# 원래의 범주형 변수 삭제
train_x.drop(cat_cols, axis=1, inplace=True)
test_x.drop(cat_cols, axis=1, inplace=True)
# frequency encoding
# -----------------------------------
# 데이터 읽어오기
train_x, test_x = load_data()
# -----------------------------------
# for문을 이용한 변수를 반복하여 프리퀀시 인코딩 수행
for c in cat_cols:
freq = train_x[c].value_counts()
# 카테고리 출현 횟수로 치환
train_x[c] = train_x[c].map(freq)
test_x[c] = test_x[c].map(freq)
타깃 인코딩은 매우 효과적인 특징이 될 수도 있지만 데이터에 따라서 별다른 효과가 없을 수도 있다. 특히 시계열 성향이 강한 테이터에서는 범주의 출현 빈도가 시간에 따라 변화하는 경우가 있으므로, 단순한 범주별 집계만으로는 시간적인 변화를 반영하지 못해 그리 좋은 특징이 되지 않을 때가 많다. 한편으로는 목적변수의 데이터 정보를 누출할 우려가 있으므로 주의해야 한다.
연도
월
일
연월, 월일
요일, 공휴일, 휴일
특정 기념일
시, 분, 초
시간차
참고 : 데이터가 뛰어노는 AI 놀이터, 캐글