22.11.14.
지난 주에 이어 feature engineering에 대해 배우고 있다.
non_col_list = []
for col in train.select_dtypes(include = 'O').columns:
if train[col].value_counts(1)[0] * 100 >= 90:
non_col_list.append(col)
print(non_col_list)
범주형 변수 중, 특정 범주가 90퍼센트 이상을 차지하는 변수들을 확인하였다.
해당 변수들은 예측에 도움이 되지 않을 확률이 높으므로 피쳐에서 제거하면 될 것 같다.
왜도와 첨도를 살펴보고, 분포의 그래프도 그려본다. 이를 토대로 어떻게 데이터를 사용할지 EDA를 진행한다.
왜도가 큰 값의 경우 로그 변환을 해주기도 한다.
df = df.drop(columns = isna_mean[isna_mean >= 0.80].index)
결측치의 비율이 80퍼센트 이상인 피쳐들을 삭제하였다.
실습에서 최빈값으로 채워보기도 진행했다.
df[fill_mode] = df[fill_mode].fillna(df[fill_mode].describe().loc['top'])
# 또는
df[fill_mode].fillna(df[fill_mode].mode().loc[0])
최빈값은 .mode로 판다스에서 구할 수 있다.
df[feature_num] = df[feature_num].fillna(df[feature_num].median())
df[feature_num].isnull().sum().sum()
이런 식으로 fillna를 사용하기도 했다.
fillna의 인자에 str, 수치 뿐 아니라 dictionary나 Series를 넣을 수 있었다. (함수는 X)
num_col = df.select_dtypes(include = 'number').columns
df[num_col].nunique()[df[num_col].nunique() < 15].index.tolist()
와 같은 코드로 수치형 변수 중 범주형 변수로 변환시킬 변수들을 골랐다.
그 후 .astype(str)
을 통해 범주형 변수로 변경해주었다.
만약 기술통계값을 살펴봤는데, 수치형 데이터의 범위가 0~3의 정수 처럼 개수가 적다면 이는 수치형데이터보다는 범주형 데이터에 가깝다. 이럴때에는 nunique 값으로 추가적인 확인이 필요하다.
히스토그램은 수치 데이터의 분포를 확인하기 위해 그려본다. 막대가 이산적이라면 수치데이터가 아니라 범주형 데이터임도 확인할 수 있다. 수치데이터의 경우 분포 확인, 왜도와 첨도의 대략적인 파악을 통해 데이터가 너무 한쪽에 치우쳐져 있지는 않은지 확인한다.
-> 전처리를 한다면 학습과 예측에 도움이 될만한 피쳐엔지니어링 기법이 무엇이 있을지 고민
이상치를 어떻게 처리해줘야할지?
-> 이상치를 평군이나 중앙값 등으로 대체하면 데이터에 왜곡이 될 수 있으니 주의가 필요하다. 차라리 상황에 따라서는 최댓값이나 최솟값 등으로 대체하는게 낫다.
-> 현실 세계에서 풀어야할 문제 중에는 이상치를 탐지하는 문제도 있다. 이상치가 특별한 의미를 가지고 있다거나.
희소값 : 범주형 데이터 중에서 빈도수가 적은 것.
One hot encoding시 희소값이 너무 많다면 연산에 시간이 오래걸릴 수도 있고, 과대적합이 발생할 수 있다. 따라서 희소한 값이라면 결측치 처리하거나 ‘기타’와 같이 묶어준다.
스케일링
트리계열모델에서는 스케일링이 필요없다. 그런데 로그를 취해 정규분포 형태를 띄게 해주는 것은 더 나은 성능을 내기도 한다.
스케일링은 회귀에 있어 아주 중요하다.
로그 트랜스포메이션
로그 트랜스포메이션을 먼저 한 후 스케일링해야 표준정규분포와 같은 형태가 된다.
왜 데이터를 정규분포 형태로 만들어주면 머신러닝이나 딥러닝에서 더 나은 성능을 낼까?
분포의 중심(평균) 부분에 일반적으로 더 많은 데이터가 존재하기 때문에라고 생각함
데이터가 너무 한쪽에 몰려있거나 치우쳐져 있을 때보다 고르게 분포되어 있다면 데이터의 특성을 더 고르게 학습할 수 있습니다. (강사님 답)
정의역에 음수가 있다면, 최솟값의 절댓값 + 1 만큼을 더한 후 로그를 씌워준다.
다시 돌리기 위해서는 지수함수를 씌운 후 해당 만큼을 빼준다.
그러니까 log1p(x – 최솟값) 한 후 expm1(결과값) + 최솟값 하면 된다.
이산화(범주화)
Cut과 qcut. Cut은 절대평가 qcut은 상대평가.
RFM 분석 기법에서도 종종 사용되는 방법으로 비즈니스 분석에서 다룰 예정.
Recency, frequency, monetary => 고객이 얼마나 최근에, 자주, 많이 구매했는지를 분석할 때 사용
연속된 수치 데이터를 구간화. => 머신러닝 알고리즘에 힌트를 줄 수도 있다
트리모델의 경우 너무 잘게 데이터를 나누지 않아 일반화에 도움이 될 수도 있다
나누는 기준이 중요한데, EDA를 통해 어떻게 나누는 것이 예측에 도움이 될지 확인, 나누는 기준에 따라 모델의 성능에 영향을 주게 된다
오히려 잘못나누면 모델의 성능이 떨어질 수도 있다
인코딩
pandas에서는 .cat.codes, ordianl, get_dummies, one-hot
범주형 데이터 => 수치형 데이터(0~N숫자로 바꿔준다면 Ordinal, 해당되는 것만 1로 만들어준다면 One-Hot)
LabelEncoder, OrdinalEncoder 의 입력값의 차이?
LabelEncoder 입력이 1차원 y 값, OrdinalEncoder 입력이 2차원 X값
OndHotEncoder
handle_unknown = 'ignore' 를 넣어주면, 변환 중에 알 수 없는 범주가 발생하면 이 기능에 대한 결과 원-핫 인코딩 열은 모두 0이 됩니다. 역변환에서 알 수 없는 범주는 없음으로 표시됩니다
그러니까 test를 인코딩하려는데, train에서 fit 할때에는 없던 범주가 있다면 오류가 나고, 우리의 목표는 이런건 다 결측치로 처리해버리는 거고, 그러려면 handle_unknown = ‘ignore’로 해주어야 한다!
업데이트된 버전의 infrequent_if_exist는 fit에(train에) 없던 범주에 대해 자동으로 기타처리를 해준다!
원핫인코딩 하고 난 후에는 train, test 피처의 수가 같은지 꼭! 확인
전처리, EDA, 시각화
까먹지 않게 계속 실습하려고 노력하자. 노력!