[My IT : Codes] Hotel Booking Demand Datasets 분석(2)(특징 분석~결론 도출)

2026. 2. 13. 18:24·My IT/Codes

데이터셋~데이터 전처리 과정

Hotel Booking Demand Datasets 분석(1)(데이터셋 불러오기~ 이상치/결측치 처리

 

Hotel Booking Demand Datasets 분석(1)(데이터셋 불러오기~ 이상치/결측치 처리)

목표 : 데이터 셋을 분석해 호텔 예약 취소에 영향을 주는 요인들에는 무엇이 있는지 찾아보기목차1. 데이터셋 설명2. 코드 & 설명데이터셋 불러오기이상치/결측치 처리특징에 따른 취소율 분석3

uj07096.tistory.com

특징 분석

이제 결측치/이상치 처리가 완료되었으니, 테이블/시각화를 통해서 취소율과 가장 상관있는 특징을 찾는 작업을 수행하기로 했다. 특징 리스트를 보면서 취소율과 가장 관련이 있을거라고 생각했던 특징들로 분석을 실행하고 특징별 결론을 도출했는데, 그 특징들은 다음과 같다 :

  • 고객의 호텔 도착 월
  • 고객의 자녀 수
  • 고객의 예약 날짜(호텔 예약 시점 부터 고객의 호텔 도착 시점까지의 기간)
  • 주차공간수
  • 총 예약 인원
  • 총 숙박 일수
  • 호텔의 특정 방의 상태
  • 고객 유형, 예약경로

1. 고객의 호텔 도착 월

먼저 이 데이터셋은 월 별로 분류는 되어있지만, 월 순서로 배열되어있지 않기 때문에 월 순서로 재배열하기 위한 리스트를 만들었다.

month_order = [
    'January','February','March','April','May','June',
    'July','August','September','October','November','December'
] #월 순서로 재배열 하기 위한 리스트

 

이후 groupby함수를 이용해 월별로 도착하는 고객들의 취소율과 총 월별 예약 수를 파악하는 DataFrame을 완성했다.

month_group = df_mv.groupby(by='arrival_date_month')['is_canceled'].agg(
    cancel_rate = 'mean', 
    total_reservation = 'count'
)
month_group['cancel_rate'] *= 100 #퍼센트로 보기 위해서 X100
month_group = month_group.reindex(month_order, axis = 0) #월 순서로 재배열

 

month_group 프레임을 보게 되면 다음과 같다.

 

이렇게 표로 보게 되면 데이터를 파악하기 힘들기 때문에 시각화를 활용하여 월별 취소율과 총 예약 수를 나타내었는데, 월별 취소율은 plt.plot()을 이용해 선그래프로 나타내었고, 월별 총 예약 수는 plt.bar()를 이용해 막대그래프로 나타내었다.

x = month_group.index

plt.figure(figsize = (20, 10))

ax1 = plt.subplot(1,2,1) #왼쪽 표
ax1.plot(x, month_group['cancel_rate'], color = 'green')
ax1.tick_params(axis = 'x', rotation = 45)
#평균 취소율을 나타내는 선을 긋기 위한 코드
ax1.axhline((month_group['cancel_rate'].mean()), color = 'red', label = 'Average')
ax1.set_title('Monthly Cancel Rate')
ax1.legend()


ax2 = plt.subplot(1,2,2) #오른쪽 표
ax2.bar(x, month_group['total_reservation'])
ax2.tick_params(axis = 'x', rotation = 45)
#평균 예약 수을 나타내는 선을 긋기 위한 코드
ax2.axhline((month_group['total_reservation'].mean()), color = 'red', label = 'Average')
ax2.set_title('Total Reservation')
ax2.legend()
plt.show()

결과

성수기(4~10월)에 예약수&취소율이 높다

결론 : 위의 표를 보면 알 수 있듯이, 4~10월에 취소 비율이 높은 것으로 나타나는데, 총 예약 건수도 같이 높아진다. 따라서 예약 건수가 많은 성수기에 취소율이 높아지는 것을 볼 수 있는데, 보통 휴가를 받는 성수기에 여행을 예약해놓았다가, 취소되는 경우가 많아서 취소율이 높아지지 않을까 하는 생각이 든다.

 

2. 고객의 자녀 수

위의 결론에 성수기에 취소비율과 높다는 결론이 나왔는데, 성수기에는 자녀를 데리고 여행을 떠나는 사람이 많기에, 연결지어서 분석해볼까 하는 생각이 들어서 자녀수와 월별 취소율로 피벗 테이블을 만들어 분석해볼까 하는 생각이 들었다.

#자녀 수에 따른 취소율 분석
children_group = df_mv.groupby(by='children')['is_canceled'].mean() * 100
#예약 월을 index로, column을 자녀수로, value를 취소율로 취하는 피벗 테이블 생성
children_month_group = pd.pivot_table(df_mv, index = 'arrival_date_month',
                                      columns = 'children',
                                      values = 'is_canceled',
                                      aggfunc = 'mean') *100

children_month_group = children_month_group.reindex(month_order, axis = 0) #월 순서로 재배열

children_month_group을 출력하면 다음과 같은 결과가 나온다.

위와 마찬가지로 표로는 데이터를 분석하기 힘들기 때문에, 시각화하여 보기로 결정했고, 시각화를 위하여 테이블의 Nan값도 같이 처리하였다.

 

#위의 테이블에 nan값이 있어 처리
children_month_group = children_month_group.fillna(children_month_group.mean())

#heatmap으로 보기

plt.figure(figsize = (7,7))
sns.heatmap(children_month_group, cmap = 'YlGnBu', annot=True, fmt='.2f') #fmt : 소수점 둘째자리까지
plt.show()

결과

결론 : 생각했던거와는 다르게 자녀가 0명이가 2명일 경우에 예약 취소율이 조금 높게 나타나긴 하지만, 자녀의 수가 예약 취소율과 크게 상관은 없다는 결론을 도출했다.

 

3. 고객의 예약 날짜

예약일에서 숙박하는 날짜까지 기간이 멀 수록 중간에 생길 수 있는 일도 많아지고, 변심에 의한 취소도 있을 확률이 높다고 생각해 lead_time column을 이용해 호텔 예약 시점부터 고객의 호텔 도착 시점까지의 기간과 취소율의 상관관계를 분석해보기로 결정하였다. 이 경우에는 lead_time의 값이 커질수록 취소율이 높아진다면 관련이 있는 경우이기 때문에, 산점도 그래프와 상관계수를 이용하여 분석을 진행했다.

 

상관계수는 소수점 4자리까지 끊어서 Title에 보이게 하였고, 상관계수 직선이 보이게 하기 위해서 sns.regplot()을 사용했다.

#lead_time과 취소율을 groupby
lead_time_group = df_mv.groupby(by= 'lead_time')['is_canceled'].mean()*100

#상관계수 확인
#groupby의 인덱스를 가져와 series로 바꿔야 상관관계 성립이 된다.
corr_lead_time = lead_time_group.index.to_series().corr(lead_time_group) 

#산점도, 상관계수 시각화
plt.figure(figsize = (10,5))
sns.regplot(x = lead_time_group.index, y = lead_time_group.values)
plt.ylabel('cancel rate')
plt.title(f'Lead Time & Cancel Rate Correaltion(Corr Rate = {corr_lead_time:.4f})')
plt.legend()
plt.show()

결과

결론 : 특성이 꽤나 많은 것을 감안하면, 여기서 도출한 상관계수는 결코 낮지 않다고 볼 수 있고, 또한 위의 시각화된 표에서 확인할 수 있듯이 예약한 날짜가 멀수록 유의미하게 예약 취소율이 높아지는 것을 볼 수 있다. 따라서 lead_time 인자와 취소율은 비례관계에 있다고 결론을 도출했다.

 

4. 주차 공간 수 & 예약 총 인원

고객이 요구하는 주차 공간 수에 따라 예약 취소율이 달라진다면, 주차 공간의 부족으로 인해서 예약 취소가 발생하는 경우가 많다고 판단할 수 있다고 생각했고, 요구하는 주차 공간수가 많다면 한 번에 방문하는 인원의 수가 많다고도 해석할 수도 있다고 생각해서 예약 총 인원도 같이 분석하기로 결정했다.

#요구하는 주차 공간 수와 취소율 groupby
parking_group = df_mv.groupby(by = 'required_car_parking_spaces')['is_canceled'].mean()*100

결과

groupby()로 도출된 표를 본 결과, 주차 공간 예약을 안 한 사람에서만 예약 취소가 발생했는데, 차를 가져온 고객도 주차 공간 예약을 안하고 올 수도 있다고 생각되어서 크게 취소율과 관련이 있는 인자가 아니라고 판단했다.

 

예약한 총 인원수에 대한 분석도 진행했는데, 시각화는 막대그래프를 선택했다.

 

#예약 총 인원
total_guest_group = df_mv.groupby(by = 'total_guests')['is_canceled'].mean() * 100

#막대그래프 형태로 시각화
plt.figure(figsize = (10,5))
sns.barplot(x = total_guest_group.index, y = total_guest_group.values)
plt.ylabel('Cancel Rate (%)')
plt.show()

결과

총 인원 수 또한 위 그래프에서 보이듯이 사람 수가 늘어날 수록 예약 취소율이 늘어나거나 그러진 않았고, 2명과 4명일 때 조금 높아보이기는 하지만 상관을 짓기엔 무리가 있다고 생각했다.

결론 : 주차 공간수와 예약한 총 인원수는 예약 취소율과 크게 상관이 있지 않다.

 

5. 총 숙박 일수

총 숙박 일수가 많아지면 취소율과 상관이 있을까 해서 분석을 진행했고, 전에 이상치 제거하는 단계에서 상한 임계값으로 총 숙박일수 value를 처리했기 때문에, 막대그래프를 통해서 시각화했다.

 

#총 숙박일수 groupby
total_night_group = df_mv.groupby(by = 'total_nights')['is_canceled'].mean() * 100

#막대그래프 형태로 시각화
plt.figure(figsize = (10,5))
#color, alpha 인자는 색을 정하기 위한 인자
sns.barplot(x = total_night_group.index, y = total_night_group.values, color = 'red', alpha = 0.5)
plt.ylabel('Cancel Rate (%)')
plt.show()

결과

 

결론 : 위 자료에서 유의미하게 볼 수 있는 것은 1박만 하는 사람들의 예약 취소율이 낮다는 것인데, 그걸로 상관관계를 특정하기엔 무리가 있다고 생각이 들었다.

 

6. City/Resort 호텔의 방 상태

보통 호텔의 예약에는 방 컨디션을 보는게 기본이다. 따라서 어떠한 방이 배정이 되었는지, 방에 따른 예약 건수와 예약 취소율은 어떤지 보기 위해 분석을 진행했다. 호텔이 총 2개이니 당연히 방을 따로 나누어서 보았었다. 배정이 된 방을 예약 취소한다면 , 예약한 방 상태가 별로라는 리뷰를 예약 후에 봤거나, 방 가격 등의 이유가 있을 것 같다고 판단했다. 

 

호텔과 reserved된 방 타임, 예약 취소율로 다중 그룹화를 진행했고, 따로 총 예약 건수를 위한 다중 그룹화를 진행 후 merge 하여 DataFrame을 생성했다.

 

#호텔과 reserved된 방 타입, 예약 취소율로 다중 그룹화
group_room_type = df_mv.groupby(by = ['hotel', 'reserved_room_type'])['is_canceled'].mean()*100
#예약 건수로 그룹화
group_room_type_sum = df_mv.groupby(by = ['hotel', 'reserved_room_type'])['is_canceled'].count()
rooms = pd.merge(group_room_type, group_room_type_sum, on = ['hotel', 'reserved_room_type'])
rooms.columns = ['cancel_rate', 'total_reservation'] #column 이름 rename

결과

이번 데이터 분포를 보기 위해 시각화를 진행할 때에는 방 별로 취소율을 보기 위한 히트맵과, 방의 예약 건수까지 같이 볼 수 있는 산점도 그래프를 같이 그렸다. 예약 건수가 많을 수록 점의 크기가 커지도록 시각화하였다.

 

#rooms 시각화(예약 취소율 히트맵, 예약수 + 취소율 산점도 그래프)

#히트맵 위해 예약 취소율만 있는 피벗 테이블 생성
rooms_pivot = pd.pivot_table(df_mv, index = 'hotel',
                             columns = 'reserved_room_type',
                             values = 'is_canceled',
                             aggfunc = 'mean')
#Resort Hotel에 있는 방 코드가 City Hotel에 없는 경우와 그 반대의 경우, nan값이 들어가기 때문에 0으로 fillna
rooms_pivot = rooms_pivot.fillna(0)

plt.figure(figsize = (20,10))
#왼쪽 히트맵(방 코드 <-> 취소율)
ax1 = plt.subplot(1,2,1)
sns.heatmap(rooms_pivot, cmap = 'YlGnBu', annot=True, fmt='.2f')
#오른쪽 산점도 그래프(방 코드, 예약 건수 <-> 취소율)
ax2 = plt.subplot(1,2,2)
sns.scatterplot(data = rooms, x = 'reserved_room_type', y = 'cancel_rate',
                size = 'total_reservation', hue = 'hotel', sizes = (50, 1000))
ax2.axhline((rooms['cancel_rate'].mean()), color = 'red', label = 'Cancel Rate Average')
ax2.legend()
plt.show()

 

결과

결론 : 위의 표에서 볼 수 있듯이, City Hotel의 A, D room, Resort Hotel의 G, H room은 어느정도의 예약수가 있음에도 예약 취소율이 평균보다 많이 높게 나오는걸 보아 방 컨디션이 좋지 않다고 판단되고, 방을 재점검할 필요가 있어 보인다.

 

7.고객 유형(market_segment), 예약 경로(distribution_channel)

호텔 데이터에서 distribution_channel과 market_segment는 상당히 중요한 요소이다. 먼저 distribution_channel은 예약이 유입된 경로를 의미하며, 종류는 다음과 같다 :

  • Direct : 호텔 직접 예약
  • TA/TO : 여행사/투어사 통해 예약
  • Corporate : 기업 계약 채널
  • GDS : 글로벌 예약 시스템

또한 market_segment는 고객 유형을 의미하며, 고객을 특성에 따라 분류했다고 할 수 있다. 

  • Online TA : 온라인 여행사 고객
  • Offline TA/TO : 오프라인 여행사
  • Direct : 직접 예약 고객
  • Corporate : 출장/법인
  • Groups : 단체 예약
  • Complementary : 무료 객실
  • Aviation : 항공 승무원

distribution_channel이 market_segment보다 상위의 개념이라고 할 수 있기에, 이에 따라서 특징을 분석해보았다.

 

먼저 Columns에 고유값 파악을 먼저 진행하고, hotel - distribution_channel - market_segment의 취소율을 담고 있는, 즉 상위 개념에서 하위 개념으로 가는 데이터프레임을 생성했다.

 

#distribution 고유값 파악
df_mv['distribution_channel'].unique()

#maket_segment 고유값 파악
df_mv['market_segment'].unique()

결과

distribution_channel 고유값
market_segment 고유값

 

#고객유형 +  예약경로 취소율
d1 = df.groupby(['hotel', 'distribution_channel', 'market_segment'])['is_canceled'].agg(['mean' ,'count']).reset_index()
#예약 경로 별 취소율
d2 = df.groupby(['hotel', 'distribution_channel'])['is_canceled'].mean().reset_index(name = 'channel_cancel_rate')
#호텔별 취소율
d3 = df.groupby('hotel')['is_canceled'].mean().reset_index(name = 'hotel_cancel_rate')

#전체적으로 테이블을 보기 위해서 groupby한 테이블들을 merge

seg_tbl = d1.merge(d2, how = 'left', on = ['hotel', 'distribution_channel']).merge(d3, on = 'hotel', how = 'left')
seg_tbl = seg_tbl.set_index(['hotel', 'distribution_channel', 'market_segment'])
seg_tbl['mean'] = seg_tbl['mean'] * 100
seg_tbl['channel_cancel_rate'] = seg_tbl['channel_cancel_rate'] * 100
seg_tbl['hotel_cancel_rate'] = seg_tbl['hotel_cancel_rate'] * 100

#undefined 항목은 수도 적고, 데이터 판단(추후 만들 히트맵 색)에 어려움을 줄 수 있기에 삭제
seg_tbl = seg_tbl.drop('Undefined', level = 'distribution_channel')

 

이렇게 생성된 데이터프레임을 소수점 둘째 자리까지 끊어서 보게 되면 다음과 같이 나온다.

...아래는 생략

 

이후 위에서 했던 것처럼 데이터 파악을 쉽게 하기 위해서 시각화하는 작업을 진행했는데, 각각 호텔의 channel에 따른 취소율을 보는 heatmap 하나, 두 호텔 모두 channel에서 TA/TO 취소율이 제일 높게 나왔기 때문에 hotel별 TA/TO 세부 항목의 취소율을 보는 heatmap을 시각화해 총 두개의 히트맵으로 시각화했다.

 

#파악하기 쉽게 하기 위해서 시각화

#히트맵으로 시각화를 위해 피벗 테이블 생성
#hotel과 channel의 취소율 보는 pivot 생성
pivot_channel = pd.pivot_table(seg_tbl, index = 'distribution_channel',
                               columns = 'hotel',
                               values = 'channel_cancel_rate')
                               
#hotel별 TA/TO 세부 항목의 취소율 보는 pivot 생성
seg_tbl_ridx = seg_tbl.reset_index()#column 필터를 위해 reindex
pivot_market = pd.pivot_table(seg_tbl_ridx[seg_tbl_ridx['distribution_channel'] == 'TA/TO'],
                              index = 'market_segment',
                              columns = 'hotel',
                              values = 'mean')

plt.figure(figsize = (20,10))
#hotel-channel 히트맵
ax1 = plt.subplot(1,2,1)
sns.heatmap(pivot_channel, cmap = 'YlGnBu', annot=True, fmt='.2f')
ax1.set_title('Hotel_Channel Cancel Rate')

#hotel-TA/TO 히트맵
ax2 = plt.subplot(1,2,2)
sns.heatmap(pivot_market, cmap = 'flare', annot=True, fmt='.2f')
ax2.set_title('Hotel_TA/TO_Market Cancel Rate(Segments in TA/TO)')
plt.show()

결과

 

결론 : 두 호텔 모두 distribution channel에서 TA/TO 특징의 취소율이 매우 높게 나왔고, 그 중 Group(단체고객) 취소율이 높게 나온것으로 보인다. City Hotel에서는 Online과 Offline TA/TO 를 통한 예약 취소율 또한 매우 높게 나왔는데, City Hotel에서 개선해야할 필요가 보인다.

 

 

총 결론 : 개인적으로 보기에 유의미한 결과가 나왔던 부분은 고객의 호텔 도착 월(arrival_date_month), 호텔 도착 시점까지의 시간(lead_time), 방의 타입코드(reserved_room_type), 고객 유형(market_segment), 유통경로(distribution_channel)정도인데, 이 중에서 호텔이 개선할 수 있는 사항이 있을까 ? 하고 생각해 보았을 때 다음과 같은 개선점을 생각해 보았다.

  • City Hotel의 A, D Room, Resort Hotel의 G,H Room의 예약 취소율이 높게 나온걸 보아 이 방들을 우선적으로 재점검 하고, 리모델링이나 청소상태 재점검을 하는 것이 좋아보인다.
  • 두 호텔 모두 단체고객 취소율이 높게 나왔는데, 단체 고객 예약 취소율을 낮추기 위해서는 단체 고객에게 미리 계약금을 받거나, 선결제를 하는 방식으로 예약을 받는 방법을 활용하면 예약 취소율을 낮추는데 도움이 될 것 같다.
  • City Hotel의 TA/TO의 경우에는 전체적으로 취소율이 높게 나타나는데, 예약 관리 방식을 재점검할 필요가 있어보인다.

'My IT > Codes' 카테고리의 다른 글

[My IT : Codes] LSTM 활용 Air Pollution Forecast 데이터셋 예측(1) 데이터셋 소개 ~ 모델링  (1) 2026.03.25
[My IT : Codes] AutoEncoder 활용 Denoising  (0) 2026.03.23
[My IT : Codes] Bank-Marketing 데이터 분석(2) (모델 비교 학습 ~ 결론 도출)  (1) 2026.03.09
[My IT : Codes]Bank-Marketing 데이터 분석(1) (데이터셋 설명 ~ 데이터 전처리)  (2) 2026.03.07
[My IT : Codes] Hotel Booking Demand Datasets 분석(1)(데이터셋 불러오기~ 이상치/결측치 처리)  (0) 2026.02.13
'My IT/Codes' 카테고리의 다른 글
  • [My IT : Codes] AutoEncoder 활용 Denoising
  • [My IT : Codes] Bank-Marketing 데이터 분석(2) (모델 비교 학습 ~ 결론 도출)
  • [My IT : Codes]Bank-Marketing 데이터 분석(1) (데이터셋 설명 ~ 데이터 전처리)
  • [My IT : Codes] Hotel Booking Demand Datasets 분석(1)(데이터셋 불러오기~ 이상치/결측치 처리)
uj07096
uj07096
개발블로그 시작 !
  • uj07096
    김재헌 님의 블로그
    uj07096
  • 전체
    오늘
    어제
    • 분류 전체보기 N
      • Algorithm
      • My IT
        • Article
        • Codes
      • TIL
      • My Projects N
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Algorithm
    Tensor
    transfer learning
    파이썬
    EDA
    LSTM
    코딩테스트
    이상치
    convolution
    DeepLearning
    프로그래머스
    LeetCode
    autoencoder
    optuna
    Faster R-CNN
    코테
    Stack
    DenseNet
    EfficientNet
    PyTorch
    python
    til
    YOLO
    kaggle
    머신러닝
    데이터전처리
    GAN
    AI
    딥러닝
    ResNet
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
uj07096
[My IT : Codes] Hotel Booking Demand Datasets 분석(2)(특징 분석~결론 도출)
상단으로

티스토리툴바