Task : 가설 설정
테이블
우수 숙소와 그렇지 않은 숙소의 비교
가설 설정 1 : 우수 숙소는 비인기 숙소에 비해 ‘월별 예상 매출’이 높을 것이다.
귀무가설 (H₀)
상위 25% 숙소(우수 숙소)의 월 예상 매출과 하위 25% 숙소(비우수 숙소)의 월 예상 매출은 같다.
대립가설 (H₁)
상위 25% 숙소(우수 숙소)의 월 예상 매출과 하위 25% 숙소(비우수 숙소)의 월 예상 매출은 같다.
•
expected_monthly_revenue 등 컬럼 활용
•
우수 숙소와 비인기 숙소의 비교 필요성 느낌!
•
예외상황이 있는지 확인하고 싶음!
두 집단을 추가로 비교 분석할 수 있는 컬럼
•
숙소 위치 (지역, 세부 지역, 도심/외곽)
•
공간 유형
•
요구되는 최소 숙박 일수
•
전체 리뷰 수, 평균 한달 리뷰 수, 최근 리뷰 날짜
•
호스트 리스팅 개수
•
예약 가능 일수
•
숙소 운영 일수
가설 검증
상위 숙소랑 하위 숙소는 한 달 매출에서 차이가 많이 날까? 얼마나 날까?
combinations = [
('도심', 'Entire home/apt'),
('도심', 'Private room'),
('도심', 'Shared room'),
('외곽', 'Entire home/apt'),
('외곽', 'Private room'),
('외곽', 'Shared room'),
]
results = []
for city_and_suburb, room in combinations:
subset = df_filtered[
(df_filtered['city_and_suburb'] == city_and_suburb) &
(df_filtered['room_type'] == room)
]
# 조합별 25%, 75% 사분위수
q1 = subset['popularity_score'].quantile(0.25)
q3 = subset['popularity_score'].quantile(0.75)
# 상위/하위 25%
upper = subset[subset['popularity_score'] >= q3]['expected_monthly_revenue']
lower = subset[subset['popularity_score'] <= q1]['expected_monthly_revenue']
# 결과 저장
results.append({
'조합': f"{city_and_suburb} & {room}",
'전체 수': len(subset),
'상위 25% 수': len(upper),
'하위 25% 수': len(lower),
'상위 25% 월 매출 중앙값': round(upper.median(), 2),
'하위 25% 월 매출 중앙값': round(lower.median(), 2),
'상위 25% 월 매출 평균': round(upper.mean(), 2),
'하위 25% 월 매출 평균': round(lower.mean(), 2)
})
expected_monthly_revenue_25 = pd.DataFrame(results)
expected_monthly_revenue_25
Python
복사
그래프로 한 번 ‘중앙값’을 시각화 해볼까?
plt.figure(figsize=(10, 6))
# 데이터를 long-format으로 만들어야 seaborn에서 그리기 좋음
melted = expected_monthly_revenue_25.melt(
id_vars='조합',
value_vars=['상위 25% 월 매출 중앙값', '하위 25% 월 매출 중앙값'],
var_name='그룹',
value_name='중앙값'
)
sns.barplot(data=melted, x='조합', y='중앙값', hue='그룹')
plt.xticks(rotation=45)
plt.title('조합별 상위/하위 25% 월 매출 중앙값 비교')
plt.ylabel('월 매출 중앙값')
plt.xlabel('')
plt.tight_layout()
plt.show()
Python
복사
평균이랑 같이 보고 싶은데?
melted = expected_monthly_revenue_25.melt(
id_vars='조합',
value_vars=['상위 25% 월 매출 중앙값', '하위 25% 월 매출 중앙값', '상위 25% 월 매출 평균', '하위 25% 월 매출 평균'],
var_name='그룹',
value_name='월 매출'
)
# 지표종류 파생
melted['지표'] = melted['그룹'].apply(lambda x: '평균' if '평균' in x else '중앙값')
melted['상하위'] = melted['그룹'].apply(lambda x: '상위' if '상위' in x else '하위')
plt.figure(figsize=(12, 6))
sns.lineplot(
data=melted,
x='조합',
y='월 매출',
hue='상하위',
style='지표',
markers=True
)
plt.xticks(rotation=45)
plt.title('상위/하위 25% 월 매출 (평균/중앙값)')
plt.ylabel('예상 월 매출')
plt.xlabel('')
plt.tight_layout()
plt.show()
Python
복사
그냥 봐도 차이가 나긴 하지만 한 번 검증을 해볼까?
from scipy.stats import shapiro, levene, ttest_ind, mannwhitneyu
combinations = [
('도심', 'Entire home/apt'),
('도심', 'Private room'),
('도심', 'Shared room'),
('외곽', 'Entire home/apt'),
('외곽', 'Private room'),
('외곽', 'Shared room'),
]
results = []
for city_and_suburb, room in combinations:
subset = df_filtered[
(df_filtered['city_and_suburb'] == city_and_suburb) &
(df_filtered['room_type'] == room)
]
# 조합별 25%, 75% 사분위수
q1 = subset['popularity_score'].quantile(0.25)
q3 = subset['popularity_score'].quantile(0.75)
# 상위/하위 25%
upper = subset[subset['popularity_score'] >= q3]['expected_monthly_revenue']
lower = subset[subset['popularity_score'] <= q1]['expected_monthly_revenue']
# 정규성
s_upper = shapiro(upper)
s_lower = shapiro(lower)
# 등분산성
lev = levene(upper, lower)
# 검정
if s_upper.pvalue > 0.05 and s_lower.pvalue > 0.05:
test_type = 't-test'
t_stat, p_val = ttest_ind(upper, lower, equal_var=(lev.pvalue > 0.05))
else:
test_type = 'Mann-Whitney'
u_stat, p_val = mannwhitneyu(upper, lower, alternative='greater')
# 저장
results.append({
'조합': f"{city_and_suburb} & {room}",
'전체 수': len(subset),
'상위 수': len(upper),
'하위 수': len(lower),
'상위 평균': round(upper.mean(), 2),
'하위 평균': round(lower.mean(), 2),
'상위 중앙값': round(upper.median(), 2),
'하위 중앙값': round(lower.median(), 2),
'정규성 상위 p': round(s_upper.pvalue, 4),
'정규성 하위 p': round(s_lower.pvalue, 4),
'등분산 p': round(lev.pvalue, 4),
'검정': test_type,
'p-value': round(p_val, 5)
})
test_result = pd.DataFrame(results)
test_result
Python
복사
p-value가 유의수준인 0.05보다 작은 0에 가까운 숫자이니 귀무가설 기각! 대립가설 채택!
"상위 25% 숙소는 하위 25% 숙소보다 월 예상 매출이 통계적으로 유의하게 높다.”
데이터를 통해 확인해 볼 수 있는 것
→ 상위 숙소일수록 + 전체실일 때 수익 높은 편
= 예상 월 매출은 접근성(도심)과 객실의 독립성(전체실)이 중요
→ 접근성이 주는 요소가 크다고 볼 수 있음
그럼 기준이 같은 우수 숙소랑 비우수 숙소에서의 비교가 필요하겠네?
가장 차이가 큰 : 도심 + entire_home/apt
# 도심 & Entire home/apt 만 필터링
subset = df_filtered[
(df_filtered['city_and_suburb'] == '도심') &
(df_filtered['room_type'] == 'Entire home/apt')
]
# 상위/하위 25% popularity_score 기준
q1 = subset['popularity_score'].quantile(0.25)
q3 = subset['popularity_score'].quantile(0.75)
upper = subset[subset['popularity_score'] >= q3]
lower = subset[subset['popularity_score'] <= q1]
results = {
'지표': [],
'상위 25% 평균': [],
'하위 25% 평균': [],
'상위 25% 중앙값': [],
'하위 25% 중앙값': []
}
columns_to_compare = [
'price',
'expected_monthly_revenue',
'number_of_reviews',
'reviews_per_month',
'availability_365',
'minimum_nights',
'calculated_host_listings_count',
'operating_months'
]
for col in columns_to_compare:
results['지표'].append(col)
results['상위 25% 평균'].append(round(upper[col].mean(), 2))
results['하위 25% 평균'].append(round(lower[col].mean(), 2))
results['상위 25% 중앙값'].append(round(upper[col].median(), 2))
results['하위 25% 중앙값'].append(round(lower[col].median(), 2))
comparison_table = pd.DataFrame(results)
comparison_table
Plain Text
복사
인사이트 도출
•
평균 가격이나 중앙값으로는 상/하위의 큰 차이가 있다고 보기 어려움
•
근데 상위 25%가 하위 25%보다 월 예상 매출이 약 10배 높음
→ 가격이 높아도 사람들이 잘 이용하므로 가격을 낮추는 것을 방안으로 내세우기 어려움
•
전체 리뷰 수는 상위 25%가 약 9-10배로 월등히 높음
•
한 달 평균 리뷰 수는 하위 25%가 거의 없는 수준
→ 하위 25%의 리뷰를 올리는 방안 모색 필요 O
<7일 이하 (단기)>
<8-30일 (중기)>
<31일 이상 (장기)>
•
상위 평균 및 중앙값은 151-154로 비슷
→ 0에 가까울수록 우수 숙소라 생각했던 걸 깨버림….
→ 장기로 넘어갈수록 예약 가능일 수
= 연중 운영일 많음
•
하위 평균은 42 중앙값은 0으로 평균과 중앙값 차이 
→ 하위 25% 는 7일 이하의 숙소
→ 운영 중단 / 비수기 운영 X / 특정 계절 운영 등 다양한 이유로 예약 가능일 수 
⇒ 예약 관리, 공실률
, 비수기 운영 전략 등의 방안 제안
•
평균을 보면 2배 정도 차이로 상위 25%의 기간이 짧음
→ 최소 숙박일 수를 단기로 하여 유동적으로 운영
•
중앙값이 1로 같은 것을 보면 이상치의 영향 큰 것으로 판단!
•
상위 25% 숙소 운영일 수 > 하위 25%
추가 분석
*제가 메인으로 세운 가설이 너무 당연하게 나와서…! ㅜ 우수 비우수를 컬럼별로 저희가 세운 기준에 맞춰 분석을 추가로 해보고 있습니다!
상위 숙소랑 하위 숙소는 예약 가능한 날짜 수에 따라 차이가 있을까?
•
최소 숙박일 수에서 나눈 단기/중기/장기에 따라 구분
상위 숙소랑 하위 숙소의 호스트 리스팅 개수도 차이가 있을까?
# 조합 정의
combinations = [
('도심', 'Entire home/apt'),
('도심', 'Private room'),
('도심', 'Shared room'),
('외곽', 'Entire home/apt'),
('외곽', 'Private room'),
('외곽', 'Shared room'),
]
results = []
# 각 조합별로 상/하위 25%에서 단기/중기/장기 숙소 수 세기
for city_and_suburb, room in combinations:
cond = (df_filtered['city_and_suburb'] == city_and_suburb) & (df_filtered['room_type'] == room)
subset = df_filtered[cond]
q3 = subset['popularity_score'].quantile(0.75)
q1 = subset['popularity_score'].quantile(0.25)
upper = subset[subset['popularity_score'] >= q3]
lower = subset[subset['popularity_score'] <= q1]
# 예약 가능일 기준 단기/중기/장기 조건
upper_general = upper[upper['calculated_host_listings_count'] <= 2]
upper_mid_pro = upper[(upper['calculated_host_listings_count'] > 2) & (upper['calculated_host_listings_count'] <= 10)]
upper_pro = upper[upper['calculated_host_listings_count'] > 10]
lower_general = lower[lower['calculated_host_listings_count'] <= 2]
lower_mid_pro = lower[(lower['calculated_host_listings_count'] > 2) & (lower['calculated_host_listings_count'] <= 10)]
lower_pro = lower[lower['calculated_host_listings_count'] > 10]
results.append({
'조합': f"{city_and_suburb} & {room}",
'상위_일반 호스트(~2개)': len(upper_general),
'상위_준전문가(2~10개)': len(upper_mid_pro),
'상위_전문가(10개~)': len(upper_pro),
'하위_일반 호스트(~2개)': len(lower_general),
'하위_준전문가(2~10개)': len(lower_mid_pro),
'하위_전문가(10개~)': len(lower_pro),
'전체 수': len(subset)
})
# 데이터프레임으로 변환
range_host_count = pd.DataFrame(results)
range_host_count
# 시각화 진행
melted = range_host_count.melt(
id_vars='조합',
value_vars=['상위_일반 호스트(~2개)', '상위_준전문가(2~10개)', '상위_전문가(10개~)', '하위_일반 호스트(~2개)', '하위_준전문가(2~10개)', '하위_전문가(10개~)'],
var_name='그룹',
value_name='호스트 숙소 리스팅 개수'
)
melted['지표종류'] = melted['그룹'].apply(
lambda x: '일반 호스트(~2개)' if '일반 호스트' in x else (
'준전문가(2~10개)' if '준전문가' in x else '상위_전문가(10개~)'
)
)
# FacetGrid 로 평균/중앙값 따로 표시
g = sns.catplot(
data=melted,
kind='bar',
x='조합',
y='호스트 숙소 리스팅 개수',
hue='그룹',
col='지표종류',
sharey=False,
errorbar=None,
height=7,
aspect=1.5
)
g.set_titles('{col_name}')
g.set_xticklabels(rotation=45)
g.set_axis_labels('', '호스트 숙소 리스팅 개수')
g.fig.suptitle('상위/하위 25% 호스트 숙소 리스팅 개수 비교', fontsize=16)
plt.tight_layout()
plt.show()
Python
복사
.png&blockId=21b2dc3e-f514-818b-b72b-f9aade6351bf)











