Search

피처 생성

기존의 피처들만으로 충분한 예측력을 발휘하지 못한다면 어떻게 할까?
이럴 때 필요한 것이 ‘피처 생성’ 이다.
기존 데이터에서 새로운 의미를 찾아내거나, 데이터의 관계를 재해석해서 새로운 피처를 만드는 과정이다.

Binning

연속적인 수치 데이터를 구간별로 범주화하는 기법 데이터의 세부적인 차이를 줄이고, 주요 패턴이나 경향성을 더 명확하게 파악할 수 있도록 한다.

어떤 상황에서 Binning 이 필요한가

이상치의 영향 감소
연속형 변수인 특정 feature에 극단적인 값이 다수 포함되어 있어 모델에 큰 영향을 미칠 가능성이 높다면, binning을 통해서 이런 이상치의 영향력을 완화시킬 수 있다.
비선형 패턴 포착
피처 변수와 타겟 변수 사이에 선형적인 관계 패턴이 보이지 않고 관계가 복잡할 때, Binning 기법은 두 데이터간의 숨겨진 비선형 관계를 드러내는 데 꽤 핵심적인 수단이 될 수 있다.
모델 해석의 용이성 및 변수간 관계의 단순화

실습

‘중간 소득’ - ‘주택 가격’ , ‘주택 연식’ - ‘주택 가격’ 의 관계를 알아보자.
산점도
import pandas as pd import seaborn as sns housing_df = pd.read_csv('housing_california.csv') display(housing_df.head())
Python
복사
import matplotlib.pyplot as plt fig, ax = plt.subplots(1,2, figsize=(10,5)) # HouseAge와 target 사이의 관계 sns.scatterplot(x=housing_df['HouseAge'], y=housing_df['target'], ax=ax[0]) ax[0].set_title('주택 연식 vs 주택 가격') ax[0].set_xlabel('주택 연식') ax[0].set_ylabel('주택 가격') # MedInc와 target 사이의 관계 sns.scatterplot(x=housing_df['MedInc'], y=housing_df['target'], ax=ax[1]) ax[1].set_title('중간 소득 vs 주택 가격') ax[1].set_xlabel('중간 소득') ax[1].set_ylabel('주택 가격') # 그래프간 충돌을 방지하기 위해 자동으로 레이아웃 조정 plt.tight_layout() plt.show()
Python
복사
‘중간 소득’ 과 ‘주택 가격’ 사이에는 일정부분 선형 관계가 있음을 확인할 수 있다. 한편 ‘주택 연식’ 과 ‘주택 가격’ 사이에는 뚜렷한 패턴이 발견되지 않는다. → binning 을 통해 숨겨져 있는 패턴을 찾아볼 수 있다.
빈도 기반으로 범주화 하기 (Quantile-based Binning) - qcut
데이터를 분위수를 기준으로 동일한 빈도수를 가지는 구간으로 나누는 방법이다. 전체 데이터를 동등한 수의 관측치를 포함하는 그룹으로 나누고 싶을 때 사용된다.
데이터의 분포가 불균등할 때 유용하다
# '주택 연식' feature를 여섯개의 동일한 빈도수를 갖는 구간으로 범주화 # 등빈도 binning housing_df['HouseAge_cat'] = pd.qcut(housing_df['HouseAge'], q=6) # 생성된 범주 확인 HouseAge_cat = housing_df['HouseAge_cat'].value_counts().sort_index() display(HouseAge_cat)
Python
복사
(0.999, 16.0] 4058 (16.0, 22.0] 3080 (22.0, 29.0] 3531 (29.0, 35.0] 3627 (35.0, 42.0] 3130 (42.0, 52.0] 3214 Name: HouseAge_cat, dtype: int64
Plain Text
복사
binning 통해 생성된 변수 시각화
# HouseAge_cat 과 주택 가격간의 관계를 boxplot 으로 시각화해보기 fig, ax = plt.subplots(figsize=(8,4)) # 상자 수염 그림 생성, 축(ax) 지정 sns.boxplot(x='HouseAge_cat', y='target', data=housing_df, ax=ax) plt.show()
Python
복사
첫번째 범주에서는 다른 연령대의 집들과 크게 다르지 않게 target의 분포가 일정하게 유지가 되고 있는 것으로 보인다. 하지만, 이 범위의 이상치가 다른 범위보다 많이 보인다는 특징이 있다. 세번째 범주에서 중앙값이 증가하는 것을 볼 수 있는데, 이 전의 두 구간에 비해 target 값이 상승하는 경향이 있다는 것을 나타낸다. 다시 네번째 범주에서부터는 중앙값이 점진적으로 감소하는 경향을 보이고 있다.

