학습 내용
배달시간 예측 해보기
import pandas as pd
import numpy as np
# 전체 데이터프레임 보기
# pd.set_option('display.max_rows', None)
# pd.set_option('display.max_columns', None)
# pd.set_option('display.width', None)
# pd.set_option('display.max_colwidth', None)
# 되돌리기
# pd.reset_option('all')
raw = pd.read_csv('delivery_raw.csv', sep='\t')
# "actual_delivery_time"이 없는 값 제외
raw_dropna_label = raw.dropna(subset=["actual_delivery_time"])
# "created_at" 과 "actual_delivery_time" 컬럼을 데이트타임 속성으로 변경
raw_dropna_label['created_at'] = pd.to_datetime(raw_dropna_label['created_at'])
raw_dropna_label['actual_delivery_time'] = pd.to_datetime(raw_dropna_label['actual_delivery_time'])
#predict 속성컬럼 생성, 초 단위로 변경
raw_dropna_label["predict"] = raw_dropna_label['actual_delivery_time'] - raw_dropna_label['created_at']
raw_dropna_label['predict'] = raw_dropna_label['predict'].dt.total_seconds()
# 'created_at' 주문 일자의 시간만 추출하여 hour 생성
# 'created_at' 주문 일자의 요일만 추출하여 day 생성 (0=월요일, 6= 일요일)
raw_dropna_label['hour'] = raw_dropna_label['created_at'].dt.hour
raw_dropna_label['day'] = raw_dropna_label['created_at'].dt.dayofweek
# 학습데이터, 테스트 데이터 1:9로 나누기
from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(raw_dropna_label, test_size=0.1, random_state=42)
# 인덱스 초기화
train_set = train_set.reset_index(drop=True)
test_set = test_set.reset_index(drop=True)
# 오브젝트값, 숫자 값
objects = ["store_primary_category", "created_at", "actual_delivery_time", "predict"]
nums = ['market_id', 'store_id', 'order_protocol', 'total_items', 'subtotal',
'num_distinct_items', 'min_item_price', 'max_item_price',
'total_onshift', 'total_busy', 'total_outstanding_orders',
'estimated_order_place_duration',
'estimated_store_to_consumer_driving_duration', 'hour', 'day']
# null인 모든 행 제거(데이터가 충분히 크다고 판단)
train_set = raw_dropna_label.dropna().reset_index(drop=True)
# 마이너스인 값 확인
negative_values = train_set[nums][(train_set[nums] < 0).any(axis=1)]
# 이상치 마이너스 값 제거
train_set = train_set.drop(negative_values.index).reset_index(drop=True)
# 목표: 음식배달에 걸리는 시간 예측하기
# • 왜 필요한가?
# • 배달시간을 정확하게 예측하는 것은 사용자의 경험에 많은 영향을 미치게 됨
# • 예측된 배달시간보다 실제 배달시간이 더 걸린 경우(under-prediction)가 반대의 경우(over-prediction)보다 두 배로
# 사용자의 경험에 안 좋은 영향을 줌
# • 가능한 실제 배달시간과 가까운 값을 예측하되 동시에 under-prediction을 최소화하는 것이 좋은 예측모델 (예, 모델이
# 30분을 예측하고 실제 배달은 29분에 도착)
# • 배달시간에 영향을 주는 요소들
# • 주문시간 (오후 7시 vs. 오후 4시)
# • 지역 (번화가 vs. 외곽지역)
# • 식당의 속성 (음식의 카테고리)
# • 주문의 속성 (아이템 개수)
# 실습문제 소개
# Copyright 2023. Grepp inc. All rights reserved.
# • 이 문제를 해결하기 위해 머신러닝이 필요한 이유?
# • 머신러닝: 과거에 관찰했던 데이터에서 패턴을 학습해서 현재(미래)의 데이터에 적용시키는 것
# • (주문시간, 지역, 식당속성, 주문속성) => 실제 배달시간
# • x => y
# • f(x) ~ y
# 6
# 음식배달 예측문제 데이터 살펴보기
# Copyright 2023. Grepp inc. All rights reserved.
# • 위 속성들 중 actual_delivery_time을 제외한 모든 속성들을 모델의 입력으로 사용할 수 있다. 모델이 예측해야하는 값은
# actual_delivery_time과 created_at을 사용해서 생성하면 된다 (초단위로 표현된 두 속성의 차이값).
# • 주어진 이 속성들 외에 이것들로부터 파생될 수 있는 속성들을 추가로 만들어서 사용할 수도 있다
# • 학습/테스트 데이터 구분
# • 위 데이터(delivery_raw.csv)에서 랜덤하게 10%를 추출해서 테스트 데이터로 사용하고 나머지는 학습데이터로
# 사용할 것
# 7
# 음식배달 예측문제 데이터 살펴보기
# Copyright 2023. Grepp inc. All rights reserved.
# • 최종 결과물
# • 간단한 요약문 (3페이지 이내)
# • 데이터 전처리와 속성 생성에 대한 간단한 설명
# • 학습을 위해서 어떤 모델을 사용했는지 그리고 어떠한 손실함수를 사용했는지를 간단히 설명
# • 테스트 데이터에 대한 평가지표들 (아래 두가지를 반드시 포함할 것)
# • Root Mean Square Error (RMSE)
# • Under-prediction의 비율 (under-prediction 개수 / 테스트 데이터의 샘플수)
# • 모델 학습에 사용한 Jupyter notebook 파일
train_set.info()
# 0 market_id 52591 non-null float64 지역(배달이 이루어지는 도시) 아이디
# 1 created_at 52869 non-null object 주문이 생성된 시간의 Timestamp(UTC) --- 예측 값에 사용 - created_at
# 2 actual_delivery_time 52866 non-null object 주문자가 배달을 받은 시간의 Timestamp(UTC) --- 예측 값에 사용
# 3 store_id 52868 non-null float64 식당 아이디
# 4 store_primary_category 51595 non-null object 식당의 카테고리(italian, asian 등)
# 5 order_protocol 52597 non-null float64 주문을 받을 수 있는 방식을 나타내는 아이디
# 6 total_items 52868 non-null float64
# 7 subtotal 52868 non-null float64
# 8 num_distinct_items 52868 non-null float64
# 9 min_item_price 52868 non-null float64
# 10 max_item_price 52868 non-null float64
# 11 total_onshift 48373 non-null float64 주문이 생성되었을 때 가게로부터 10마일 이내에 있는 배달원들의 수
# 12 total_busy 48373 non-null float64 위 배달원들 중 주문에 관여하고 있는 사람들의 수
# 13 total_outstanding_orders 48373 non-null float64 주문한 가게로부터 10마일 이내에 있는 다른 주문들의 수
# 14 estimated_order_place_duration 52868 non-null float64 식당이 주문을 받을 때까지 걸릴 것으로 예상되는 시간 (초단위)
# 15 estimated_store_to_consumer_driving_duration 52733 non-null float64 식당에서 출발해 주문자에 도착할 때까지 걸릴 것으로 예측되는 시간 (초단위)
print(len(nums), len(objects), len(train_set.columns))
train_set.isnull().sum()
print(train_set)
## 데이터 값, 종류 확인
for i in objects:
print(train_set[i].value_counts())
print('-'*20)
for i in nums:
print(train_set[i].value_counts())
print('-'*20)
# "actual_delivery_time", "created_at"는 데이트타임이면서 상관관계가 적기때문에 삭제
# 요일과 시간대 속성은 이미 초기에 생성했음
train_set.drop(["actual_delivery_time", "created_at"], axis=1, inplace=True)
test_set.drop(["actual_delivery_time", "created_at"], axis=1, inplace=True)
cols = ['market_id', 'store_id', 'order_protocol', 'total_items', 'subtotal',
'num_distinct_items', 'min_item_price', 'max_item_price',
'total_onshift', 'total_busy', 'total_outstanding_orders',
'estimated_order_place_duration',
'estimated_store_to_consumer_driving_duration', 'hour', 'day', "predict"]
# 상관관계 확인
train_set[cols].corr()['predict'].sort_values(ascending=False)
train_set[cols].corr()['predict'].abs().sort_values(ascending=False)
train_set
# LabelEncoder: 주어진 순서가 있는(ordinal) 데이터를 인코딩할 때 사용.
# OrdinalEncoder: 크기에 상관있는 텍스트 데이터를 인코딩할 때 사용.
# OnehotEncdoer : 순서, 크기에 상관이 없는 텍스트 데이터를 인코딩할 때 사용
from sklearn.preprocessing import OrdinalEncoder
oe = OrdinalEncoder()
ob = ["store_primary_category"]
combined_data = pd.concat([train_set, test_set], ignore_index=True)
for i in ob :
oe.fit(combined_data[[i]])
train_set[[i]] = oe.transform(train_set[[i]])
test_set[[i]] = oe.transform(test_set[[i]])
# #원핫 인코더
# import pandas as pd
# # 합쳐진 데이터 프레임을 준비합니다.
# combined_data = pd.concat([train_set, test_set], ignore_index=True)
# # store_primary_category 열을 원핫 인코딩합니다.
# combined_data = pd.get_dummies(combined_data, columns=["store_primary_category"])
# # 다시 train_set과 test_set으로 분리합니다.
# train_set = combined_data.iloc[:len(train_set)]
# test_set = combined_data.iloc[len(train_set):]
test_set
# StandardScaler사용할 컬럼
# : [total_items, subtotal, num_distinct_items, min_item_price, max_item_price, total_onshift, total_busy, total_outstanding_orders, estimated_order_place_duration, estimated_store_to_consumer_driving_duration]
s = ["total_items", "subtotal", "num_distinct_items", "min_item_price", "max_item_price", "total_onshift", "total_busy", "total_outstanding_orders", "estimated_order_place_duration", "estimated_store_to_consumer_driving_duration"
]
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
for i in s:
ss.fit(train_set[[i]])
train_set[[i]] = ss.transform(train_set[[i]])
test_set[[i]] = ss.transform(test_set[[i]])
# 훈련/정답 데이터 나누기
# y_tr 이 예측할 값 - "predict"
x_tr = train_set.drop("predict", axis=1)
y_tr = train_set['predict']
x_val = test_set.drop("predict", axis=1)
y_val = test_set['predict']
# 모델훈련 (xgboost 사용)
from xgboost import XGBRegressor
model = XGBRegressor()
model.fit(x_tr,y_tr)
# 예측 값
pred_tr = model.predict(x_tr)
# 변수의 중요도 확인
cols_importance = pd.DataFrame({'feature':x_tr.columns, 'xgb':model.feature_importances_})
cols_importance
# 회귀 모델의 예측 성능을 평가
# MSE, MAE, r2_score, RMSE, MAPE
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import r2_score
from sklearn.metrics import mean_squared_error
# 파라미터 설정전 기본 모델 평가 값
mse = mean_squared_error(y_tr, pred_tr)
mae = mean_absolute_error(y_tr, pred_tr)
r2 = r2_score(y_tr, pred_tr)
rmse = np.sqrt(mean_squared_error(y_tr, pred_tr))
print(f'Mean Squared Error (MSE): {mse}\n'
f'Mean Absolute Error (MAE): {mae}\n'
f'R^2 Score: {r2}\n'
f'Root Mean Squared Error (RMSE): {rmse}\n'
)
# #GridSearchCV를 사용한 파라미터 설정
# from sklearn.model_selection import GridSearchCV
# param_grid = {
# 'learning_rate': [0.1, 0.15, 0.2], # 학습률
# 'max_depth': [5, 7, 9], # 트리의 최대 깊이
# 'n_estimators': [100, 200, 300], # 트리의 개수
# # 'min_child_weight': [1, 3, 5], # 각 잎사귀 노드에서 추가적으로 필요한 가중치
# # 'gamma': [0, 0.1, 0.2], # 트리의 가지치기에 필요한 최소 손실 감소
# # 'subsample': [0.6, 0.8, 1.0], # 각 트리에서 사용할 행 샘플링 비율
# # 'colsample_bytree': [0.6, 0.8, 1.0], # 각 트리에서 사용할 열 샘플링 비율
# # 'reg_alpha': [0, 0.1, 0.5], # L1 정규화 텀의 파라미터
# # 'reg_lambda': [0, 0.1, 0.5] # L2 정규화 텀의 파라미터
# }
# grid_search = GridSearchCV(estimator=model, param_grid=param_grid, scoring='neg_root_mean_squared_error', n_jobs=-1, verbose=1)
# grid_search.fit(x_tr, y_tr)
# print("최적 하이퍼파라미터:", grid_search.best_params_)
# print("최고 성능 (RMSE):", -grid_search.best_score_)
#파라미터 수정후 final_model
#손실 함수로 mse 사용
# optimal_params = grid_search.best_params_
optimal_params = {'learning_rate': 0.2, 'max_depth': 9, 'n_estimators': 300}
final_model = XGBRegressor(objective='reg:squarederror', **optimal_params)
final_model.fit(x_tr,y_tr)
# 하이퍼파라미터 설정 전과 성능 비교
final_pred_tr = final_model.predict(x_tr)
final_mse = mean_squared_error(y_tr, final_pred_tr)
final_mae = mean_absolute_error(y_tr, final_pred_tr)
final_r2 = r2_score(y_tr, final_pred_tr)
final_rmse = np.sqrt(mean_squared_error(y_tr, final_pred_tr))
print(f'Mean Squared Error (MSE): {mse}, {final_mse} \n'
f'Mean Absolute Error (MAE): {mae}, {final_mae} \n'
f'R^2 Score: {r2}, {final_r2} \n'
f'Root Mean Squared Error (RMSE): {rmse}, {final_rmse} \n'
)
# 테스트 데이터셋 비교
final_pred_val = final_model.predict(x_val)
final_mse = mean_squared_error(y_val, final_pred_val)
final_mae = mean_absolute_error(y_val, final_pred_val)
final_r2 = r2_score(y_val, final_pred_val)
final_rmse = np.sqrt(mean_squared_error(y_val, final_pred_val))
print(f'Mean Squared Error (MSE): {final_mse} \n'
f'Mean Absolute Error (MAE): {final_mae} \n'
f'R^2 Score: {final_r2} \n'
f'Root Mean Squared Error (RMSE): {final_rmse} \n'
)
# 테스트 데이터 예측치 비교
# 초단위/ 절대값 초단위/ 분 단위(반 올림)
df = pd.DataFrame({
'y_val': y_val,
'final_pred_val': final_pred_val
})
df['diff_second'] = round(df['y_val'] - df['final_pred_val'],0)
df['diff_abs_second'] = abs(df['diff_second'])
df['diff_abs_minute'] = round(df['diff_abs_second']/60,0)
# Under-prediction 개수 계산
df['under_prediction'] = df['y_val']>df['final_pred_val']
under_prediction_count = df['under_prediction'].sum()
total_samples = len(df['y_val'])
under_prediction_ratio = under_prediction_count/total_samples
print(under_prediction_ratio)
print(f"Under-prediction 비율: {under_prediction_ratio:.2f}")
print(df)
'데브코스' 카테고리의 다른 글
데이터 엔지니어링 72일차 TIL (0) | 2024.07.09 |
---|---|
데이터 엔지니어링 71일차 TIL (0) | 2024.07.08 |
데이터 엔지니어링 69일차 TIL (1) | 2024.07.04 |
데이터 엔지니어링 68일차 TIL (1) | 2024.07.03 |
데이터 엔지니어링 67일차 TIL (1) | 2024.07.02 |