1. 데이터 로드
import pandas as pd
import numpy as np
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
2. 일자 관련 피처 처리 및 결측치 처리 결과 확인
- job_type 칼럼의 결측값을 문자열 'nan'으로 대체
- minus_col 리스트에 있는 피처들의 값들은 모두 음수에서 양수로 변환
- days_birth 피처를 사용해서 나이를 계산하고, 이 값을 새로운 피처 age에 저장
- job_type의 값이 'nan'이면서 income_type이 'Pensioner'인 고객들의 job_type 값을 'No job'으로 대체
- job_type 값이 'nan'인 각 행에 대해, 해당 행의 income_total 값과 각 job_type의 평균 income_total 값 사이의 차이를 계산하고, 가장 작은 차이를 가지는 job_type을 해당 행의 job_type으로 대체
# job_type 칼럼의 결측값을 문자열 'nan'으로 대체
train['job_type'] = train['job_type'].fillna('nan')
test['job_type'] = test['job_type'].fillna('nan')
# 일자 피처 처리 및 연금 수령자의 직업 유형 대체
minus_col = ['days_birth', 'days_employed', 'begin_month']
for i in minus_col:
train[i] = -train[i]
test[i] = -test[i]
# 나이 피처 생성: 출생 일자를 이용하여 나이 계산
train['age'] = train['days_birth'] // 365
test['age'] = test['days_birth'] // 365
# 연금 수령자(Pensioner)의 job_type을 'No job'으로 대체
train.loc[(train['job_type'] == 'nan') & (train['income_type'] == 'Pensioner'), 'job_type'] = 'No job'
test.loc[(test['job_type'] == 'nan') & (test['income_type'] == 'Pensioner'), 'job_type'] = 'No job'
# 등장 빈도가 낮은 job_type을 배제하기 위한 기준 값을 n으로 설정합니다.
n = 3
# 'job_type'의 각 범주별 등장 빈도를 확인합니다.
job_type_counts = train['job_type'].value_counts()
# 등장 빈도가 낮은 하위 n개의 'job_type' 범주를 선택합니다.
exclude_job_types = job_type_counts.tail(n).index.tolist()
# 결측치와 등장빈도가 낮은 job_type을 제외하고 각 job_type에 대해 'income_total'의 평균을 계산합니다.
mean_values = train[(train['job_type'] != 'nan') & (~train['job_type'].isin(exclude_job_types))].groupby('job_type')['income_total'].mean()
# train 데이터의 'job_type' 결측치 대체
# train 데이터의 job_type의 값이 'nan'인 모든 행을 순회합니다.
for idx, row in train[train['job_type'] == 'nan'].iterrows():
differences = abs(mean_values - row['income_total'])
closest_job_type = differences.idxmin()
train.at[idx, 'job_type'] = closest_job_type
# test 데이터의 'job_type' 결측치 대체 (train 데이터의 mean_values를 사용합니다.)
# test 데이터의 job_type의 값이 'nan'인 모든 행을 순회합니다.
for idx, row in test[test['job_type'] == 'nan'].iterrows():
differences = abs(mean_values - row['income_total'])
closest_job_type = differences.idxmin()
test.at[idx, 'job_type'] = closest_job_type
# 결측치 처리 후 확인
train_missing_values_after = len(train[train['job_type'] == 'nan'])
test_missing_values_after = len(test[test['job_type'] == 'nan'])
print('결측치를 처리한 후의 train 데이터의 결측치는 ', train_missing_values_after, '개 입니다.')
print('결측치를 처리한 후의 train 데이터의 결측치는 ', test_missing_values_after, '개 입니다.')
train.head()
결측치를 처리한 후의 train 데이터의 결측치는 0 개 입니다.
결측치를 처리한 후의 train 데이터의 결측치는 0 개 입니다.
3. 이상치 days_employed 값을 가진 행의 job_type 확인 및 극단값 대체
outlier_employed = train['days_employed'].min()
outlier_df = train[train['days_employed'] == outlier_employed]['job_type']
print(outlier_df.value_counts())
# days_employed가 극단치인 값을 0으로 대체합니다.
train.loc[(train['days_employed'] == outlier_employed), 'days_employed'] = 0
test.loc[(test['days_employed'] == outlier_employed), 'days_employed'] = 0
No job 4438
Name: job_type, dtype: int64
※ 결과 해석
days_employed 칼럼의 극단치에 해당하는 모든 job_type 값이 'No job'으로 확인되었습니다.
train과 test 데이터에서 income_type이 'Pensioner'에 해당하면서 job_type이 결측값인 모든 경우를 'No job'으로 수정했습니다.
이를 통해, days_employed의 극단 값은 연금 수급자를 나타내는 것으로 파악됩니다.
'Pensioner'는 데이터 수집 당시 일하고 있지 않은 경우로 규정하였으므로, days_employed의 극단값을 0으로 대체합니다.
4. 이진 범주형 칼럼 값 인코딩하기
- 이진 범주형 칼럼에서 고유값을 추출하고 이를 unique_values 변수에 저장합니다.
- enumerate() 함수를 사용하여 unique_values의 각 고유 값에 순차적인 정수 인덱스를 할당합니다.
이렇게 생성된 인덱스와 고유 값의 쌍을 {값:인덱스} 형태의 딕셔너리로 저장합니다. - replace() 메서드를 사용하여 train과 test 데이터셋의 해당 칼럼의 값들을 할당된 정수값으로 변환합니다.
binary_col = ['gender', 'car', 'reality']
for column in binary_col:
unique_values = train[column].unique()
value_mapping = {value: idx for idx, value in enumerate(unique_values)}
train[column] = train[column].replace(value_mapping)
test[column] = test[column].replace(value_mapping)
train[binary_col].head()
5. 학력 순서에 따른 범주형 인코딩
# 학력 순서 정의
edu_order = ['Lower secondary', 'Secondary / secondary special', 'Incomplete higher', 'Higher education', 'Academic degree']
# 학력 순서에 따른 인코딩 딕셔너리 생성
edu_mapping = {edu: idx for idx, edu in enumerate(edu_order)}
# 인코딩 수행
train['edu_type_encoded'] = train['edu_type'].replace(edu_mapping)
test['edu_type_encoded'] = test['edu_type'].replace(edu_mapping)
# 결과 확인
display(train[['edu_type', 'edu_type_encoded']].drop_duplicates().reset_index(drop = True))
6. 바이너리 인코딩을 통한 범주형 변수 처리
※ 바이너리 인코딩 (Binary Encoding)
바이너리 인코딩은 범주형 변수의 각 범주를 이진 코드로 변환하는 방법입니다.
이 방법은 특히 범주의 수가 많을 때 유용하며, 원-핫 인코딩보다 훨씬 적은 수의 칼럼을 생성합니다.
이는 데이터의 차원을 크게 증가시키지 않으면서도 범주형 정보를 포함합니다.
바이너리 인코딩의 기본 원리는 다음과 같습니다.
- 각 범주를 순서형 데이터로 변환합니다.
여기서 각 범주에는 고유한 정수 값이 할당됩니다. - 할당된 순서형 정수를 이진(binary) 형식으로 변환합니다.
- 변환된 이진 코드를 별도의 칼럼으로 분리하여 저장합니다.
이 방식의 주요 장점은 범주의 수에 비해 생성되는 칼럼의 수가 상대적으로 적다는 것입니다.
예를 들어, 7개의 범주를 가진 칼럼을 원-핫 인코딩하면 7개의 칼럼이 생성되지만, 바이너리 인코딩을 사용하면 단 3개의 칼럼으로 해당 정보를 표현할 수 있습니다.
(001, 010, 011, 100, 101, 110, 111)
따라서, 데이터에 선형 값이 없으면서도 범주의 수가 매우 많아지는 문제를 효율적으로 해결하고자 할 때, 바이너리 인코딩은 차원의 크기를 줄이며 또한 정보를 유지하는 효율적인 방법으로 사용될 수 있습니다.
- select_dtypes() 메서드를 사용하여 자료형이 object인 칼럼을 가져오고, 이를 categorical_cols변수에 저장해주세요.
- ce.BinaryEncoder(cols=categorical_cols) 코드를 사용하여 BinaryEncoder를 초기화하고 encoder 변수에 저장합니다.
- train 데이터에 fit_transform() 메서드를 사용하여 바이너리 인코딩을 수행합니다.
- test 데이터에 transform() 메서드를 사용하여 바이너리 인코딩을 수행합니다.
- pd.concat() 함수를 사용하여 기존 데이터와 바이너리 인코딩이 된 데이터를 결합합니다.
- 바이너리 인코딩을 적용한 칼럼을 데이터프레임에서 제거합니다.
import category_encoders as ce
print(f"job_type 칼럼의 고유값의 개수는 {train['job_type'].nunique()}개 입니다.")
# 선택할 범주형 칼럼들
categorical_cols = train.select_dtypes(include='object').columns.tolist()
# BinaryEncoder 초기화
encoder = ce.BinaryEncoder(cols=categorical_cols)
# 학습 데이터에 대해 fit_transform을 사용하여 인코딩
binary_train = encoder.fit_transform(train[categorical_cols])
# 테스트 데이터에 대해 transform을 사용하여 인코딩
binary_test = encoder.transform(test[categorical_cols])
# 인코딩된 데이터를 원래 데이터에 추가
train = pd.concat([train, binary_train], axis=1)
test = pd.concat([test, binary_test], axis=1)
# 원본 범주형 칼럼 삭제
train.drop(categorical_cols, axis=1, inplace=True)
test.drop(categorical_cols, axis=1, inplace=True)
# job_type 칼럼에 대해 몇 개의 칼럼이 생성되었는지 확인
created_job_type_num = 0
for i in train.columns:
if 'job_type' in i:
created_job_type_num += 1
print(f"job_type 칼럼에 바이너리 인코딩을 적용한 결과 {created_job_type_num}개의 칼럼이 생성되었습니다.")
train.head()
'머신러닝 > 머신러닝: 실전 프로젝트 학습' 카테고리의 다른 글
신용카드 사용자 연체 예측 프로젝트 5 : 피처 선택 및 모델 고도화 (0) | 2025.01.06 |
---|---|
신용카드 사용자 연체 예측 프로젝트 4 : 특성 공학 및 검증 점수 확인 (3) | 2025.01.06 |
신용카드 사용자 연체 예측 프로젝트 2 : 범주형 변수 EDA 및 결측값 대체 (0) | 2025.01.04 |
신용카드 사용자 연체 예측 프로젝트 1 : 데이터 분석 (0) | 2025.01.03 |
인구 소득 예측 프로젝트 7 : 성능 최적화를 위한 모델 튜닝과 테스트 데이터 예측 (0) | 2025.01.02 |