다항식 피처 생성(Polynomial)

기존의 피처들을 사용해서 피처들의 고차항피처 간의 상호작용 항을 추가함으로써 새로운 피처 셋을 만드는 기법 피처들의 복잡한 패턴과 비선형 관계를 포착할 수 있는 정보를 얻을 수 있다.
import pandas as pd housing_df = pd.read_csv('californial_housing.csv') housing_df['new_HouseAge_AveRooms'] = housing_df['HouseAge'] * housing_df['AveRooms']* housing_df['AveRooms'] features_analysis = ['HouseAge', 'AveRooms', 'new_HouseAge_AveRooms', 'target'] # 상관관계 행렬 계산 correlation_matrix = housing_df[features_analysis].corr() print(correlation_matrix['target'])
Python
복사
HouseAge 0.127987 AveRooms 0.253044 new_HouseAge_AveRooms 0.311591 target 1.000000 Name: target, dtype: float64
Plain Text
복사
import matplotlib.pyplot as plt import seaborn as sns fig, axs = plt.subplots(1,2, figsize=(12,6)) sns.regplot(x='AveRooms', y='target', data=housing_df, line_kws={'color':'red'}, ax=axs[0]) axs[0].set_title('AveRooms vs.Target') axs[0].set_xlabel('AveRooms') axs[0].set_ylabel('Target') axs[0].set_xlim(0, 10) axs[0].set_ylim(0, 5.2) sns.regplot(x='new_HouseAge_AveRooms', y='target', data=housing_df, line_kws={'color':'red'}, ax=axs[1]) axs[1].set_title('new_HouseAge_AveRooms vs.Target') axs[1].set_xlabel('new_HouseAge_AveRooms') axs[1].set_ylabel('Target') axs[1].set_xlim(0, 1000) axs[1].set_ylim(0, 5.2) plt.show()
Python
복사
왼쪽 그래프의 경우에도 양의 상관관계를 보이지만, 다항식의 새로운 피처의 오른쪽 구간에서 새로운 패턴을 확인할 수 있다. → 이 구간에서는 확실히 평균 방의 개수가 많아질수록, 연식이 오래될수록 Target값도 높아지는 것으로 보인다.
AveRooms 단일 피처만으로는 포착하기 어려웠던 주택 연식과 방 개수의 상호작용 효과가 새로운 다항식 피처를 통해 나타난 것으로 보인다.

Scikit-learn 을 이용한 다항식 피처 생성

