현재 수치형 변수가 오른쪽으로 꼬리가 긴 right-skewed
분포를 가지고 있다. 따라서, 로그변환 방법으로 변수들을 조금 더 정규분포에 더 가깝게 만들어 비대칭성을 줄인다.
이후 정규화(Normalization)을 통해 데이터의 범위를 일정한 구간으로 변환하여 모델의 학습 속도와 성능을 높이고자 한다.
세 개의 컬럼을 곱해서 부피 컬럼을 만들고 기존 3 개의 컬럼은 드랍.
# 'product_length_cm', 'product_height_cm', 'product_width_cm' 곱해서 'volume' 컬럼 생성
merged_df_cleaned['volume'] = merged_df_cleaned['product_length_cm'] * merged_df_cleaned['product_height_cm'] * merged_df_cleaned['product_width_cm']
# 기존의 세 개의 컬럼 드랍
merged_df_cleaned = merged_df_cleaned.drop(columns=['product_length_cm', 'product_height_cm', 'product_width_cm'])
이후에 팀원들과 회의를 통해 수치형, 범주형 변수를 재정의 해줬다.
numerical_cols = ['price', 'shipping_charges', 'payment_value', 'product_weight_g', 'volume']
categorical_cols = ['order_status','customer_zip_code_prefix', 'customer_city', 'customer_state',
'order_item_id', 'payment_type', 'payment_installments', 'product_category_name']
수치형 변수 로그변환 및 정규화 수행 코드
# 수치형 변수 로그변환 함수
def log_transform(df, cols):
df_log_transformed = df[cols].apply(lambda x: np.log1p(x))
return df_log_transformed
def normalize(df, cols):
scaler = MinMaxScaler()
df_normalized = pd.DataFrame(scaler.fit_transform(df[cols]), columns=cols, index=df.index)
return df_normalized
# 로그 변환
log_transformed_df = log_transform(merged_df_cleaned, numerical_cols)
# 정규화 수행
normalized_df = normalize(log_transformed_df, numerical_cols)
# 결과를 원래 데이터프레임에 반영
merged_df_cleaned[numerical_cols] = normalized_df
스케일링 후 결과 확인
# 변수 스케일링 후 수치형 kdeplot()
plt.figure(figsize=(20, 10))
for idx, col in enumerate(numerical_cols):
plt.subplot(3, 4, idx+1)
ax = sns.kdeplot(merged_df_cleaned[col], shade=True)
plt.title(f'KDE Plot of {col}')
plt.tight_layout()
plt.show()
One-Hot-Encoding
: 각 카테고리를 0과 1로 구성된 벡터로 표현하는 기법. 카테고리의 수만큼 벡터가 생성되므로 각 카테고리가 새로운 변수가 되어 표현된다. 다만, 카테고리가 너무 많은 변수의 경우 데이터의 cardinality를 증가시켜 모델의 성능을 저하시킬 수 있다는 단점.
Label-Encoding
: n개의 범주형 데이터를 0 ~ n-1의 연속적인 수치 데이터로 표현. 데이터의 범주가 3개 이상일 때는 주의해서 사용. 라벨 인코딩은 한번 실행시킬 때 단 하나의 컬럼만 실행 가능.
우리 데이터에는 범주형 변수가 많고, 예를들어 product_category_name에는 70개의 카테고리를 가지고 있기 때문에, 원-핫 인코딩과 라벨 인코딩을 사용하는 데는 한계가 있을 것으로 사료된다. 따라서, 다음의 두 가지를 고려한 인코딩 방법을 생각해야 할 것이다.
우리의 선택 : Label-Encoding
추가적으로
region 맵핑:
# state로 5개의 지방으로 묶기
state_to_region = {
'AC': '북부 지방', 'AL': '북동부 지방', 'AP': '북부 지방', 'AM': '북부 지방',
'RR': '북부 지방', 'RO': '북부 지방', 'PA': '북부 지방', 'PB': '북동부 지방',
'MA': '북동부 지방', 'PI': '북동부 지방', 'PE': '북동부 지방', 'RN': '북동부 지방',
'CE': '북동부 지방', 'SE': '북동부 지방', 'BA': '북동부 지방', 'DF': '중서부 지방',
'TO': '북부 지방', 'GO': '중서부 지방', 'MS': '중서부 지방', 'MT': '중서부 지방',
'RJ': '남동부 지방', 'SP': '남동부 지방', 'MG': '남동부 지방', 'ES': '남동부 지방',
'RS': '남부 지방', 'SC': '남부 지방', 'PR': '남부 지방'
}
# state 컬럼을 기준으로 지방으로 변환
merged_df_cleaned['region'] = merged_df_cleaned['customer_state'].map(state_to_region)
# customer_zip_code_prefix, customer_city, customer_state 컬럼 drop
columns_to_drop = ['customer_zip_code_prefix', 'customer_city', 'customer_state']
merged_df_cleaned = merged_df_cleaned.drop(columns=columns_to_drop)
merged_df_cleaned['region'].value_counts()
region
남동부 지방 55872
남부 지방 11141
북동부 지방 5471
중서부 지방 4463
북부 지방 924
Name: count, dtype: int64
# 결과 확인했으니 이제 레이블 인코딩
label_encoder = LabelEncoder()
# 'region' 컬럼을 레이블 인코딩
merged_df_cleaned['region'] = label_encoder.fit_transform(merged_df_cleaned['region'])
merged_df_cleaned['region'].value_counts()
region
0 55872
1 11141
2 5471
4 4463
3 924
Name: count, dtype: int64
팀원들과 회의를 통해 총 카테고리를 7개로 나눠 기존의 70개의 카테고리를 줄여줬다.
product_category_group 인코딩:
# product_category_name 종류별로 묶어서 카테고리 줄이기
category_mapping = {
'가구/인테리어': [
'furniture_decor', 'furniture_living_room', 'furniture_bedroom',
'furniture_mattress_and_upholstery', 'kitchen_dining_laundry_garden_furniture',
'la_cuisine', 'flowers', 'cool_stuff', 'perfumery', 'party_supplies',
'bed_bath_table', 'market_place', 'home_construction', 'christmas_supplies'
],
'패션': [
'fashion_underwear_beach', 'fashion_bags_accessories', 'fashion_shoes',
'fashion_male_clothing', 'fashion_sport', 'fashion_childrens_clothes',
'fashio_female_clothing', 'housewares', 'watches_gifts'
],
'전자제품': [
'telephony', 'computers_accessories', 'audio', 'tablets_printing_image',
'cine_photo', 'musical_instruments', 'consoles_games', 'dvds_blu_ray',
'music', 'electronics', 'air_conditioning', 'small_appliances',
'home_appliances', 'home_appliances_2', 'small_appliances_home_oven_and_coffee',
'home_comfort_2', 'signaling_and_security', 'security_and_services',
'fixed_telephony'
],
'건설/공구': [
'construction_tools_construction', 'costruction_tools_garden',
'construction_tools_safety', 'construction_tools_lights',
'costruction_tools_tools', 'garden_tools'
],
'생활용품': [
'baby', 'diapers_and_hygiene', 'health_beauty', 'home_confort',
'luggage_accessories', 'auto', 'food', 'drinks', 'food_drink',
'sports_leisure', 'pet_shop', 'agro_industry_and_commerce'
],
'문구/사무용품': [
'stationery', 'office_furniture', 'books_technical',
'books_general_interest', 'books_imported', 'arts_and_craftmanship',
'art', 'industry_commerce_and_business'
],
'장난감': ['toys']
}
# 카테고리 매핑을 수행하는 함수
def map_category(category_name):
for main_category, subcategories in category_mapping.items():
if category_name in subcategories:
return main_category
return '기타'
# `product_category_name` 컬럼을 매핑하여 새로운 컬럼 추가
merged_df_cleaned['product_category_group'] = merged_df_cleaned['product_category_name'].apply(map_category)
# 'product_category_name' 컬럼 삭제
merged_df_cleaned = merged_df_cleaned.drop(columns='product_category_name')
merged_df_cleaned['product_category_group'].value_counts()
product_category_group
장난감 58189
생활용품 6108
가구/인테리어 4968
전자제품 4164
패션 2730
건설/공구 1107
문구/사무용품 605
Name: count, dtype: int64
# product_category_name 잘 맵핑 됐으니 이제 레이블 인코딩
label_encoder = LabelEncoder()
# 'product_category_group' 컬럼을 레이블 인코딩
merged_df_cleaned['product_category_group'] = label_encoder.fit_transform(merged_df_cleaned['product_category_group'])
# 변환된 데이터 확인
merged_df_cleaned['product_category_group'].value_counts()
product_category_group
4 58189
3 6108
0 4968
5 4164
6 2730
1 1107
2 605
Name: count, dtype: int64
# 범주형 변수들 처리 전 날짜 변수들의 dtype datetime으로 변경.
date_columns = ['order_purchase_timestamp', 'order_approved_at',
'order_delivered_timestamp', 'order_estimated_delivery_date']
for col in date_columns:
merged_df_cleaned[col] = pd.to_datetime(merged_df_cleaned[col])
merged_df_cleaned[date_columns].dtypes
order_purchase_timestamp datetime64[ns]
order_approved_at datetime64[ns]
order_delivered_timestamp datetime64[ns]
order_estimated_delivery_date datetime64[ns]
dtype: object
order_status:
payment_type:
# order_status, payment_type 인코딩
# 매핑 딕셔너리 정의
order_status_mapping = {
'delivered': 0,
'shipped': 1,
'canceled': 2,
'unavailable': 3,
'processing': 4,
'invoiced': 5,
'created': 6,
'approved': 7
}
payment_type_mapping = {
'credit_card': 0,
'wallet': 1,
'voucher': 2,
'debit_card': 3,
'not_defined': 4
}
# 직접 레이블 인코딩 수행
merged_df_cleaned['order_status_encoded'] = merged_df_cleaned['order_status'].map(order_status_mapping)
merged_df_cleaned['payment_type_encoded'] = merged_df_cleaned['payment_type'].map(payment_type_mapping)
# 원본 컬럼 제거 (선택 사항)
merged_df_cleaned = merged_df_cleaned.drop(columns=['order_status', 'payment_type'])
모든 데이터 전처리 후의 결과:
merged_df_cleaned.to_csv('merged_df_pre_processed.csv', index=False)
merged_df_cleaned.info()
<class 'pandas.core.frame.DataFrame'>
Index: 77871 entries, 0 to 119159
Data columns (total 19 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 order_id 77871 non-null object
1 customer_id 77871 non-null object
2 order_purchase_timestamp 77871 non-null datetime64[ns]
3 order_approved_at 77871 non-null datetime64[ns]
4 order_delivered_timestamp 76297 non-null datetime64[ns]
5 order_estimated_delivery_date 77871 non-null datetime64[ns]
6 order_item_id 77871 non-null int64
7 product_id 77871 non-null object
8 seller_id 77871 non-null object
9 price 77871 non-null float64
10 shipping_charges 77871 non-null float64
11 payment_installments 77871 non-null float64
12 payment_value 77871 non-null float64
13 product_weight_g 77871 non-null float64
14 volume 77871 non-null float64
15 region 77871 non-null int64
16 product_category_group 77871 non-null int64
17 order_status_encoded 77871 non-null int64
18 payment_type_encoded 77871 non-null int64
dtypes: datetime64[ns](4), float64(6), int64(5), object(4)
memory usage: 11.9+ MB
기존 데이터 119,160개 → 전처리 후 77,871개
기존 컬럼 24개 → 전처리 후 19개