첫 번째 ML 프로젝트
AI 부트캠프를 진행하면서 내가 스스로 결정해서 하는 프로젝트가 시작되었다. Section1에서 하던 것도 물론 재밌었지만, 스스로 예측한다는 느낌보다는 주어진 데이터를 가지고 분석하고 "아마도 잘 팔릴걸요....?"하는 느낌이어서 재미가 없었다. 따라서 이번에 시작되는 프로젝트에 조금 더 신경을 써보고 싶다😝 (과제로 푼 문제의 kaggle 점수가 0.044가 나와서 그러는 거 아니다....)
문제 선택📃
Kaggle Dataset 중에서 [Hotel booking demand]
(https://www.kaggle.com/datasets/jessemostipak/hotel-booking-demand)
많이 찾아보고 흥미가 가는 것도 많았지만(예를 들면, 교통, 재정, 패션, 영화 추천, 유튜브 등, 인사) 일단 데이터 찾기가 너무 어려웠다. 데이콘 같은 곳에서 제공해주는 것도 있었지만, feature가 너무 적거나 특별하게 문제 설정하기 어려워보이거나, 아예 데이터를 크롤링 해야하는 곳도 있어서 포기했다....😢
그래서 캐글에서 지금까지 많이 사용된 데이터셋 중에 최근에 관심이 많이 간 이진분류 문제를 선택했다. 만약 시간이 남는다면 데이콘에 주차 수요 예측도 해보고 싶다. (회귀인가...?)
문제 설정🔑
어느 호텔은 예약을 취소건이 많은 것이 운영상의 큰 문제로 지적되어왔다.
이를 해결하기위해, 예약을 취소할 확률이 높은 고객을 선별해 예약을 취소 전에 예약 확인 전화를 건다던가 할인 쿠폰이나 주변 관광지에 대한 정보가 담긴 웹페이지 등을 제공함에 따라, 예약 취소를 줄이려고 한다. 그를 위한 ML 모델을 구축하자.
문제 설정은 이렇게 하였다. 실제로 EDA를 해보니, 투숙객 중 예약을 취소하는 비율은 (전체 고객 중) 37%나 되었다😮😨
저녁으로 양꼬치까지 신나게 먹고 왔으니 신나게 EDA를 진행해주었다🍗
데이터 꼼꼼히 살펴보기🔎
import pandas as pd
df = pd.read_csv('/content/drive/MyDrive/Colab Notebooks/kaggle/hotel_demand/hotel_bookings.csv')
df.shape
#(119390, 32)
df.info()
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 hotel 119390 non-null object
1 is_canceled 119390 non-null int64
2 lead_time 119390 non-null int64
3 arrival_date_year 119390 non-null int64
4 arrival_date_month 119390 non-null object
5 arrival_date_week_number 119390 non-null int64
6 arrival_date_day_of_month 119390 non-null int64
7 stays_in_weekend_nights 119390 non-null int64
8 stays_in_week_nights 119390 non-null int64
9 adults 119390 non-null int64
10 children 119386 non-null float64
11 babies 119390 non-null int64
12 meal 119390 non-null object
13 country 118902 non-null object
14 market_segment 119390 non-null object
15 distribution_channel 119390 non-null object
16 is_repeated_guest 119390 non-null int64
17 previous_cancellations 119390 non-null int64
18 previous_bookings_not_canceled 119390 non-null int64
19 reserved_room_type 119390 non-null object
20 assigned_room_type 119390 non-null object
21 booking_changes 119390 non-null int64
22 deposit_type 119390 non-null object
23 agent 103050 non-null float64
24 company 6797 non-null float64
25 days_in_waiting_list 119390 non-null int64
26 customer_type 119390 non-null object
27 adr 119390 non-null float64
28 required_car_parking_spaces 119390 non-null int64
29 total_of_special_requests 119390 non-null int64
30 reservation_status 119390 non-null object
31 reservation_status_date 119390 non-null object
dtypes: float64(4), int64(16), object(12)
중간중간에 결측치도 보이고, 데이터 타입들도 확인해주었다.
더 자세하게 살펴보았다.
[(x, df[x].isnull().sum()) for x in df.columns if df[x].isnull().any()]
#[('children', 4), ('country', 488), ('agent', 16340), ('company', 112593)]
df.nunique().sort_values()
hotel 2
is_canceled 2
is_repeated_guest 2
arrival_date_year 3
deposit_type 3
reservation_status 3
customer_type 4
required_car_parking_spaces 5
meal 5
babies 5
distribution_channel 5
children 5
total_of_special_requests 6
market_segment 8
reserved_room_type 10
arrival_date_month 12
assigned_room_type 12
adults 14
previous_cancellations 15
stays_in_weekend_nights 17
booking_changes 21
arrival_date_day_of_month 31
stays_in_week_nights 35
arrival_date_week_number 53
previous_bookings_not_canceled 73
days_in_waiting_list 128
country 177
agent 333
company 352
lead_time 479
reservation_status_date 926
adr 8879
결측치가 어디있는지 확인하고, unique값이 각 컬럼별로 몇 개나 있나 확인해보았다.
우선 각 컬럼별로 어떤 걸 의미하는지를 모르기 때문에 정리해보았다.
'hotel' : city인가 resort인가
'is_canceled' : **target값**
'lead_time' : 도착까지 남은 일수
'arrival_date_year' : 도착한 연도
'arrival_date_month' : 도착한 달
'arrival_date_week_number' : 몇 번째 주에 도착한지(1년 중에)
'arrival_date_day_of_month' : 도착한 일
'stays_in_weekend_nights' : 주말 투숙으로 예약한 건수
'stays_in_week_nights' : 주중 투숙으로 예약한 건수
'adults', 'children', 'babies', : 각각 몇 명
'meal' : 식사 예약(BB:방과 조식, HB:조식+중식or석식, FB:조식+중식+석식, SC:아무것도 예약X)
'country' : 국적
'market_segment' : 통한 대리점의 형태
'distribution_channel' : 예약한 방법(대리점 or 직접)
'is_repeated_guest' : 재방문 고객인가
'previous_cancellations': 이전 예약 건 중에 취소된 예약 건의 수
'previous_bookings_not_canceled': 이전 예약 건 중에 취소되지 않은 예약 건의 수
'reserved_room_type' : 예약한 룸타입
'assigned_room_type' : 배정받은 룸타입
'booking_changes' : 예약 변경된 횟수
'deposit_type' : 보증금 타입
'agent' : 예약한 여행사 코드
'company' : 호텔을 예약하거나 비용을 지불할 책임이 있는 회사 코드
'days_in_waiting_list' : 예약일과 확정일 사이 일수
'customer_type' : 단기투숙, 계약? 단체?
'adr' : 하루 평균 숙박 비용
'required_car_parking_spaces' : 주차 공간 예약
'total_of_special_requests' : 요청사항 갯수
'reservation_status' : 예약 상태
'reservation_status_date' : 예약한 날짜
컬럼을 살펴본 뒤에는, 결측값을 처리하고 새로운 컬럼을 만들었다.
#불필요한 행 제거
df = df[df['adr'] > 0.00]
df = df[df['adults'] != 0]
#결측값들을 처리
#children, country 결측치는 얼마 없으니 삭제
df = df.dropna(subset=['children'])
df = df.dropna(subset=['country'])
#agent, company는 예약을 direct로 하면 nan으로 처리되는 듯하니 그대로 놔두기
df.shape
#(116711, 32)
이렇게 하니 아래와 같은 shape이 되었다.
그리고 새로운 컬럼을 만들었다.
#새로운 피쳐 생성1(예약을 취소 안 하고 진짜 왔을 경우의 총 숙박객 수)
df['final_total_guests'] = df['adults'] + df['children'] + df['babies']
#예약을 취소한 사람은 final guests가 없으니까 0으로 처리
df.loc[df['is_canceled']==1, 'final_total_guests'] = 0
#새로운 피쳐 생성2(투숙객당 평균 비용)
df['price_per_guest'] = round(df['adr'] / df['final_total_guests'],4)
df.shape
#(116711, 34)
#원래 shape (119390, 32)
이것저것 구글링해서 공부하는 과정이 생각보다 시간이 많이 걸리고 나의 이 느린 타자에 답답도 했지만 그래도 순수하게 나의 힘만으로 데이터를 처리했다는 것에 의의를 두고(...!) 오늘은 이만 마치려 한다🤗🙉⭕