사이킷런의 PolynomialFeatures 변환기를 사용하면, 기존 피처의 다항식 조합을 쉽게 생성할 수 있다.
# 라이브러리 import from sklearn.preprocessing import PolynomialFeatures import numpy as np # 샘플 데이터 생성 X = np.arange(6).reshape(3,2) print('Original features:\n', X) # 다항식 피처 생성 poly = PolynomialFeatures(degree=2) # 2차 다항식 X_poly = poly.fit_transform(X) print('Polynomial features:\n', X_poly) # 원본 피처 + 피처의 제곱 + 상호작용항
Python
복사
피처 생성 후에는 기존 변수들과의 상관관계를 분석해 불필요한 피처들을 제거해주어야 한다. 오히려 고차원의 피처들을 사용함으로써 모델 일반화 능력에 방해가 될 수도 있다. 마지막으로는 생성된 피처들을 사용했을 때의 교차검증 성능을 비교해서 실제로 긍정적인 영향을 미치는지도 확인해 주어야 한다. 차수(degree)를 너무 높게 설정하지 않도록 주의한다.
Polynomial 실습
import pandas as pd from sklearn.preprocessing import PolynomialFeatures import numpy as np train_solar = pd.read_csv('train_solar.csv') train_solar_x = train_solar.drop(['ID', 'TARGET'], axis=1) poly = PolynomialFeatures(degree=2, include_bias=False) features_poly = poly.fit_transform(train_solar_x) # get_feature_names_out 메소드 : 생성된 다항피처들의 이름 features_names = poly.get_feature_names_out(input_features=train_solar_x.columns) # PolynomialFeatures로 생성된 피처들을 포함하는 새로운 DataFrame 생성 train_solar_poly_df = pd.DataFrame(features_poly, columns=features_names) display(features_names) display(train_solar_poly_df.head())
Python
복사
array(['DHI', 'DNI', 'WS', 'RH', 'T', 'DHI^2', 'DHI DNI', 'DHI WS', 'DHI RH', 'DHI T', 'DNI^2', 'DNI WS', 'DNI RH', 'DNI T', 'WS^2', 'WS RH', 'WS T', 'RH^2', 'RH T', 'T^2'], dtype=object)
DHI
DNI
WS
RH
T
DHI^2
DHI DNI
DHI WS
DHI RH
DHI T
DNI^2
DNI WS
DNI RH
DNI T
WS^2
WS RH
WS T
RH^2
RH T
T^2
0
0.315650
0.885087
3.129167
-0.563454
-0.106409
0.099635
0.279378
0.987722
-0.177854
-0.033588
0.783379
2.769586
-0.498706
-0.094181
9.791684
-1.763142
-0.332971
0.317481
0.059956
0.011323
1
0.075239
0.756084
3.068750
-1.030301
0.317479
0.005661
0.056887
0.230889
-0.077519
0.023887
0.571663
2.320232
-0.778994
0.240041
9.417227
-3.161736
0.974263
1.061520
-0.327099
0.100793
2
0.148714
0.628092
1.547917
-1.190128
0.411156
0.022116
0.093406
0.230197
-0.176989
0.061145
0.394500
0.972235
-0.747511
0.258244
2.396046
-1.842219
0.636435
1.416405
-0.489328
0.169049
3
0.821308
0.283800
3.020833
-0.218049
1.308111
0.674547
0.233087
2.481034
-0.179085
1.074362
0.080542
0.857311
-0.061882
0.371241
9.125434
-0.658690
3.951585
0.047545
-0.285232
1.711155
4
0.174578
0.592866
2.056250
0.286934
-0.312498
0.030477
0.103501
0.358975
0.050092
-0.054555
0.351490
1.219080
0.170113
-0.185269
4.228164
0.590009
-0.642573
0.082331
-0.089666
0.097655
생성된 피처들과 Target 의 상관 분석
train_solar_poly_df['TARGET'] = train_solar['TARGET'] correlation_matrix = train_solar_poly_df.corr() correlation_matrix['TARGET'].abs().sort_values(ascending=False)
Python
복사
TARGET 1.000000 DHI DNI 0.817730 DNI 0.726132 DNI^2 0.712234 T 0.689052 WS T 0.666005 DNI T 0.658100 DNI WS 0.554549 DHI T 0.513256 RH 0.486821 WS RH 0.479258 DNI RH 0.430429 DHI 0.346327 DHI RH 0.324267 DHI^2 0.299618 DHI WS 0.275917 WS^2 0.111976 WS 0.086688 RH^2 0.073187 T^2 0.055375 RH T 0.029028 Name: TARGET, dtype: float64
Plain Text
복사
- DNI 와 DHI 변수의 조합이 태양 에너지 출력에 중요한 비선형 영향을 미치는 것을 확인할 수 있다. → 즉, 두 변수의 상호작용이 태양 에너지 출력을 예측하는 데 핵심적인 역할을 한다는 걸 의미한다.
비선형 패턴 시각화(추가 분석)
피처 추가에 따른 모델 성능 평가(교차 검증)

