2222
2323merged_model = pd .read_pickle ("merged_model.pkl" )
2424
25- # '?' -> '· ' 변환
26- merged_model ['행정동_코드_명' ] = merged_model ['행정동_코드_명' ].str .replace ('?' , '· ' , regex = False )
25+ # '?' -> '. ' 변환
26+ merged_model ['행정동_코드_명' ] = merged_model ['행정동_코드_명' ].str .replace ('?' , '. ' , regex = False )
2727
2828# 수치 포맷 함수들
2929def format_ratio (x ):
3030 return f"{ x :.5f} "
3131
3232def format_money_mw (x ):
33+ """
34+ 금액(x)을 만원 단위로만 반환
35+ ex) 36000000 → '3600'
36+ """
3337 return str (int (round (x / 10000 )))
3438
3539def format_income_mw (x ):
40+ """
41+ 월 평균 소득(x)을 만원 단위로 반환
42+ ex) 3600000 → '360'
43+ """
3644 return str (int (round (x / 10000 )))
3745
38- def generate_result_template (행정동명 ):
46+ def generate_result_template (merged_model , 행정동명 ):
3947 등급_텍스트 = {0 : '하' , 1 : '중' , 2 : '상' }
4048 등급_info = {
4149 '상' : {'desc' : '매우 높음' , 'precision' : '77%' , 'recommendations' : ['카페' , '헬스장' , '미용실' ]},
4250 '중' : {'desc' : '보통' , 'precision' : '62%' , 'recommendations' : ['편의점' , '분식집' , '세탁소' ]},
43- '하' : {'desc' : '낮음' , 'precision' : '69%' , 'recommendations' : ['중고매장' , 'PC방' , '호프집' ]},
51+ '하' : {'desc' : '낮음' , 'precision' : '69%' , 'recommendations' : ['중고매장' , 'PC방' , '호프집' ]}
4452 }
4553
4654 filtered = merged_model [merged_model ['행정동_코드_명' ] == 행정동명 ].copy ()
@@ -70,56 +78,76 @@ def generate_result_template(행정동명):
7078 여_ratio = filtered ['여성_유동인구_비율' ].mean ()
7179 주성별 = '남성' if 남_ratio >= 여_ratio else '여성'
7280
73- age_pop_cols = [
74- '연령대_10_유동인구_수' , '연령대_20_유동인구_수' ,
75- '연령대_30_유동인구_수' , '연령대_40_유동인구_수' ,
76- '연령대_50_유동인구_수' , '연령대_60_이상_유동인구_수'
77- ]
7881 연령대별비율_cols = [f"{ col } _비율" for col in age_pop_cols ]
7982 avg_age_ratios = filtered [연령대별비율_cols ].mean ()
80- 주연령대 = avg_age_ratios .idxmax ().replace ('_비율' , '' ). replace ( '연령대_' , '' ). replace ( '_유동인구_수' , '' ) + '대'
83+ 주연령대 = avg_age_ratios .idxmax ().replace ('_비율' , '' )
8184
8285 peak_time = filtered ['최대_시간대_이름' ].mode ().iloc [0 ]
8386
8487 reasons = []
88+ # 1) 유동인구 변화율 비교
8589 if 지역_avg_flow > 전국_avg_flow :
86- reasons .append (f"유동인구 변화율이 전국 평균보다 높습니다 ({ format_ratio (지역_avg_flow )} > { format_ratio (전국_avg_flow )} )." )
90+ reasons .append (
91+ f"유동인구 변화율이 전국 평균보다 높습니다 ({ format_ratio (지역_avg_flow )} > { format_ratio (전국_avg_flow )} )."
92+ )
8793 else :
88- reasons .append (f"유동인구 변화율이 전국 평균보다 낮습니다 ({ format_ratio (지역_avg_flow )} < { format_ratio (전국_avg_flow )} )." )
89-
94+ reasons .append (
95+ f"유동인구 변화율이 전국 평균보다 낮습니다 ({ format_ratio (지역_avg_flow )} < { format_ratio (전국_avg_flow )} )."
96+ )
97+ # 2) 전년 동기 대비 유동인구 변화율 “추세” 템플릿
9098 if np .isclose (지역_avg_yoy_pop , 전국_avg_yoy_pop ):
9199 reasons .append ("전년 동기 대비 유동인구 변화가 전국 평균과 유사한 수준입니다." )
92100 elif 지역_avg_yoy_pop > 전국_avg_yoy_pop :
93101 reasons .append ("전년 동기 대비 유동인구가 전국 평균보다 증가 추세를 보입니다." )
94102 else :
95103 reasons .append ("전년 동기 대비 유동인구가 전국 평균보다 감소 추세를 보입니다." )
96-
104+ # 3) 현재 매출 비교
97105 if 지역_avg_sales > 전국_avg_sales :
98- reasons .append (f"현재(당월) 평균 매출이 전국 평균보다 높습니다 ({ format_money_mw (지역_avg_sales )} 만 원 > { format_money_mw (전국_avg_sales )} 만 원)." )
106+ reasons .append (
107+ f"현재(당월) 평균 매출이 전국 평균보다 높습니다 ({ format_money_mw (지역_avg_sales )} 만 원 > { format_money_mw (전국_avg_sales )} 만 원)."
108+ )
99109 else :
100- reasons .append (f"현재(당월) 평균 매출이 전국 평균보다 낮습니다 ({ format_money_mw (지역_avg_sales )} 만 원 < { format_money_mw (전국_avg_sales )} 만 원)." )
101-
110+ reasons .append (
111+ f"현재(당월) 평균 매출이 전국 평균보다 낮습니다 ({ format_money_mw (지역_avg_sales )} 만 원 < { format_money_mw (전국_avg_sales )} 만 원)."
112+ )
113+ # 4) 전년 동기 대비 매출 변화율 “추세” 템플릿
102114 if np .isclose (지역_avg_yoy_sales , 전국_avg_yoy_sales ):
103115 reasons .append ("전년 동기 대비 매출 변화가 전국 평균과 유사한 수준입니다." )
104116 elif 지역_avg_yoy_sales > 전국_avg_yoy_sales :
105117 reasons .append ("전년 동기 대비 매출이 전국 평균보다 상승 추세를 보입니다." )
106118 else :
107119 reasons .append ("전년 동기 대비 매출이 전국 평균보다 하락 추세를 보입니다." )
108-
120+ # 5) 평균 소득 비교 (단위: 만원)
109121 if 지역_avg_income > 전국_avg_income :
110- reasons .append (f"월 평균 소득이 전국 평균보다 높습니다 ({ format_income_mw (지역_avg_income )} 만 원 > { format_income_mw (전국_avg_income )} 만 원)." )
122+ reasons .append (
123+ f"월 평균 소득이 전국 평균보다 높습니다 ({ format_income_mw (지역_avg_income )} 만 원 > { format_income_mw (전국_avg_income )} 만 원)."
124+ )
111125 else :
112- reasons .append (f"월 평균 소득이 전국 평균보다 낮습니다 ({ format_income_mw (지역_avg_income )} 만 원 < { format_income_mw (전국_avg_income )} 만 원)." )
113-
126+ reasons .append (
127+ f"월 평균 소득이 전국 평균보다 낮습니다 ({ format_income_mw (지역_avg_income )} 만 원 < { format_income_mw (전국_avg_income )} 만 원)."
128+ )
129+ # 6) 데이터 건수 비교
114130 전국_avg_count = 전국 .groupby ('행정동_코드_명' ).size ().mean ()
115131 if 지역_count > 전국_avg_count :
116- reasons .append (f"이 지역 데이터 건수가 평균보다 많아 예측 신뢰도가 높습니다 ({ 지역_count } 개 > { 전국_avg_count :.1f} 개)." )
132+ reasons .append (
133+ f"이 지역 데이터 건수가 평균보다 많아 예측 신뢰도가 높습니다 ({ 지역_count } 개 > { 전국_avg_count :.1f} 개)."
134+ )
117135 else :
118- reasons .append (f"이 지역 데이터 건수가 평균보다 적어 데이터가 제한적일 수 있습니다 ({ 지역_count } 개 < { 전국_avg_count :.1f} 개)." )
119-
120- 업종별_avg_future = filtered .groupby ('서비스_업종_코드_명' )['향후_평균_매출' ].mean ().sort_values (ascending = False )
136+ reasons .append (
137+ f"이 지역 데이터 건수가 평균보다 적어 데이터가 제한적일 수 있습니다 ({ 지역_count } 개 < { 전국_avg_count :.1f} 개)."
138+ )
139+
140+ # 동적 Top3 업종 추천
141+ 업종별_avg_future = (
142+ filtered
143+ .groupby ('서비스_업종_코드_명' )['향후_평균_매출' ]
144+ .mean ()
145+ .sort_values (ascending = False )
146+ )
121147 top3_업종 = 업종별_avg_future .head (3 ).index .tolist ()
122- fixed_reco = info ['recommendations' ]
148+
149+ # 하드코딩된 등급별 추천업종
150+ fixed_reco = 등급_info [top_grade ]['recommendations' ]
123151
124152 output = f"""
125153🔍 '{ 행정동명 } ' 상권 분석 결과
@@ -137,7 +165,7 @@ def generate_result_template(행정동명):
137165 output += f"""
138166🔹 주요 고객층 및 피크 시간대:
139167- 주요 고객 성별: { 주성별 }
140- - 주요 고객 연령대: { 주연령대 }
168+ - 주요 고객 연령대: { 주연령대 . replace ( '연령대_' , '' ). replace ( '_유동인구_수' , '' ) } 대
141169- 피크 매출 시간대: { peak_time }
142170
143171✅ '{ 행정동명 } ' 지역 동적 추천 업종 Top 3:
@@ -146,13 +174,15 @@ def generate_result_template(행정동명):
1461743. { top3_업종 [2 ]}
147175
148176🔹 '{ top_grade } ' 등급에 속할 때 추천 업종:
149- 1. { fixed_reco [0 ]}
150- 2. { fixed_reco [1 ]}
151- 3. { fixed_reco [2 ]}
152177"""
178+ for idx , 업종 in enumerate (fixed_reco , start = 1 ):
179+ output += f"{ idx } . { 업종 }
180+ "
153181
154182 return output
155183
184+
185+
156186@app .get ("/" )
157187def health_check ():
158188 return {"status" : "ok" , "message" : "상권 분석 API가 실행 중입니다." }
0 commit comments