피처 중요도(feature importance)

머신 러닝 모델 중에는 자체적으로 피처의 중요도를 계산하는 것들이 있다. 크게 두 가지 범주로 나눌 수 있는데, - 트리 기반 모델 - 선형 모델
트리 기반 모델 - 피처 중요도
데이터를 분류하거나 예측할 때 피처가 얼마나 유용한지를 평가
from sklearn.tree import DecisionTreeRegressor housing_df = pd.read_csv('californial_housing.csv') housing_df.head()
Python
복사
X = housing_df.drop('target', axis=1) y = housing_df['target'] model_dt = DecisionTreeRegressor(random_state=42) model_dt.fit(X, y) # 피처 중요도 추출 feature_importances_dt = model_dt.feature_importances_ display(feature_importances_dt) # feature_importances와 X.columns을 이용하여 중요도가 높은 순서로 정렬하여 출력 df_feature_importances_dt = pd.DataFrame({ 'Feature': X.columns, 'Importance': feature_importances_dt}) sorted_df_feature_importances_dt = df_feature_importances_dt.sort_values(by='Importance', ascending=False).reset_index(drop=True) display(sorted_df_feature_importances_dt)
Python
복사
array([0.47470085, 0.09667093, 0.09763334, 0.06455698, 0.06876249, 0.19767541])
Feature
Importance
0
MedInc
0.474701
1
AveOccup
0.197675
2
AveRooms
0.097633
3
HouseAge
0.096671
4
Population
0.068762
5
AveBedrms
0.064557
# 피처 중요도 시각화 import matplotlib.pyplot as plt # 피처 중요도를 수직 바 그래프로 시각화 fig, ax = plt.subplots(figsize=(8, 4)) ax.bar(X.columns, feature_importances_dt, color='skyblue') ax.set_xlabel("Feature") ax.set_ylabel("Feature Importance") ax.set_title("Feature Importance in Decision Tree Model") plt.tight_layout() # 레이블이 잘리지 않도록 조정 plt.show()
Python
복사
선형 모델 - 피처 중요도
각 피처에 할당된 가중치(회귀 계수)의 크기와 방향을 통해 중요도를 평가한다.
이때 선형 모델의 경우에는 반드시 피처 정규화를 진행해줘야 한다.
from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression # 피처 스케일링 scaler = StandardScaler() X_scaled = scaler.fit_transform(X) # 스케일링된 데이터로 선형 회귀 모델 학습 model_lr_scaled = LinearRegression() model_lr_scaled.fit(X_scaled, y) # 피처의 가중치를 피처 중요도로 사용 (스케일링된 모델) feature_importances_lr = abs(model_lr_scaled.coef_) feature_importances_lr
Python
복사
array([0.96849035, 0.23637448, 0.33845237, 0.15501547, 0.06306794, 0.26386702])
Plain Text
복사
# 시각화 import matplotlib.pyplot as plt # 피처 중요도를 수직 바 그래프로 시각화 fig, ax = plt.subplots(figsize=(8, 4)) ax.bar(X.columns, feature_importances_dt, color='skyblue') ax.set_xlabel("Feature") ax.set_ylabel("Feature Importance") ax.set_title("Feature Importance in Decision Tree Model") plt.tight_layout() # 레이블이 잘리지 않도록 조정 plt.show()
Python
복사
통계 기반 피처 중요도
피처와 타겟 변수간의 관계를 수치적으로 평가한다.
가장 흔하게 ‘상관 계수’