diff --git a/best_shuttle_bus_route.ipynb b/best_shuttle_bus_route.ipynb deleted file mode 100644 index 3d7ed0a..0000000 --- a/best_shuttle_bus_route.ipynb +++ /dev/null @@ -1,2317 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 최적 셔틀버스 노선 탐색" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 데이터 준비" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import pandas as pd\n", - "\n", - "# 'data/raw_data' 경로 설정\n", - "root_dir = 'data/'\n", - "visitor_city = pd.read_csv(os.path.join(root_dir, \"city_of_festival_visitors.csv\")) # 무주축제 방문객 top 18 지역들 (시군구명,od_cnts,시도명,행정동코드,위도,경도)\n", - "address = pd.read_csv(os.path.join(root_dir, \"address_with_lon_lat_final.csv\")) # 행정동코드 + 위도경도 (행정동코드,시도명,시군구명,읍면동명,동리명,위도,경도)\n", - "mooju = set(list(address[address['시군구명'] == '무주군']['행정동코드'])) # 무주군 행정동코드\n", - "other_city = list(address.merge(visitor_city, on=['시도명', '시군구명'])['행정동코드_x']) # 다른 지역들 행정동코드 모음" - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
시군구명od_cnts시도명행정동코드위도경도시도 시군구
0전주시 덕진구2834전라북도451130000035.847561127.117672전라북도 전주시 덕진구
1서구1813대전광역시301700000036.355179127.383849대전광역시 서구
2전주시 완산구1561전라북도451110000035.795512127.132447전라북도 전주시 완산구
3유성구1379대전광역시302000000036.362073127.356410대전광역시 유성구
4동구1207대전광역시301100000035.886500128.635500대전광역시 동구
\n", - "
" - ], - "text/plain": [ - " 시군구명 od_cnts 시도명 행정동코드 위도 경도 시도 시군구\n", - "0 전주시 덕진구 2834 전라북도 4511300000 35.847561 127.117672 전라북도 전주시 덕진구\n", - "1 서구 1813 대전광역시 3017000000 36.355179 127.383849 대전광역시 서구\n", - "2 전주시 완산구 1561 전라북도 4511100000 35.795512 127.132447 전라북도 전주시 완산구\n", - "3 유성구 1379 대전광역시 3020000000 36.362073 127.356410 대전광역시 유성구\n", - "4 동구 1207 대전광역시 3011000000 35.886500 128.635500 대전광역시 동구" - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "visitor_city['시도 시군구'] = visitor_city['시도명'].fillna('') + ' ' + visitor_city['시군구명'].fillna('')\n", - "\n", - "# 양쪽 값이 모두 null인 경우 빈 문자열 처리\n", - "visitor_city['시도 시군구'] = visitor_city['시도 시군구'].str.strip()\n", - "visitor_city.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
행정동코드시도명시군구명읍면동명동리명위도경도시도 시군구
01100000000서울특별시NaNNaN서울특별시37.566679126.978291서울특별시
11111000000서울특별시종로구NaN종로구37.580695126.982799서울특별시 종로구
21111051500서울특별시종로구청운효자동세종로37.579997126.976930서울특별시 종로구
31111051500서울특별시종로구청운효자동옥인동37.583480126.963850서울특별시 종로구
41111051500서울특별시종로구청운효자동누하동37.578998126.967561서울특별시 종로구
...........................
217725183035000강원특별자치도양양군강현면정암리38.143050128.607330강원특별자치도 양양군
217735183035000강원특별자치도양양군강현면용호리38.132320128.610700강원특별자치도 양양군
217745183035000강원특별자치도양양군강현면전진리38.124830128.624220강원특별자치도 양양군
217755183035000강원특별자치도양양군강현면물치리38.158083128.608889강원특별자치도 양양군
217765183035000강원특별자치도양양군강현면적은리38.118555128.598405강원특별자치도 양양군
\n", - "

21777 rows × 8 columns

\n", - "
" - ], - "text/plain": [ - " 행정동코드 시도명 시군구명 읍면동명 동리명 위도 경도 \\\n", - "0 1100000000 서울특별시 NaN NaN 서울특별시 37.566679 126.978291 \n", - "1 1111000000 서울특별시 종로구 NaN 종로구 37.580695 126.982799 \n", - "2 1111051500 서울특별시 종로구 청운효자동 세종로 37.579997 126.976930 \n", - "3 1111051500 서울특별시 종로구 청운효자동 옥인동 37.583480 126.963850 \n", - "4 1111051500 서울특별시 종로구 청운효자동 누하동 37.578998 126.967561 \n", - "... ... ... ... ... ... ... ... \n", - "21772 5183035000 강원특별자치도 양양군 강현면 정암리 38.143050 128.607330 \n", - "21773 5183035000 강원특별자치도 양양군 강현면 용호리 38.132320 128.610700 \n", - "21774 5183035000 강원특별자치도 양양군 강현면 전진리 38.124830 128.624220 \n", - "21775 5183035000 강원특별자치도 양양군 강현면 물치리 38.158083 128.608889 \n", - "21776 5183035000 강원특별자치도 양양군 강현면 적은리 38.118555 128.598405 \n", - "\n", - " 시도 시군구 \n", - "0 서울특별시 \n", - "1 서울특별시 종로구 \n", - "2 서울특별시 종로구 \n", - "3 서울특별시 종로구 \n", - "4 서울특별시 종로구 \n", - "... ... \n", - "21772 강원특별자치도 양양군 \n", - "21773 강원특별자치도 양양군 \n", - "21774 강원특별자치도 양양군 \n", - "21775 강원특별자치도 양양군 \n", - "21776 강원특별자치도 양양군 \n", - "\n", - "[21777 rows x 8 columns]" - ] - }, - "execution_count": 56, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "address['시도 시군구'] = address['시도명'].fillna('') + ' ' + address['시군구명'].fillna('')\n", - "\n", - "# 양쪽 값이 모두 null인 경우 빈 문자열 처리\n", - "address['시도 시군구'] = address['시도 시군구'].str.strip()\n", - "address" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - " 0%| | 0/9 [00:00\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
origin_hdong_cddest_hdong_cddatestart_timeend_timegenderagemodalorigin_purposedest_purposeod_dist_avgod_duration_avgod_cnts
1096431137410045730310002023090209:0011:00100.00.051637821096
2269457303300045730250002023090220:0020:00031.05.0520247289
3874481296500045730340002023090216:0018:00040.04.054240721317
4218481707300045730320002023090213:0016:00100.00.051191001419
4773457303200045730250002023090217:0019:00040.05.051280301189
..........................................
3237279457403600045730340002023091012:0012:00011.05.0538926275
3238635457303300045730250002023091012:0012:00150.05.059476135
3239981457202500045730330002023091018:0018:00030.05.0550623336
3246027457302500045730340002023091011:0012:00030.05.0546949515
3249613478302530045730320002023091010:0011:00010.05.052183741005
\n", - "

4173 rows × 13 columns

\n", - "" - ], - "text/plain": [ - " origin_hdong_cd dest_hdong_cd date start_time end_time gender \\\n", - "1096 4311374100 4573031000 20230902 09:00 11:00 1 \n", - "2269 4573033000 4573025000 20230902 20:00 20:00 0 \n", - "3874 4812965000 4573034000 20230902 16:00 18:00 0 \n", - "4218 4817073000 4573032000 20230902 13:00 16:00 1 \n", - "4773 4573032000 4573025000 20230902 17:00 19:00 0 \n", - "... ... ... ... ... ... ... \n", - "3237279 4574036000 4573034000 20230910 12:00 12:00 0 \n", - "3238635 4573033000 4573025000 20230910 12:00 12:00 1 \n", - "3239981 4572025000 4573033000 20230910 18:00 18:00 0 \n", - "3246027 4573025000 4573034000 20230910 11:00 12:00 0 \n", - "3249613 4783025300 4573032000 20230910 10:00 11:00 0 \n", - "\n", - " age modal origin_purpose dest_purpose od_dist_avg \\\n", - "1096 0 0.0 0.0 5 163782 \n", - "2269 3 1.0 5.0 5 20247 \n", - "3874 4 0.0 4.0 5 424072 \n", - "4218 0 0.0 0.0 5 119100 \n", - "4773 4 0.0 5.0 5 128030 \n", - "... ... ... ... ... ... \n", - "3237279 1 1.0 5.0 5 38926 \n", - "3238635 5 0.0 5.0 5 9476 \n", - "3239981 3 0.0 5.0 5 50623 \n", - "3246027 3 0.0 5.0 5 46949 \n", - "3249613 1 0.0 5.0 5 218374 \n", - "\n", - " od_duration_avg od_cnts \n", - "1096 109 6 \n", - "2269 28 9 \n", - "3874 131 7 \n", - "4218 141 9 \n", - "4773 118 9 \n", - "... ... ... \n", - "3237279 27 5 \n", - "3238635 13 5 \n", - "3239981 33 6 \n", - "3246027 51 5 \n", - "3249613 100 5 \n", - "\n", - "[4173 rows x 13 columns]" - ] - }, - "execution_count": 57, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from tqdm import tqdm\n", - "\n", - "data_frames = []\n", - "muju_codes = [4573000000, 4573025000, 4573031000, 4573032000, 4573033000, 4573034000, 4573035000]\n", - "\n", - "for i in tqdm(range(2, 11)):\n", - " # print(f\"{i}번째 \", end=\" \")\n", - " index = str(i).rjust(2, '0')\n", - " df_tmp = pd.read_csv(f\"EDA/jiwon/data/od_20230901_10/od_202309{index}_1.csv\")\n", - " to_muju = df_tmp['dest_hdong_cd'].isin(muju_codes) & (df_tmp['dest_purpose'] == 5)\n", - " data_frames.append(df_tmp[to_muju])\n", - "del df_tmp\n", - "\n", - "df_od = pd.concat(data_frames, axis=0)\n", - "df_od" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 9/9 [00:18<00:00, 2.08s/it]\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
hdong_cddateagestay_cnts
0111105150020230902015738
1111105150020230902126248
2111105150020230902221350
3111105150020230902332200
4111105150020230902437772
...............
3125251830350002023091045202
3125351830350002023091057813
3125451830350002023091066296
3125551830350002023091075745
3125651830350002023091082541
\n", - "

281450 rows × 4 columns

\n", - "
" - ], - "text/plain": [ - " hdong_cd date age stay_cnts\n", - "0 1111051500 20230902 0 15738\n", - "1 1111051500 20230902 1 26248\n", - "2 1111051500 20230902 2 21350\n", - "3 1111051500 20230902 3 32200\n", - "4 1111051500 20230902 4 37772\n", - "... ... ... ... ...\n", - "31252 5183035000 20230910 4 5202\n", - "31253 5183035000 20230910 5 7813\n", - "31254 5183035000 20230910 6 6296\n", - "31255 5183035000 20230910 7 5745\n", - "31256 5183035000 20230910 8 2541\n", - "\n", - "[281450 rows x 4 columns]" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data_frames = []\n", - "for i in tqdm(range(2, 11)):\n", - " # print(f\"9월 {i}일 \", end=\" \")\n", - " index = str(i).rjust(2, '0')\n", - " df_tmp = pd.read_csv(f\"EDA/jiwon/data/stay_20230901_15/stay_202309{index}_1.csv\")\n", - " residents = df_tmp[(df_tmp['purpose'] == 0)]\n", - " grouped_residents = residents.groupby(['hdong_cd', 'date', 'age'])['stay_cnts'].sum().reset_index()\n", - " data_frames.append(grouped_residents)\n", - "del df_tmp\n", - "\n", - "df_stay = pd.concat(data_frames, axis=0)\n", - "df_stay" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 지역별 거주인원" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
hdong_cdstay_cnts
01111051500205476.0
11111053000101129.0
2111105400038539.0
31111055000135173.0
41111056000183499.0
\n", - "
" - ], - "text/plain": [ - " hdong_cd stay_cnts\n", - "0 1111051500 205476.0\n", - "1 1111053000 101129.0\n", - "2 1111054000 38539.0\n", - "3 1111055000 135173.0\n", - "4 1111056000 183499.0" - ] - }, - "execution_count": 59, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 지역별 전체 거주민 수\n", - "df_stay_all = df_stay.groupby(['hdong_cd', 'date'])['stay_cnts'].sum().reset_index() # 날짜별 거주인구 합산\n", - "avg_stay_cnts_all = round(df_stay_all.groupby('hdong_cd')['stay_cnts'].mean().reset_index(), 0) # 하루 평균 거주인구\n", - "avg_stay_cnts_all.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
hdong_cdstay_cnts
0111105150022012.0
1111105300011963.0
211110540005773.0
3111105500018550.0
4111105600024343.0
\n", - "
" - ], - "text/plain": [ - " hdong_cd stay_cnts\n", - "0 1111051500 22012.0\n", - "1 1111053000 11963.0\n", - "2 1111054000 5773.0\n", - "3 1111055000 18550.0\n", - "4 1111056000 24343.0" - ] - }, - "execution_count": 60, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 지역별 20대,50대,60대 거주민 수\n", - "df_stay_age = df_stay[df_stay['age'].isin([2,5,6])] # 날짜별 20대 거주인구\n", - "avg_stay_cnts_age = round(df_stay_age.groupby(['hdong_cd'])['stay_cnts'].mean().reset_index(), 0) # 하루 평균 20대 거주인구\n", - "avg_stay_cnts_age.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 지역별 무주축제방문객 인원" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
origin_hdong_cddest_hdong_cddatestart_timeend_timegenderagemodalorigin_purposedest_purposeod_dist_avgod_duration_avgod_cnts
1096431137410045730310002023090209:0011:00100.00.051637821096
2269457303300045730250002023090220:0020:00031.05.0520247289
3874481296500045730340002023090216:0018:00040.04.054240721317
\n", - "
" - ], - "text/plain": [ - " origin_hdong_cd dest_hdong_cd date start_time end_time gender \\\n", - "1096 4311374100 4573031000 20230902 09:00 11:00 1 \n", - "2269 4573033000 4573025000 20230902 20:00 20:00 0 \n", - "3874 4812965000 4573034000 20230902 16:00 18:00 0 \n", - "\n", - " age modal origin_purpose dest_purpose od_dist_avg od_duration_avg \\\n", - "1096 0 0.0 0.0 5 163782 109 \n", - "2269 3 1.0 5.0 5 20247 28 \n", - "3874 4 0.0 4.0 5 424072 131 \n", - "\n", - " od_cnts \n", - "1096 6 \n", - "2269 9 \n", - "3874 7 " - ] - }, - "execution_count": 61, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df_od.head(3)" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
origin_hdong_cddateageod_cnts
0111706850020230908012
111200670002023090246
211215840002023090209
311320511002023090946
4113506400020230902064
...............
1703511306600020230902012
170451130675002023090736
170551130675002023090936
1706511306750020230909412
1707518103300020230908050
\n", - "

1708 rows × 4 columns

\n", - "
" - ], - "text/plain": [ - " origin_hdong_cd date age od_cnts\n", - "0 1117068500 20230908 0 12\n", - "1 1120067000 20230902 4 6\n", - "2 1121584000 20230902 0 9\n", - "3 1132051100 20230909 4 6\n", - "4 1135064000 20230902 0 64\n", - "... ... ... ... ...\n", - "1703 5113066000 20230902 0 12\n", - "1704 5113067500 20230907 3 6\n", - "1705 5113067500 20230909 3 6\n", - "1706 5113067500 20230909 4 12\n", - "1707 5181033000 20230908 0 50\n", - "\n", - "[1708 rows x 4 columns]" - ] - }, - "execution_count": 62, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 그룹화\n", - "df_od_group = df_od.groupby(['origin_hdong_cd', 'date', 'age'])['od_cnts'].sum().reset_index()\n", - "df_od_group" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
origin_hdong_cdod_cnts
0111706850012
111200670006
211215840009
311320511006
4113506400070
\n", - "
" - ], - "text/plain": [ - " origin_hdong_cd od_cnts\n", - "0 1117068500 12\n", - "1 1120067000 6\n", - "2 1121584000 9\n", - "3 1132051100 6\n", - "4 1135064000 70" - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 지역별 전체 무주축제방문객 수\n", - "df_od_all = df_od_group.groupby(['origin_hdong_cd', 'date'])['od_cnts'].sum().reset_index() # 날짜별 방문객수 합산\n", - "sum_od_cnts_all = round(df_od_all.groupby(['origin_hdong_cd'])['od_cnts'].sum().reset_index(), 0) # 해당 지역에서 온 전체 방문객수\n", - "sum_od_cnts_all.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
origin_hdong_cdod_cnts
011350640006
111380510005
211410620007
3114107200014
411440600008
.........
310488803400013
31148880350006
312488803700013
313488804000017
31448890450008
\n", - "

315 rows × 2 columns

\n", - "
" - ], - "text/plain": [ - " origin_hdong_cd od_cnts\n", - "0 1135064000 6\n", - "1 1138051000 5\n", - "2 1141062000 7\n", - "3 1141072000 14\n", - "4 1144060000 8\n", - ".. ... ...\n", - "310 4888034000 13\n", - "311 4888035000 6\n", - "312 4888037000 13\n", - "313 4888040000 17\n", - "314 4889045000 8\n", - "\n", - "[315 rows x 2 columns]" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 지역별 20대,50대,60대 무주축제방문객 수\n", - "df_od_age = df_od_group[df_od_group['age'].isin([2,5,6])]\n", - "sum_od_cnts_age = round(df_od_age.groupby('origin_hdong_cd')['od_cnts'].sum().reset_index(), 0)\n", - "sum_od_cnts_age" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 함수" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 노드 간 정보" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [], - "source": [ - "# 시도시군구명으로 위경도 좌표를 반환하는 함수\n", - "def get_coordinates(lst):\n", - " coordinates = []\n", - " for name in lst:\n", - " try:\n", - " # '시도 시군구'로 '위도', '경도' 가져옴\n", - " target = address[address['시도 시군구'] == name][['위도','경도']].iloc[0]\n", - " x, y = target.iloc[0], target.iloc[1]\n", - " coordinates.append((x,y))\n", - " except IndexError:\n", - " # 해당 '시도 시군구'에 대한 데이터가 없는 경우\n", - " print(f\"{name} 지역의 위도, 경도 정보가 없습니다.\")\n", - " return coordinates" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": {}, - "outputs": [], - "source": [ - "import yaml\n", - "import requests\n", - "\n", - "# api 사용을 위한 key값 가져오기 \n", - "with open(\"config.yaml\", \"r\") as file:\n", - " config = yaml.safe_load(file)\n", - "naver_api_id = config['naver api']['id']\n", - "naver_api_key = config['naver api']['key']\n", - "kakao_api_key = config['kakao api']['key']\n", - "\n", - "# 네이버 api 요청 함수\n", - "def naver_request(start, goal):\n", - " # 요청\n", - " url = 'https://naveropenapi.apigw.ntruss.com/map-direction/v1/driving'\n", - " params = {\n", - " 'goal': f'{goal[1]},{goal[0]}',\n", - " 'start': f'{start[1]},{start[0]}',\n", - " }\n", - " headers = {\n", - " 'x-ncp-apigw-api-key-id': naver_api_id,\n", - " 'x-ncp-apigw-api-key': naver_api_key\n", - " }\n", - " response = requests.get(url, headers=headers, params=params)\n", - "\n", - " # 응답\n", - " data = response.json()\n", - " if data['code'] == 0:\n", - " if data['route']['traoptimal']:\n", - " summary = data['route']['traoptimal'][0]['summary']\n", - " distance = summary['distance']\n", - " duration = summary['duration']\n", - " return distance, duration\n", - " else:\n", - " print(\"응답 에러: 'traoptimal' 데이터가 없습니다.\")\n", - " return -1, -1\n", - " else:\n", - " print(\"요청 실패: \", data['message'])\n", - " return -1, -1\n", - "\n", - "# 카카오 api 요청 함수\n", - "def kakao_request(start, goal):\n", - " # 요청\n", - " url = 'https://apis-navi.kakaomobility.com/v1/directions'\n", - " params = {\n", - " 'origin': f'{start[1]},{start[0]}',\n", - " 'destination': f'{goal[1]},{goal[0]}',\n", - " 'alternatives': True,\n", - " }\n", - " headers = {\n", - " 'Authorization': f'KakaoAK {kakao_api_key}'\n", - " }\n", - " response = requests.get(url, headers=headers, params=params)\n", - "\n", - " # 응답\n", - " if response.status_code == 200:\n", - " data = response.json()\n", - " if data['routes']:\n", - " summary = data['routes'][0]['summary']\n", - " distance = summary.get('distance')\n", - " duration = summary.get('duration')\n", - " return distance, duration\n", - " else:\n", - " print(\"응답 에러: 'routes' 데이터가 없습니다.\")\n", - " return -1, -1\n", - " else:\n", - " print(\"요청 실패\")\n", - " return -1, -1" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": {}, - "outputs": [], - "source": [ - "# 노드 간 이동시간, 이동거리 테이블 반환하는 함수\n", - "def get_path_info(coordinates, request_func):\n", - " n = len(coordinates)\n", - " distance_table = [[0] * n for _ in range(n)]\n", - " duration_table = [[0] * n for _ in range(n)]\n", - "\n", - " for i in range(n):\n", - " for j in range(n):\n", - " if j > i:\n", - " start = coordinates[i]\n", - " goal = coordinates[j]\n", - " distance, duration = request_func(start, goal)\n", - " distance_table[i][j], distance_table[j][i] = distance, distance\n", - " duration_table[i][j], duration_table[j][i] = duration, duration\n", - "\n", - " return distance_table, duration_table" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 가중치" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "지역명: 세종특별자치시\n", - "관련 행정동코드: [3600000000, 3611000000, 3611025000, 3611031000, 3611032000, 3611033000, 3611034000, 3611035000, 3611036000, 3611037000, 3611038000, 3611039000, 3611051000, 3611051500, 3611051800, 3611052000, 3611052300, 3611052500, 3611053000, 3611054000, 3611055000, 3611055500, 3611055600, 3611056000, 3611057000, 3611058000]\n", - "총 거주인원: 3048131.0 , 20/50/60대 거주인원: 290518.0 \n", - "\n", - "지역명: 대전광역시 유성구\n", - "관련 행정동코드: [3020000000, 3020052000, 3020052600, 3020052700, 3020053000, 3020054000, 3020054600, 3020054700, 3020054800, 3020055000, 3020057000, 3020058000, 3020060000, 3020061000]\n", - "총 거주인원: 3820791.0 , 20/50/60대 거주인원: 424653.0 \n", - "\n", - "지역명: 대전광역시 서구\n", - "관련 행정동코드: [3017000000, 3017051000, 3017052000, 3017053000, 3017053500, 3017054000, 3017055000, 3017055500, 3017056000, 3017057000, 3017057500, 3017058100, 3017058200, 3017058600, 3017058700, 3017058800, 3017059000, 3017059300, 3017059600, 3017059700, 3017060000, 3017063000, 3017064000, 3017065000, 3017066000]\n", - "총 거주인원: 5536840.0 , 20/50/60대 거주인원: 674439.0 \n", - "\n", - "지역명: 대전광역시 대덕구\n", - "관련 행정동코드: [3023000000, 3023051000, 3023052000, 3023052500, 3023053300, 3023054300, 3023054600, 3023055000, 3023056000, 3023057000, 3023058000, 3023060000, 3023061000]\n", - "총 거주인원: 2377042.0 , 20/50/60대 거주인원: 306052.0 \n", - "\n", - "지역명: 대전광역시 중구\n", - "관련 행정동코드: [3014000000, 3014053500, 3014055000, 3014056000, 3014057500, 3014060500, 3014062000, 3014063000, 3014064000, 3014065500, 3014067000, 3014068000, 3014069000, 3014070000, 3014071000, 3014072000, 3014073000, 3014074000]\n", - "총 거주인원: 2922017.0 , 20/50/60대 거주인원: 354956.0 \n", - "\n", - "지역명: 충청남도 금산군\n", - "관련 행정동코드: [4471000000, 4471025000, 4471031000, 4471032000, 4471033000, 4471034000, 4471035000, 4471036000, 4471037000, 4471038000, 4471039000]\n", - "총 거주인원: 554830.0 , 20/50/60대 거주인원: 76765.0 \n", - "\n", - "지역명: 충청북도 영동군\n", - "관련 행정동코드: [4374000000, 4374025000, 4374031000, 4374032000, 4374033500, 4374034000, 4374035000, 4374036000, 4374037000, 4374038000, 4374039000, 4374040000]\n", - "총 거주인원: 450046.0 , 20/50/60대 거주인원: 61760.0 \n", - "\n", - "지역명: 전라북도 무주군\n", - "관련 행정동코드: [4573000000, 4573025000, 4573031000, 4573032000, 4573033000, 4573034000, 4573035000]\n", - "총 거주인원: 211731.0 , 20/50/60대 거주인원: 26123.0 \n", - "\n", - "[3048131, 3820791, 5536840, 2377042, 2922017, 554830, 450046, 211731]\n", - "[290518, 424653, 674439, 306052, 354956, 76765, 61760, 26123]\n" - ] - } - ], - "source": [ - "# 각 지역 거주인원\n", - "def get_residents_num(lst):\n", - " residents_num = []\n", - " residents_num_256 = []\n", - " for name in lst:\n", - " print(\"지역명:\", name)\n", - " codes = address[address['시도 시군구'] == name]['행정동코드'].unique().tolist()\n", - " print(\"관련 행정동코드: \", codes)\n", - " # 지역 내 행정동코드들의 거주인원 합산\n", - " cnt_all = 0\n", - " cnt_256 = 0\n", - " for code in codes:\n", - " tmp_for_all = avg_stay_cnts_all[avg_stay_cnts_all['hdong_cd']==code]\n", - " if not tmp_for_all.empty:\n", - " cnt_all += tmp_for_all['stay_cnts'].iloc[0]\n", - " tmp_for_256 = avg_stay_cnts_age[avg_stay_cnts_age['hdong_cd']==code]\n", - " if not tmp_for_256.empty:\n", - " cnt_256 += tmp_for_256['stay_cnts'].iloc[0]\n", - " residents_num.append(int(cnt_all))\n", - " residents_num_256.append(int(cnt_256))\n", - " print(\"총 거주인원:\", cnt_all, \", 20/50/60대 거주인원:\", cnt_256, \"\\n\")\n", - " return residents_num, residents_num_256\n", - "\n", - "# 대전 지역으로 테스트\n", - "daejun = ['세종특별자치시', '대전광역시 유성구', '대전광역시 서구', '대전광역시 대덕구', '대전광역시 중구', '충청남도 금산군', '충청북도 영동군', '전라북도 무주군']\n", - "\n", - "daejun_residents_num, daejun_residents_num_256 = get_residents_num(daejun)\n", - "\n", - "print(daejun_residents_num)\n", - "print(daejun_residents_num_256)" - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "지역명: 세종특별자치시\n", - "관련 행정동코드: [3600000000, 3611000000, 3611025000, 3611031000, 3611032000, 3611033000, 3611034000, 3611035000, 3611036000, 3611037000, 3611038000, 3611039000, 3611051000, 3611051500, 3611051800, 3611052000, 3611052300, 3611052500, 3611053000, 3611054000, 3611055000, 3611055500, 3611055600, 3611056000, 3611057000, 3611058000]\n", - "총 방문객 수: 758 , 20/50/60대 방문객 수: 41 \n", - "\n", - "지역명: 대전광역시 유성구\n", - "관련 행정동코드: [3020000000, 3020052000, 3020052600, 3020052700, 3020053000, 3020054000, 3020054600, 3020054700, 3020054800, 3020055000, 3020057000, 3020058000, 3020060000, 3020061000]\n", - "총 방문객 수: 895 , 20/50/60대 방문객 수: 189 \n", - "\n", - "지역명: 대전광역시 서구\n", - "관련 행정동코드: [3017000000, 3017051000, 3017052000, 3017053000, 3017053500, 3017054000, 3017055000, 3017055500, 3017056000, 3017057000, 3017057500, 3017058100, 3017058200, 3017058600, 3017058700, 3017058800, 3017059000, 3017059300, 3017059600, 3017059700, 3017060000, 3017063000, 3017064000, 3017065000, 3017066000]\n", - "총 방문객 수: 1182 , 20/50/60대 방문객 수: 304 \n", - "\n", - "지역명: 대전광역시 대덕구\n", - "관련 행정동코드: [3023000000, 3023051000, 3023052000, 3023052500, 3023053300, 3023054300, 3023054600, 3023055000, 3023056000, 3023057000, 3023058000, 3023060000, 3023061000]\n", - "총 방문객 수: 375 , 20/50/60대 방문객 수: 60 \n", - "\n", - "지역명: 대전광역시 중구\n", - "관련 행정동코드: [3014000000, 3014053500, 3014055000, 3014056000, 3014057500, 3014060500, 3014062000, 3014063000, 3014064000, 3014065500, 3014067000, 3014068000, 3014069000, 3014070000, 3014071000, 3014072000, 3014073000, 3014074000]\n", - "총 방문객 수: 602 , 20/50/60대 방문객 수: 109 \n", - "\n", - "지역명: 충청남도 금산군\n", - "관련 행정동코드: [4471000000, 4471025000, 4471031000, 4471032000, 4471033000, 4471034000, 4471035000, 4471036000, 4471037000, 4471038000, 4471039000]\n", - "총 방문객 수: 538 , 20/50/60대 방문객 수: 92 \n", - "\n", - "지역명: 충청북도 영동군\n", - "관련 행정동코드: [4374000000, 4374025000, 4374031000, 4374032000, 4374033500, 4374034000, 4374035000, 4374036000, 4374037000, 4374038000, 4374039000, 4374040000]\n", - "총 방문객 수: 618 , 20/50/60대 방문객 수: 129 \n", - "\n", - "지역명: 전라북도 무주군\n", - "관련 행정동코드: [4573000000, 4573025000, 4573031000, 4573032000, 4573033000, 4573034000, 4573035000]\n", - "총 방문객 수: 24957 , 20/50/60대 방문객 수: 4768 \n", - "\n", - "[758, 895, 1182, 375, 602, 538, 618, 24957]\n", - "[41, 189, 304, 60, 109, 92, 129, 4768]\n" - ] - } - ], - "source": [ - "# 각 지역 방문객\n", - "def get_visitors_num(lst):\n", - " visitors_num = []\n", - " visitors_num_256 = []\n", - " for name in lst:\n", - " print(\"지역명:\", name)\n", - " codes = address[address['시도 시군구'] == name]['행정동코드'].unique().tolist()\n", - " print(\"관련 행정동코드: \", codes)\n", - " # 지역 내 행정동코드들의 방문객 합산\n", - " cnt_all = 0\n", - " cnt_256 = 0\n", - " for code in codes:\n", - " tmp_for_all = sum_od_cnts_all[sum_od_cnts_all['origin_hdong_cd']==code]\n", - " if not tmp_for_all.empty:\n", - " cnt_all += tmp_for_all['od_cnts'].iloc[0]\n", - " tmp_for_256 = sum_od_cnts_age[sum_od_cnts_age['origin_hdong_cd']==code]\n", - " if not tmp_for_256.empty:\n", - " cnt_256 += tmp_for_256['od_cnts'].iloc[0]\n", - " visitors_num.append(int(cnt_all))\n", - " visitors_num_256.append(int(cnt_256))\n", - " print(\"총 방문객 수:\", cnt_all, \", 20/50/60대 방문객 수:\", cnt_256, \"\\n\")\n", - " return visitors_num, visitors_num_256\n", - "\n", - "# 대전 지역으로 테스트\n", - "daejun_visitors_num, daejun_visitors_num_256 = get_visitors_num(daejun)\n", - "\n", - "print(daejun_visitors_num)\n", - "print(daejun_visitors_num_256)" - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
origin_hdong_cd시도 시군구od_cnts
01135064000서울특별시 노원구6
11138051000서울특별시 은평구5
21141062000서울특별시 서대문구7
31141072000서울특별시 서대문구14
41144060000서울특별시 마포구8
............
2734888025000경상남도 거창군45
2744888034000경상남도 거창군8
2754888037000경상남도 거창군8
2764888040000경상남도 거창군7
2774889045000경상남도 합천군8
\n", - "

278 rows × 3 columns

\n", - "
" - ], - "text/plain": [ - " origin_hdong_cd 시도 시군구 od_cnts\n", - "0 1135064000 서울특별시 노원구 6\n", - "1 1138051000 서울특별시 은평구 5\n", - "2 1141062000 서울특별시 서대문구 7\n", - "3 1141072000 서울특별시 서대문구 14\n", - "4 1144060000 서울특별시 마포구 8\n", - ".. ... ... ...\n", - "273 4888025000 경상남도 거창군 45\n", - "274 4888034000 경상남도 거창군 8\n", - "275 4888037000 경상남도 거창군 8\n", - "276 4888040000 경상남도 거창군 7\n", - "277 4889045000 경상남도 합천군 8\n", - "\n", - "[278 rows x 3 columns]" - ] - }, - "execution_count": 70, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 전체 데이터에서 20대 어디서 오는지 확인\n", - "age20 = df_od[df_od['age']==2][['origin_hdong_cd', 'date', 'age', 'od_cnts']]\n", - "age20 = age20.groupby(['origin_hdong_cd'])['od_cnts'].sum().reset_index()\n", - "unique_address = address.drop_duplicates(subset=['행정동코드'])\n", - "age20eng = pd.merge(age20, unique_address[['행정동코드','시도 시군구']], 'left', left_on='origin_hdong_cd', right_on='행정동코드')\n", - "age20eng = age20eng[['origin_hdong_cd', '시도 시군구', 'od_cnts']]\n", - "age20eng" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": {}, - "outputs": [], - "source": [ - "# 지역별 20/50/60대 방문율\n", - "def get_proportion(a, b, n):\n", - " lst = []\n", - " for i in range(n):\n", - " lst.append(a[i] / b[i])\n", - " return lst" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "def softmax(weights):\n", - " e = np.exp(weights - np.max(weights)) # 오버플로 방지를 위해 최대값을 빼줌\n", - " return e / e.sum()\n", - "\n", - "def get_weights(nodes, weights): \n", - " residents_num, residents_num_256 = get_residents_num(nodes)\n", - " visitors_num, visitors_num_256 = get_visitors_num(nodes)\n", - " # 방식1: 해당 지역의 2/5/60대 거주민 중 축제방문객 비율. (2/5/60대 축제 관심도가 높은 곳을 우선)\n", - " # weight_age = get_proportion(visitors_num_256, residents_num_256, len(nodes))\n", - " # 방식2: 해당 지역의 전체 축제방문객 중 2/5/60대 비율. ()\n", - " weight_age = get_proportion(visitors_num_256, visitors_num, len(nodes))\n", - " # 방식3: 해당 지역의 거주민 중 2/5/60대 거주민 비율. (단순히 2/5/60대가 많은 곳을 우선)\n", - " # weight_age = get_proportion(residents_num_256, residents_num, len(nodes))\n", - "\n", - " for i, w in enumerate(weight_age):\n", - " weights[i] += w\n", - "\n", - " softmax_weights = softmax(weights)\n", - " \n", - " stations = {nodes: weight for nodes, weight in zip(nodes, softmax_weights)}\n", - " print(stations)\n", - "\n", - " return stations" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 탐색" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "metadata": {}, - "outputs": [], - "source": [ - "import networkx as nx\n", - "\n", - "# 모든 경로 찾기\n", - "def find_all_routes(G, start, target, visited=None, path=None):\n", - " if visited is None:\n", - " visited = set()\n", - " if path is None:\n", - " path = []\n", - "\n", - " visited.add(start)\n", - " path.append(start)\n", - "\n", - " if start == target:\n", - " yield path.copy()\n", - " else:\n", - " for neighbor in G.neighbors(start):\n", - " if neighbor not in visited:\n", - " yield from find_all_routes(G, neighbor, target, visited, path)\n", - "\n", - " path.pop()\n", - " visited.remove(start)\n", - "\n", - "# 최적 경로 탐색 함수\n", - "def find_optimal_routes(G, stations, min_threshold, max_threshold, step):\n", - " optimal_routes = []\n", - " max_stops = 0\n", - " explored_routes = set() # 탐색된 경로를 저장할 집합\n", - "\n", - " # 여러 배수 값을 탐색\n", - " for threshold in np.arange(min_threshold, max_threshold + step, step):\n", - "\n", - " for station in stations.keys():\n", - " if station != \"전라북도 무주군\": # 목적지가 아니면\n", - " routes = list(find_all_routes(G, station, \"전라북도 무주군\"))\n", - "\n", - " for route in routes:\n", - " valid_route = True\n", - " total_distance = 0\n", - " total_time = 0\n", - " total_weight = 0 # 가중치 누적\n", - "\n", - " # 각 경로의 모든 구간에 대해 확인\n", - " for i in range(len(route) - 1):\n", - " current_station = route[i]\n", - "\n", - " # 현재 노드에서 무주군으로 바로 이동할 때의 거리와 시간\n", - " if \"전라북도 무주군\" in G[current_station]: # 직접 경로가 있는 경우에만\n", - " direct_distance = G[current_station][\"전라북도 무주군\"]['distance']\n", - " direct_time = G[current_station][\"전라북도 무주군\"]['time']\n", - "\n", - " # 현재 구간의 거리와 시간을 누적\n", - " total_distance += G[route[i]][route[i + 1]]['distance']\n", - " total_time += G[route[i]][route[i + 1]]['time']\n", - "\n", - " # 각 중간 노드에서도 주어진 배수(factor) 조건 확인\n", - " if total_distance > direct_distance * threshold or total_time > direct_time * threshold:\n", - " valid_route = False\n", - " break\n", - "\n", - " # 가중치 누적 (출발지 제외)\n", - " if i > 0:\n", - " total_weight += stations[current_station]\n", - "\n", - " if valid_route:\n", - " # 경로를 집합에 저장할 수 있는 형태로 변환 (정렬하여 순서 상관없이 비교 하기 위함)\n", - " route_tuple = tuple(sorted(route))\n", - "\n", - " # 만약 해당 경로가 이전 배수에서 이미 탐색되었다면 제외\n", - " if route_tuple not in explored_routes:\n", - " explored_routes.add(route_tuple) # 새로운 경로로 저장\n", - "\n", - " # 최대 정거장 수 확인\n", - " if len(route) > max_stops:\n", - " max_stops = len(route)\n", - " optimal_routes = [(route, total_distance, total_time, total_weight, threshold)]\n", - " elif len(route) == max_stops:\n", - " optimal_routes.append((route, total_distance, total_time, total_weight, threshold))\n", - "\n", - " return optimal_routes\n" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [], - "source": [ - "def print_optimal_routes(optimal_routes, name):\n", - " print(f\"{name}지역 최적의 셔틀 노선\")\n", - " if optimal_routes:\n", - " for route, total_distance, total_time, total_weight, threshold in optimal_routes:\n", - " # 총 거리를 km로 변환하고 소수점 아래 두째 자리까지 포맷\n", - " total_distance_km = total_distance / 1000 # 미터를 킬로미터로 변환\n", - " total_distance_formatted = f\"{total_distance_km:.2f} km\"\n", - "\n", - " # 총 시간을 시간과 분으로 변환 (반올림)\n", - " total_time_hours = total_time // 60\n", - " total_time_minutes = round(total_time % 60)\n", - "\n", - " print(f\"최적의 셔틀 노선: {route} | 총 거리: {total_distance_formatted} | 총 시간: {total_time_hours}시간 {total_time_minutes}분 | 총 가중치: {total_weight} | threshold: {threshold:.2f}\")\n", - " else:\n", - " print(\"유효한 경로가 없습니다.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 메인" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "metadata": {}, - "outputs": [], - "source": [ - "# 경유지 리스트\n", - "daejun = ['세종특별자치시', '대전광역시 유성구', '대전광역시 서구', '대전광역시 대덕구', '대전광역시 중구', '충청남도 금산군', '충청북도 영동군', '전라북도 무주군']\n", - "jeonbuk = ['전라북도 군산시', '전라북도 익산시', '전라북도 전주시 완산구', '전라북도 전주시 덕진구', '전라북도 진안군', '전라북도 장수군', '전라북도 무주군']\n", - "# 방문객 수에 근거한 노드별 초기 가중치\n", - "daejun_weights = [1, 2, 2, 1, 1, 1, 2, 0] \n", - "jeonbuk_weights = [1, 1, 2, 2, 1, 2, 0]" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "metadata": {}, - "outputs": [], - "source": [ - "def search(nodes, weights, name):\n", - " # 1. 위경도 좌표 기반으로 경유지 간 이동거리, 이동시간 테이블 만듦\n", - "\n", - " # 경유지 위경도 좌표 \n", - " coordinates = get_coordinates(nodes) \n", - "\n", - " # 네이버 api\n", - " # n_distance_table, n_duration_table = get_path_info(coordinates, naver_request)\n", - " # n_duration_min_table = [[round(value / 3600, 2) for value in row] for row in n_duration_table]\n", - "\n", - " # 카카오 api\n", - " k_distance_table, k_duration_table = get_path_info(coordinates, kakao_request)\n", - " k_duration_min_table = [[round(value / 60, 2) for value in row] for row in k_duration_table]\n", - "\n", - "\n", - " distance_table = k_distance_table\n", - " duration_table = k_duration_min_table\n", - "\n", - " # 2. 정거장 후보지와 가중치\n", - " stations = get_weights(nodes, weights) \n", - "\n", - " # 3. 탐색\n", - " # 그래프 생성\n", - " G = nx.DiGraph()\n", - "\n", - " # 그래프에 엣지 추가\n", - " for i, from_station in enumerate(stations.keys()):\n", - " for j, to_station in enumerate(stations.keys()):\n", - " if i != j:\n", - " G.add_edge(from_station, to_station, \n", - " distance=distance_table[i][j], \n", - " time=duration_table[i][j])\n", - " \n", - " # 탐색 범위 설정 (예: 1.1부터 1.5까지 0.1 간격으로 탐색)\n", - " min_factor = 1.1\n", - " max_factor = 1.5\n", - " step = 0.1\n", - "\n", - " # 최적 경로 탐색 실행\n", - " optimal_routes = find_optimal_routes(G, stations, min_factor, max_factor, step)\n", - "\n", - " # 결과 출력\n", - " print_optimal_routes(optimal_routes, name)" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "지역명: 세종특별자치시\n", - "관련 행정동코드: [3600000000, 3611000000, 3611025000, 3611031000, 3611032000, 3611033000, 3611034000, 3611035000, 3611036000, 3611037000, 3611038000, 3611039000, 3611051000, 3611051500, 3611051800, 3611052000, 3611052300, 3611052500, 3611053000, 3611054000, 3611055000, 3611055500, 3611055600, 3611056000, 3611057000, 3611058000]\n", - "총 거주인원: 3048131.0 , 20/50/60대 거주인원: 290518.0 \n", - "\n", - "지역명: 대전광역시 유성구\n", - "관련 행정동코드: [3020000000, 3020052000, 3020052600, 3020052700, 3020053000, 3020054000, 3020054600, 3020054700, 3020054800, 3020055000, 3020057000, 3020058000, 3020060000, 3020061000]\n", - "총 거주인원: 3820791.0 , 20/50/60대 거주인원: 424653.0 \n", - "\n", - "지역명: 대전광역시 서구\n", - "관련 행정동코드: [3017000000, 3017051000, 3017052000, 3017053000, 3017053500, 3017054000, 3017055000, 3017055500, 3017056000, 3017057000, 3017057500, 3017058100, 3017058200, 3017058600, 3017058700, 3017058800, 3017059000, 3017059300, 3017059600, 3017059700, 3017060000, 3017063000, 3017064000, 3017065000, 3017066000]\n", - "총 거주인원: 5536840.0 , 20/50/60대 거주인원: 674439.0 \n", - "\n", - "지역명: 대전광역시 대덕구\n", - "관련 행정동코드: [3023000000, 3023051000, 3023052000, 3023052500, 3023053300, 3023054300, 3023054600, 3023055000, 3023056000, 3023057000, 3023058000, 3023060000, 3023061000]\n", - "총 거주인원: 2377042.0 , 20/50/60대 거주인원: 306052.0 \n", - "\n", - "지역명: 대전광역시 중구\n", - "관련 행정동코드: [3014000000, 3014053500, 3014055000, 3014056000, 3014057500, 3014060500, 3014062000, 3014063000, 3014064000, 3014065500, 3014067000, 3014068000, 3014069000, 3014070000, 3014071000, 3014072000, 3014073000, 3014074000]\n", - "총 거주인원: 2922017.0 , 20/50/60대 거주인원: 354956.0 \n", - "\n", - "지역명: 충청남도 금산군\n", - "관련 행정동코드: [4471000000, 4471025000, 4471031000, 4471032000, 4471033000, 4471034000, 4471035000, 4471036000, 4471037000, 4471038000, 4471039000]\n", - "총 거주인원: 554830.0 , 20/50/60대 거주인원: 76765.0 \n", - "\n", - "지역명: 충청북도 영동군\n", - "관련 행정동코드: [4374000000, 4374025000, 4374031000, 4374032000, 4374033500, 4374034000, 4374035000, 4374036000, 4374037000, 4374038000, 4374039000, 4374040000]\n", - "총 거주인원: 450046.0 , 20/50/60대 거주인원: 61760.0 \n", - "\n", - "지역명: 전라북도 무주군\n", - "관련 행정동코드: [4573000000, 4573025000, 4573031000, 4573032000, 4573033000, 4573034000, 4573035000]\n", - "총 거주인원: 211731.0 , 20/50/60대 거주인원: 26123.0 \n", - "\n", - "지역명: 세종특별자치시\n", - "관련 행정동코드: [3600000000, 3611000000, 3611025000, 3611031000, 3611032000, 3611033000, 3611034000, 3611035000, 3611036000, 3611037000, 3611038000, 3611039000, 3611051000, 3611051500, 3611051800, 3611052000, 3611052300, 3611052500, 3611053000, 3611054000, 3611055000, 3611055500, 3611055600, 3611056000, 3611057000, 3611058000]\n", - "총 방문객 수: 758 , 20/50/60대 방문객 수: 41 \n", - "\n", - "지역명: 대전광역시 유성구\n", - "관련 행정동코드: [3020000000, 3020052000, 3020052600, 3020052700, 3020053000, 3020054000, 3020054600, 3020054700, 3020054800, 3020055000, 3020057000, 3020058000, 3020060000, 3020061000]\n", - "총 방문객 수: 895 , 20/50/60대 방문객 수: 189 \n", - "\n", - "지역명: 대전광역시 서구\n", - "관련 행정동코드: [3017000000, 3017051000, 3017052000, 3017053000, 3017053500, 3017054000, 3017055000, 3017055500, 3017056000, 3017057000, 3017057500, 3017058100, 3017058200, 3017058600, 3017058700, 3017058800, 3017059000, 3017059300, 3017059600, 3017059700, 3017060000, 3017063000, 3017064000, 3017065000, 3017066000]\n", - "총 방문객 수: 1182 , 20/50/60대 방문객 수: 304 \n", - "\n", - "지역명: 대전광역시 대덕구\n", - "관련 행정동코드: [3023000000, 3023051000, 3023052000, 3023052500, 3023053300, 3023054300, 3023054600, 3023055000, 3023056000, 3023057000, 3023058000, 3023060000, 3023061000]\n", - "총 방문객 수: 375 , 20/50/60대 방문객 수: 60 \n", - "\n", - "지역명: 대전광역시 중구\n", - "관련 행정동코드: [3014000000, 3014053500, 3014055000, 3014056000, 3014057500, 3014060500, 3014062000, 3014063000, 3014064000, 3014065500, 3014067000, 3014068000, 3014069000, 3014070000, 3014071000, 3014072000, 3014073000, 3014074000]\n", - "총 방문객 수: 602 , 20/50/60대 방문객 수: 109 \n", - "\n", - "지역명: 충청남도 금산군\n", - "관련 행정동코드: [4471000000, 4471025000, 4471031000, 4471032000, 4471033000, 4471034000, 4471035000, 4471036000, 4471037000, 4471038000, 4471039000]\n", - "총 방문객 수: 538 , 20/50/60대 방문객 수: 92 \n", - "\n", - "지역명: 충청북도 영동군\n", - "관련 행정동코드: [4374000000, 4374025000, 4374031000, 4374032000, 4374033500, 4374034000, 4374035000, 4374036000, 4374037000, 4374038000, 4374039000, 4374040000]\n", - "총 방문객 수: 618 , 20/50/60대 방문객 수: 129 \n", - "\n", - "지역명: 전라북도 무주군\n", - "관련 행정동코드: [4573000000, 4573025000, 4573031000, 4573032000, 4573033000, 4573034000, 4573035000]\n", - "총 방문객 수: 24957 , 20/50/60대 방문객 수: 4768 \n", - "\n", - "{'세종특별자치시': 0.0690751357958365, '대전광역시 유성구': 0.21970336845100272, '대전광역시 서구': 0.23004991987725867, '대전광역시 대덕구': 0.07679235776727512, '대전광역시 중구': 0.07842699953443562, '충청남도 금산군': 0.07764202535394257, '충청북도 영동군': 0.21916897136041014, '전라북도 무주군': 0.029141221859838744}\n", - "대전지역 최적의 셔틀 노선\n", - "최적의 셔틀 노선: ['대전광역시 중구', '대전광역시 대덕구', '대전광역시 서구', '전라북도 무주군'] | 총 거리: 75.06 km | 총 시간: 1.0시간 24분 | 총 가중치: 0.3068422776445338 | threshold: 1.40\n", - "최적의 셔틀 노선: ['대전광역시 유성구', '대전광역시 서구', '대전광역시 대덕구', '전라북도 무주군'] | 총 거리: 71.59 km | 총 시간: 1.0시간 17분 | 총 가중치: 0.3068422776445338 | threshold: 1.50\n", - "최적의 셔틀 노선: ['대전광역시 유성구', '대전광역시 서구', '대전광역시 중구', '전라북도 무주군'] | 총 거리: 64.15 km | 총 시간: 1.0시간 21분 | 총 가중치: 0.3084769194116943 | threshold: 1.50\n", - "최적의 셔틀 노선: ['대전광역시 유성구', '대전광역시 대덕구', '대전광역시 중구', '전라북도 무주군'] | 총 거리: 65.30 km | 총 시간: 1.0시간 19분 | 총 가중치: 0.15521935730171074 | threshold: 1.50\n" - ] - } - ], - "source": [ - "search(daejun, daejun_weights, \"대전\")" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "지역명: 전라북도 군산시\n", - "관련 행정동코드: [4513000000, 4513025000, 4513031000, 4513032000, 4513033000, 4513034000, 4513035000, 4513036000, 4513037000, 4513038000, 4513039000, 4513040000, 4513051500, 4513053000, 4513055000, 4513056000, 4513060500, 4513064000, 4513065000, 4513066000, 4513067000, 4513068000, 4513069000, 4513070100, 4513070200, 4513070300, 4513071000, 4513072000]\n", - "총 거주인원: 3132379.0 , 20/50/60대 거주인원: 378252.0 \n", - "\n", - "지역명: 전라북도 익산시\n", - "관련 행정동코드: [4514000000, 4514025000, 4514031000, 4514032000, 4514033000, 4514034000, 4514035000, 4514036000, 4514037000, 4514038000, 4514039000, 4514040000, 4514041000, 4514042000, 4514043000, 4514044000, 4514052000, 4514053000, 4514056000, 4514057000, 4514058000, 4514059500, 4514061000, 4514062000, 4514064600, 4514064700, 4514065200, 4514065600, 4514067000, 4514069000]\n", - "총 거주인원: 3328022.0 , 20/50/60대 거주인원: 420440.0 \n", - "\n", - "지역명: 전라북도 전주시 완산구\n", - "관련 행정동코드: [4511100000, 4511151000, 4511153000, 4511160500, 4511163500, 4511165000, 4511166000, 4511167100, 4511167200, 4511168000, 4511169100, 4511169200, 4511170100, 4511170200, 4511170300, 4511171100, 4511171200, 4511171300, 4511171400, 4511173000]\n", - "총 거주인원: 4116105.0 , 20/50/60대 거주인원: 486210.0 \n", - "\n", - "지역명: 전라북도 전주시 덕진구\n", - "관련 행정동코드: [4511300000, 4511352500, 4511354000, 4511355000, 4511356000, 4511357000, 4511358000, 4511359000, 4511360000, 4511361100, 4511361200, 4511362000, 4511364100, 4511364200, 4511365000, 4511366500, 4511367000]\n", - "총 거주인원: 3377434.0 , 20/50/60대 거주인원: 400961.0 \n", - "\n", - "지역명: 전라북도 진안군\n", - "관련 행정동코드: [4572000000, 4572025000, 4572031000, 4572032000, 4572033000, 4572034000, 4572035000, 4572036000, 4572037000, 4572038000, 4572039000, 4572040000]\n", - "총 거주인원: 179814.0 , 20/50/60대 거주인원: 24852.0 \n", - "\n", - "지역명: 전라북도 장수군\n", - "관련 행정동코드: [4574000000, 4574025000, 4574031000, 4574032000, 4574033500, 4574034000, 4574035000, 4574036000]\n", - "총 거주인원: 201626.0 , 20/50/60대 거주인원: 26141.0 \n", - "\n", - "지역명: 전라북도 무주군\n", - "관련 행정동코드: [4573000000, 4573025000, 4573031000, 4573032000, 4573033000, 4573034000, 4573035000]\n", - "총 거주인원: 211731.0 , 20/50/60대 거주인원: 26123.0 \n", - "\n", - "지역명: 전라북도 군산시\n", - "관련 행정동코드: [4513000000, 4513025000, 4513031000, 4513032000, 4513033000, 4513034000, 4513035000, 4513036000, 4513037000, 4513038000, 4513039000, 4513040000, 4513051500, 4513053000, 4513055000, 4513056000, 4513060500, 4513064000, 4513065000, 4513066000, 4513067000, 4513068000, 4513069000, 4513070100, 4513070200, 4513070300, 4513071000, 4513072000]\n", - "총 방문객 수: 14 , 20/50/60대 방문객 수: 14 \n", - "\n", - "지역명: 전라북도 익산시\n", - "관련 행정동코드: [4514000000, 4514025000, 4514031000, 4514032000, 4514033000, 4514034000, 4514035000, 4514036000, 4514037000, 4514038000, 4514039000, 4514040000, 4514041000, 4514042000, 4514043000, 4514044000, 4514052000, 4514053000, 4514056000, 4514057000, 4514058000, 4514059500, 4514061000, 4514062000, 4514064600, 4514064700, 4514065200, 4514065600, 4514067000, 4514069000]\n", - "총 방문객 수: 21 , 20/50/60대 방문객 수: 7 \n", - "\n", - "지역명: 전라북도 전주시 완산구\n", - "관련 행정동코드: [4511100000, 4511151000, 4511153000, 4511160500, 4511163500, 4511165000, 4511166000, 4511167100, 4511167200, 4511168000, 4511169100, 4511169200, 4511170100, 4511170200, 4511170300, 4511171100, 4511171200, 4511171300, 4511171400, 4511173000]\n", - "총 방문객 수: 96 , 20/50/60대 방문객 수: 10 \n", - "\n", - "지역명: 전라북도 전주시 덕진구\n", - "관련 행정동코드: [4511300000, 4511352500, 4511354000, 4511355000, 4511356000, 4511357000, 4511358000, 4511359000, 4511360000, 4511361100, 4511361200, 4511362000, 4511364100, 4511364200, 4511365000, 4511366500, 4511367000]\n", - "총 방문객 수: 127 , 20/50/60대 방문객 수: 47 \n", - "\n", - "지역명: 전라북도 진안군\n", - "관련 행정동코드: [4572000000, 4572025000, 4572031000, 4572032000, 4572033000, 4572034000, 4572035000, 4572036000, 4572037000, 4572038000, 4572039000, 4572040000]\n", - "총 방문객 수: 277 , 20/50/60대 방문객 수: 62 \n", - "\n", - "지역명: 전라북도 장수군\n", - "관련 행정동코드: [4574000000, 4574025000, 4574031000, 4574032000, 4574033500, 4574034000, 4574035000, 4574036000]\n", - "총 방문객 수: 436 , 20/50/60대 방문객 수: 160 \n", - "\n", - "지역명: 전라북도 무주군\n", - "관련 행정동코드: [4573000000, 4573025000, 4573031000, 4573032000, 4573033000, 4573034000, 4573035000]\n", - "총 방문객 수: 24957 , 20/50/60대 방문객 수: 4768 \n", - "\n", - "{'전라북도 군산시': 0.16290893515183477, '전라북도 익산시': 0.08364023615032236, '전라북도 전주시 완산구': 0.18079395836979623, '전라북도 전주시 덕진구': 0.23586747596828486, '전라북도 진안군': 0.0749647557263006, '전라북도 장수군': 0.23513594628281875, '전라북도 무주군': 0.026688692350642314}\n", - "전북지역 최적의 셔틀 노선\n", - "최적의 셔틀 노선: ['전라북도 전주시 완산구', '전라북도 전주시 덕진구', '전라북도 무주군'] | 총 거리: 75.56 km | 총 시간: 1.0시간 32분 | 총 가중치: 0.23586747596828486 | threshold: 1.20\n", - "최적의 셔틀 노선: ['전라북도 군산시', '전라북도 익산시', '전라북도 무주군'] | 총 거리: 118.16 km | 총 시간: 2.0시간 4분 | 총 가중치: 0.08364023615032236 | threshold: 1.30\n", - "최적의 셔틀 노선: ['전라북도 전주시 덕진구', '전라북도 군산시', '전라북도 무주군'] | 총 거리: 153.42 km | 총 시간: 2.0시간 22분 | 총 가중치: 0.16290893515183477 | threshold: 1.40\n", - "최적의 셔틀 노선: ['전라북도 전주시 덕진구', '전라북도 익산시', '전라북도 무주군'] | 총 거리: 118.14 km | 총 시간: 2.0시간 10분 | 총 가중치: 0.08364023615032236 | threshold: 1.40\n", - "최적의 셔틀 노선: ['전라북도 익산시', '전라북도 전주시 완산구', '전라북도 무주군'] | 총 거리: 104.53 km | 총 시간: 2.0시간 2분 | 총 가중치: 0.18079395836979623 | threshold: 1.50\n", - "최적의 셔틀 노선: ['전라북도 전주시 완산구', '전라북도 군산시', '전라북도 무주군'] | 총 거리: 163.53 km | 총 시간: 2.0시간 29분 | 총 가중치: 0.16290893515183477 | threshold: 1.50\n" - ] - } - ], - "source": [ - "search(jeonbuk, jeonbuk_weights, \"전북\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.7" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/dataloader/dataloader.py b/dataloader/dataloader.py index 6c0b054..7f1f4ec 100644 --- a/dataloader/dataloader.py +++ b/dataloader/dataloader.py @@ -4,34 +4,21 @@ import json sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))) -from preprocess.dataload_preprocess import preprocess_od, preprocess_stay +from preprocess.dataload_preprocess import preprocess_od, preprocess_stay, preprocess_address, preprocess_visitor_city # paths 파일 불러오기 with open('config.json', 'r', encoding='utf-8') as f: paths = json.load(f) - -# 'data/raw_data' 경로 설정 -root_dir = paths['root_dir'] -visitor_city = pd.read_csv(os.path.join(root_dir, "city_of_festival_visitors.csv")) # 무주축제 방문객 top 18 지역들 (시군구명,od_cnts,시도명,행정동코드,위도,경도) -address = pd.read_csv(os.path.join(root_dir, "address_with_lon_lat_final.csv")) # 행정동코드 + 위도경도 (행정동코드,시도명,시군구명,읍면동명,동리명,위도,경도) -mooju = set(list(address[address['시군구명'] == '무주군']['행정동코드'])) # 무주군 행정동코드 -other_city = list(address.merge(visitor_city, on=['시도명', '시군구명'])['행정동코드_x']) # 다른 지역들 행정동코드 모음 - -visitor_city['시도 시군구'] = visitor_city['시도명'].fillna('') + ' ' + visitor_city['시군구명'].fillna('') -visitor_city['시도 시군구'] = visitor_city['시도 시군구'].str.strip() # 양쪽 값이 모두 null인 경우 빈 문자열 처리 - -address['시도 시군구'] = address['시도명'].fillna('') + ' ' + address['시군구명'].fillna('') -address['시도 시군구'] = address['시도 시군구'].str.strip() # 양쪽 값이 모두 null인 경우 빈 문자열 처리 - -# raw_data 경로 설정 -od_dir = paths['od_dir'] -stay_dir = paths['stay_dir'] - - -# festival od data load +# 축제기간의 od 데이터 로드 함수 def load_od(): df_od = pd.DataFrame() + od_dir = paths['od_dir'] + + address_df = load_address() + # 무주군의 행정동코드 추출 + mooju = set(address_df[address_df['시군구명'] == '무주군']['행정동코드']) + for dirpath, dirnames, filenames in os.walk(od_dir): for filename in tqdm(filenames, desc="load od data", unit="file"): if filename.endswith('.csv'): @@ -55,9 +42,10 @@ def load_od(): df_od = pd.concat([df_od, globals()[f'df_{mmdd_str}']]) return df_od -# festival stay data load +# stay 데이터 로드 함수 def load_stay(): df_stay = pd.DataFrame() + stay_dir = paths['stay_dir'] for dirpath, dirnames, filenames in os.walk(stay_dir): for filename in tqdm(filenames, desc="load stay data", unit="file"): if filename.endswith('.csv'): @@ -79,4 +67,24 @@ def load_stay(): # 동적으로 변수 생성 (예: df_0902) globals()[f'df_{mmdd_str}'] = filtered_data df_stay = pd.concat([df_stay, globals()[f'df_{mmdd_str}']]) - return df_stay \ No newline at end of file + return df_stay + +# address 데이터 로드 함수 +def load_address(): + address_path = os.path.join(paths['root_dir'], "address_with_lon_lat_final.csv") + address_df = pd.read_csv(address_path) + + address_df = preprocess_address(address_df) + + return address_df + + + +# visitor_city 데이터 로드 함수 +def load_visitor_city(): + visitor_city_path = os.path.join(paths['root_dir'], "city_of_festival_visitors.csv") + visitor_city_df = pd.read_csv(visitor_city_path) + + visitor_city_df = preprocess_visitor_city(visitor_city_df) + + return visitor_city_df \ No newline at end of file diff --git a/func/weight_by_region.py b/func/weight_by_region.py index 30b71bf..14d6c1e 100644 --- a/func/weight_by_region.py +++ b/func/weight_by_region.py @@ -5,44 +5,42 @@ sys.path.append(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))) -from dataloader.dataloader import load_od, load_stay +from dataloader.dataloader import load_od, load_stay, load_address -# 'data/raw_data' 경로 설정 -with open('config.json', 'r', encoding='utf-8') as f: - paths = json.load(f) - -root_dir = paths['root_dir'] -address = pd.read_csv(os.path.join(root_dir, "address_with_lon_lat_final.csv")) # 행정동코드 + 위도경도 (행정동코드,시도명,시군구명,읍면동명,동리명,위도,경도) -address['시도 시군구'] = address['시도명'].fillna('') + ' ' + address['시군구명'].fillna('') -address['시도 시군구'] = address['시도 시군구'].str.strip() # 양쪽 값이 모두 null인 경우 빈 문자열 처리 - - -#### -df_od = load_od() -df_stay = load_stay() +def total_number_of_residents_by_region(): + df_stay = load_stay() + + # 지역별 전체 거주민 수 + df_stay_all = df_stay.groupby(['hdong_cd', 'date'])['stay_cnts'].sum().reset_index() # 날짜별 거주인구 합산 + avg_stay_cnts_all = round(df_stay_all.groupby('hdong_cd')['stay_cnts'].mean().reset_index(), 0) # 하루 평균 거주인구 -# 지역별 전체 거주민 수 -df_stay_all = df_stay.groupby(['hdong_cd', 'date'])['stay_cnts'].sum().reset_index() # 날짜별 거주인구 합산 -avg_stay_cnts_all = round(df_stay_all.groupby('hdong_cd')['stay_cnts'].mean().reset_index(), 0) # 하루 평균 거주인구 + # 지역별 20대,50대,60대 거주민 수 + df_stay_age = df_stay[df_stay['age'].isin([2,5,6])] # 날짜별 20대 거주인구 + avg_stay_cnts_age = round(df_stay_age.groupby(['hdong_cd'])['stay_cnts'].mean().reset_index(), 0) # 하루 평균 20대 거주인구 -# 지역별 20대,50대,60대 거주민 수 -df_stay_age = df_stay[df_stay['age'].isin([2,5,6])] # 날짜별 20대 거주인구 -avg_stay_cnts_age = round(df_stay_age.groupby(['hdong_cd'])['stay_cnts'].mean().reset_index(), 0) # 하루 평균 20대 거주인구 + return avg_stay_cnts_all, avg_stay_cnts_age -# 지역별 무주축제 방문객 인원수 -df_od_group = df_od.groupby(['origin_hdong_cd', 'date', 'age'])['od_cnts'].sum().reset_index() -df_od_all = df_od_group.groupby(['origin_hdong_cd', 'date'])['od_cnts'].sum().reset_index() # 날짜별 방문객수 합산 -sum_od_cnts_all = round(df_od_all.groupby(['origin_hdong_cd'])['od_cnts'].sum().reset_index(), 0) # 해당 지역에서 온 전체 방문객수 +def number_of_visitors_to_Muju_by_region(): + df_od = load_od() + + # 지역별 무주축제 방문객 인원수 + df_od_group = df_od.groupby(['origin_hdong_cd', 'date', 'age'])['od_cnts'].sum().reset_index() + df_od_all = df_od_group.groupby(['origin_hdong_cd', 'date'])['od_cnts'].sum().reset_index() # 날짜별 방문객수 합산 + sum_od_cnts_all = round(df_od_all.groupby(['origin_hdong_cd'])['od_cnts'].sum().reset_index(), 0) # 해당 지역에서 온 전체 방문객수 -# 지역별 20대,50대,60대 무주축제방문객 수 -df_od_age = df_od_group[df_od_group['age'].isin([2,5,6])] -sum_od_cnts_age = round(df_od_age.groupby('origin_hdong_cd')['od_cnts'].sum().reset_index(), 0) + # 지역별 20대,50대,60대 무주축제방문객 수 + df_od_age = df_od_group[df_od_group['age'].isin([2,5,6])] + sum_od_cnts_age = round(df_od_age.groupby('origin_hdong_cd')['od_cnts'].sum().reset_index(), 0) + return sum_od_cnts_all, sum_od_cnts_age # 각 지역 거주인원 def get_residents_num(lst): + address = load_address() + avg_stay_cnts_all, avg_stay_cnts_age = total_number_of_residents_by_region() residents_num = [] residents_num_256 = [] + for name in lst: #print("지역명:", name) codes = address[address['시도 시군구'] == name]['행정동코드'].unique().tolist() @@ -64,8 +62,11 @@ def get_residents_num(lst): # 각 지역 방문객 def get_visitors_num(lst): + address = load_address() + sum_od_cnts_all, sum_od_cnts_age = number_of_visitors_to_Muju_by_region() visitors_num = [] visitors_num_256 = [] + for name in lst: #print("지역명:", name) codes = address[address['시도 시군구'] == name]['행정동코드'].unique().tolist() @@ -114,8 +115,5 @@ def get_weights(nodes, weights): stations = {nodes: weight for nodes, weight in zip(nodes, softmax_weights)} stations['전라북도 무주군'] = 0 # 무주군은 목적지이므로 가중치 0 으로 설정 - - #print("정류장 가중치") - #print(stations) return stations \ No newline at end of file diff --git a/preprocess/dataload_preprocess.py b/preprocess/dataload_preprocess.py index 321254d..14dd01f 100644 --- a/preprocess/dataload_preprocess.py +++ b/preprocess/dataload_preprocess.py @@ -29,4 +29,21 @@ def preprocess_stay(df): filtered_df = df[(df['purpose'] == 0)] grouped_residents = filtered_df.groupby(['hdong_cd', 'date', 'age'])['stay_cnts'].sum().reset_index() - return grouped_residents \ No newline at end of file + return grouped_residents + + +def preprocess_address(df): + + # 전처리: 시도명과 시군구명 결합 및 null 값 처리 + df['시도 시군구'] = df['시도명'].fillna('') + ' ' + df['시군구명'].fillna('') + df['시도 시군구'] = df['시도 시군구'].str.strip() # 양쪽 값이 모두 null인 경우 빈 문자열 처리 + + return df + +def preprocess_visitor_city(df): + + # 전처리: 시도명과 시군구명 결합 및 null 값 처리 + df['시도 시군구'] = df['시도명'].fillna('') + ' ' + df['시군구명'].fillna('') + df['시도 시군구'] = df['시도 시군구'].str.strip() # 양쪽 값이 모두 null인 경우 빈 문자열 처리 + + return df \ No newline at end of file diff --git a/visualization/chanwoong.ipynb b/visualization/chanwoong.ipynb deleted file mode 100644 index 2fe3e8d..0000000 --- a/visualization/chanwoong.ipynb +++ /dev/null @@ -1,946 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 내/외부인 비율 파이차트" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 축제 방문객 내/외부인 분포 파악" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "데이터프레임 df_0901가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0902가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0903가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0904가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0905가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0906가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0907가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0908가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0909가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0910가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0911가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0912가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0913가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0914가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0915가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0916가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0917가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0918가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0919가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0920가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0921가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0922가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0923가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0924가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0925가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0926가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0927가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0928가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0929가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_0930가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1001가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1002가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1003가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1004가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1005가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1006가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1007가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1008가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1009가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1010가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1011가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1012가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1013가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1014가 생성되었고, 기존데이터프레임에 포함합니다.\n", - "데이터프레임 df_1015가 생성되었고, 기존데이터프레임에 포함합니다.\n" - ] - } - ], - "source": [ - "import os\n", - "import pandas as pd\n", - "\n", - "# 'data/raw_data' 경로 설정\n", - "root_dir = '../data/raw_data'\n", - "df = pd.DataFrame()\n", - "address = pd.read_csv(\"../data/address_with_lon_lat_final.csv\")\n", - "mooju = set(list(address[address['시군구명'] == '무주군']['행정동코드']))\n", - "\n", - "# 전처리코드는 모두 여기에\n", - "def preprocess(df):\n", - " # dest_hdong_cd 값이 축제가 열리는 곳인 무주군 데이터만 필터링\n", - " filtered_df = df[df['dest_hdong_cd'].isin(mooju)]\n", - "\n", - " #filtered_df = df[df['dest_hdong_cd'] == 4573025000]\n", - " \"\"\"#filtered_df = df[df['dest_hdong_cd'] == 4573025000]\n", - " df = pd.merge(df, address[['행정동코드', '시도명', '시군구명']], \n", - " left_on='dest_hdong_cd', right_on='행정동코드', how='left')\n", - "\n", - " # 병합 후 불필요한 '행정동코드' 컬럼 제거 (필요에 따라)\n", - " df = df.drop(columns=['행정동코드'])\n", - "\n", - " filtered_df = df[df['시군구명'] == '무주군']\n", - "\n", - " filtered_df = filtered_df.drop(columns=['시도명', '시군구명'])\"\"\"\n", - "\n", - " # 타 지역에서 온 데이터만 필터링\n", - " #filtered_df = filtered_df[filtered_df['origin_hdong_cd'] != filtered_df['dest_hdong_cd']]\n", - " #filtered_df = filtered_df[~filtered_df['origin_hdong_cd'].isin(mooju)]\n", - "\n", - " # 체류목적이 3(쇼핑여가), 4(기타), 5(여행) 인경우만 \n", - " filtered_df = filtered_df[(filtered_df['dest_purpose'] == 3) | \n", - " (filtered_df['dest_purpose'] == 4) | \n", - " (filtered_df['dest_purpose'] == 5)]\n", - "\n", - " return filtered_df\n", - "\n", - "# 'od'로 시작하는 폴더 내의 모든 CSV 파일 처리\n", - "for dirpath, dirnames, filenames in os.walk(root_dir):\n", - " if os.path.basename(dirpath).startswith('od'):\n", - " for filename in filenames:\n", - " if filename.endswith('.csv'):\n", - " # 파일 이름에서 날짜 추출\n", - " date_str = filename.split('_')[1]\n", - " \n", - " # 파일 경로 설정 및 CSV 읽기\n", - " file_path = os.path.join(dirpath, filename)\n", - " csv_data = pd.read_csv(file_path)\n", - " \n", - " # 전처리\n", - " filtered_data = preprocess(csv_data)\n", - "\n", - " # 날짜에서 월일(MMDD) 부분 추출\n", - " mmdd_str = date_str[4:] # 'YYYYMMDD'에서 마지막 네 자리 'MMDD' 추출\n", - " \n", - " # 동적으로 변수 생성 (예: df_0901)\n", - " globals()[f'df_{mmdd_str}'] = filtered_data\n", - " \n", - " # 데이터프레임 이름 출력 (예: df_0901)\n", - " print(f\"데이터프레임 df_{mmdd_str}가 생성되었고, 기존데이터프레임에 포함합니다.\")\n", - " df = pd.concat([df, globals()[f'df_{mmdd_str}']])" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [], - "source": [ - "festival_df = df[(df['date'] >= 20230902) & (df['date'] <= 20230910)]\n", - "festival_df_grouped = festival_df.groupby('date')['od_cnts'].sum().reset_index()\n", - "\n", - "# 'date'와 'origin_hdong_cd'을 기준으로 'od_cnts'를 집계\n", - "festival_df_grouped_2 = festival_df.groupby(['origin_hdong_cd'])['od_cnts'].sum().reset_index()\n", - "\n", - "# df_1의 'origin_hdong_cd'와 df_2의 '행정동코드'를 기준으로 병합\n", - "festival_df_grouped_2 = pd.merge(festival_df_grouped_2, address[['행정동코드', '시도명', '시군구명']], \n", - " left_on='origin_hdong_cd', right_on='행정동코드', how='left')\n", - "\n", - "# 병합 후 불필요한 '행정동코드' 컬럼 제거 (필요에 따라)\n", - "festival_df_grouped_2 = festival_df_grouped_2.drop(columns=['행정동코드'])\n", - "\n", - "festival_df_grouped_2.drop_duplicates(inplace=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "metadata": {}, - "outputs": [], - "source": [ - "# '시도명'별로 'od_cnts' 합계를 구한 후 'od_cnts' 기준으로 정렬\n", - "festival_df_grouped_sido = festival_df_grouped_2.groupby('시도명')['od_cnts'].sum().reset_index()\n", - "\n", - "# 'od_cnts' 기준으로 내림차순 정렬\n", - "festival_df_grouped_sido = festival_df_grouped_sido.sort_values(by='od_cnts', ascending=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "metadata": {}, - "outputs": [], - "source": [ - "# '시도명'별로 'od_cnts' 합계를 구한 후 'od_cnts' 기준으로 정렬\n", - "junbok_filtered = festival_df_grouped_2[festival_df_grouped_2['시도명'] == '전라북도']\n", - "festival_df_grouped_junbok = junbok_filtered.groupby('시군구명')['od_cnts'].sum().reset_index()\n", - "\n", - "# 'od_cnts' 기준으로 내림차순 정렬\n", - "festival_df_grouped_junbok = festival_df_grouped_junbok.sort_values(by='od_cnts', ascending=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
내/외부인od_cnts
0외부인35542
1내부인8863
\n", - "
" - ], - "text/plain": [ - " 내/외부인 od_cnts\n", - "0 외부인 35542\n", - "1 내부인 8863" - ] - }, - "execution_count": 77, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 내부인(첫 번째 행)과 외부인(나머지 행)의 데이터프레임 생성\n", - "internal_df = festival_df_grouped_junbok.iloc[[0]].copy() # .copy() 사용하여 SettingWithCopyWarning 방지\n", - "internal_df['시군구명'] = '외부인'\n", - "\n", - "external_sum = festival_df_grouped_junbok.iloc[1:]['od_cnts'].sum()\n", - "external_df = pd.DataFrame([['내부인', external_sum]], columns=['시군구명', 'od_cnts'])\n", - "\n", - "# 최종 데이터프레임 합치기\n", - "in_out_df = pd.concat([internal_df, external_df]).reset_index(drop=True)\n", - "in_out_df.columns = ['내/외부인', 'od_cnts']\n", - "\n", - "in_out_df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 외부인 세부분포 파악" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "metadata": {}, - "outputs": [], - "source": [ - "#festival_df_grouped_sido = festival_df_grouped_sido[1:]\n", - "festival_df_grouped_junbok = festival_df_grouped_junbok.iloc[1:]" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "전라북도\n", - " 시군구명 od_cnts 시도명\n", - "4 무주군 35542 전라북도\n", - "11 전주시 덕진구 2834 전라북도\n", - "12 전주시 완산구 1561 전라북도\n", - "10 장수군 1139 전라북도\n", - "8 익산시 772 전라북도\n", - "14 진안군 704 전라북도\n", - "1 군산시 592 전라북도\n", - "7 완주군 488 전라북도\n", - "3 남원시 259 전라북도\n", - "2 김제시 141 전라북도\n", - "13 정읍시 123 전라북도\n", - "9 임실군 109 전라북도\n", - "6 순창군 66 전라북도\n", - "0 고창군 54 전라북도\n", - "5 부안군 21 전라북도\n", - "\n", - "대전광역시\n", - " 시군구명 od_cnts 시도명\n", - "2 서구 1813 대전광역시\n", - "3 유성구 1379 대전광역시\n", - "1 동구 1207 대전광역시\n", - "4 중구 776 대전광역시\n", - "0 대덕구 562 대전광역시\n", - "\n", - "경상남도\n", - " 시군구명 od_cnts 시도명\n", - "1 거창군 658 경상남도\n", - "9 진주시 560 경상남도\n", - "16 통영시 384 경상남도\n", - "0 거제시 374 경상남도\n", - "19 함양군 323 경상남도\n", - "3 김해시 179 경상남도\n", - "5 사천시 131 경상남도\n", - "6 산청군 121 경상남도\n", - "14 창원시 의창구 115 경상남도\n", - "13 창원시 성산구 81 경상남도\n", - "11 창원시 마산합포구 70 경상남도\n", - "20 합천군 49 경상남도\n", - "18 함안군 43 경상남도\n", - "4 남해군 35 경상남도\n", - "12 창원시 마산회원구 31 경상남도\n", - "7 양산시 27 경상남도\n", - "17 하동군 15 경상남도\n", - "10 창녕군 14 경상남도\n", - "2 고성군 13 경상남도\n", - "15 창원시 진해구 7 경상남도\n", - "8 의령군 5 경상남도\n", - "\n", - "충청남도\n", - " 시군구명 od_cnts 시도명\n", - "2 금산군 882 충청남도\n", - "11 천안시 동남구 310 충청남도\n", - "12 천안시 서북구 265 충청남도\n", - "1 공주시 170 충청남도\n", - "10 예산군 153 충청남도\n", - "3 논산시 148 충청남도\n", - "9 아산시 142 충청남도\n", - "0 계룡시 125 충청남도\n", - "13 홍성군 52 충청남도\n", - "7 서산시 47 충청남도\n", - "6 부여군 41 충청남도\n", - "4 당진시 40 충청남도\n", - "5 보령시 32 충청남도\n", - "8 서천군 6 충청남도\n", - "\n", - "충청북도\n", - " 시군구명 od_cnts 시도명\n", - "3 영동군 1084 충청북도\n", - "10 청주시 흥덕구 230 충청북도\n", - "8 청주시 서원구 217 충청북도\n", - "4 옥천군 211 충청북도\n", - "7 청주시 상당구 194 충청북도\n", - "9 청주시 청원구 185 충청북도\n", - "5 음성군 60 충청북도\n", - "6 진천군 55 충청북도\n", - "2 보은군 53 충청북도\n", - "11 충주시 12 충청북도\n", - "1 단양군 7 충청북도\n", - "0 괴산군 6 충청북도\n", - "\n", - "경기도\n", - " 시군구명 od_cnts 시도명\n", - "34 화성시 202 경기도\n", - "30 이천시 133 경기도\n", - "31 평택시 132 경기도\n", - "26 용인시 기흥구 126 경기도\n", - "7 남양주시 123 경기도\n", - "33 하남시 99 경기도\n", - "10 성남시 분당구 84 경기도\n", - "0 고양시 덕양구 71 경기도\n", - "28 용인시 처인구 66 경기도\n", - "4 구리시 63 경기도\n", - "9 부천시 57 경기도\n", - "20 안성시 56 경기도\n", - "2 광명시 49 경기도\n", - "19 안산시 상록구 49 경기도\n", - "3 광주시 48 경기도\n", - "14 수원시 영통구 48 경기도\n", - "24 여주시 46 경기도\n", - "13 수원시 권선구 45 경기도\n", - "15 수원시 장안구 40 경기도\n", - "17 시흥시 38 경기도\n", - "25 오산시 37 경기도\n", - "5 군포시 33 경기도\n", - "27 용인시 수지구 31 경기도\n", - "12 성남시 중원구 20 경기도\n", - "23 양주시 19 경기도\n", - "29 의정부시 18 경기도\n", - "1 과천시 18 경기도\n", - "18 안산시 단원구 16 경기도\n", - "21 안양시 동안구 12 경기도\n", - "22 안양시 만안구 12 경기도\n", - "16 수원시 팔달구 12 경기도\n", - "6 김포시 12 경기도\n", - "11 성남시 수정구 6 경기도\n", - "32 포천시 6 경기도\n", - "8 동두천시 5 경기도\n", - "\n", - "대구광역시\n", - " 시군구명 od_cnts 시도명\n", - "1 달서구 656 대구광역시\n", - "2 달성군 335 대구광역시\n", - "4 북구 285 대구광역시\n", - "6 수성구 163 대구광역시\n", - "3 동구 126 대구광역시\n", - "5 서구 82 대구광역시\n", - "7 중구 27 대구광역시\n", - "0 남구 12 대구광역시\n", - "\n", - "경상북도\n", - " 시군구명 od_cnts 시도명\n", - "4 김천시 568 경상북도\n", - "3 구미시 393 경상북도\n", - "15 포항시 북구 146 경상북도\n", - "0 경산시 133 경상북도\n", - "13 칠곡군 117 경상북도\n", - "1 경주시 94 경상북도\n", - "7 안동시 46 경상북도\n", - "6 성주군 39 경상북도\n", - "8 영양군 29 경상북도\n", - "14 포항시 남구 28 경상북도\n", - "2 고령군 19 경상북도\n", - "10 예천군 14 경상북도\n", - "11 의성군 12 경상북도\n", - "12 청도군 12 경상북도\n", - "9 영천시 11 경상북도\n", - "5 상주시 5 경상북도\n", - "\n", - "광주광역시\n", - " 시군구명 od_cnts 시도명\n", - "0 광산구 477 광주광역시\n", - "4 서구 245 광주광역시\n", - "3 북구 223 광주광역시\n", - "1 남구 138 광주광역시\n", - "2 동구 9 광주광역시\n", - "\n", - "세종특별자치시\n", - " 시도명 od_cnts\n", - "0 세종특별자치시 999\n", - "\n", - "전라남도\n", - " 시군구명 od_cnts 시도명\n", - "11 여수시 142 전라남도\n", - "3 광양시 111 전라남도\n", - "10 순천시 103 전라남도\n", - "7 목포시 61 전라남도\n", - "5 나주시 54 전라남도\n", - "8 무안군 44 전라남도\n", - "6 담양군 24 전라남도\n", - "9 보성군 19 전라남도\n", - "13 장성군 18 전라남도\n", - "1 고흥군 14 전라남도\n", - "14 화순군 12 전라남도\n", - "0 강진군 11 전라남도\n", - "12 영광군 10 전라남도\n", - "2 곡성군 7 전라남도\n", - "4 구례군 5 전라남도\n", - "\n", - "서울특별시\n", - " 시군구명 od_cnts 시도명\n", - "16 송파구 116 서울특별시\n", - "8 노원구 70 서울특별시\n", - "17 영등포구 42 서울특별시\n", - "4 관악구 37 서울특별시\n", - "6 구로구 33 서울특별시\n", - "10 동작구 32 서울특별시\n", - "7 금천구 29 서울특별시\n", - "13 서초구 27 서울특별시\n", - "12 서대문구 26 서울특별시\n", - "1 강동구 26 서울특별시\n", - "0 강남구 23 서울특별시\n", - "3 강서구 21 서울특별시\n", - "11 마포구 19 서울특별시\n", - "15 성북구 18 서울특별시\n", - "18 용산구 12 서울특별시\n", - "20 종로구 10 서울특별시\n", - "5 광진구 9 서울특별시\n", - "9 도봉구 6 서울특별시\n", - "14 성동구 6 서울특별시\n", - "2 강북구 5 서울특별시\n", - "19 은평구 5 서울특별시\n", - "21 중구 5 서울특별시\n", - "\n", - "부산광역시\n", - " 시군구명 od_cnts 시도명\n", - "9 중구 64 부산광역시\n", - "5 부산진구 62 부산광역시\n", - "2 기장군 58 부산광역시\n", - "10 해운대구 42 부산광역시\n", - "0 강서구 40 부산광역시\n", - "4 동래구 40 부산광역시\n", - "7 사하구 24 부산광역시\n", - "1 금정구 14 부산광역시\n", - "3 남구 12 부산광역시\n", - "8 영도구 9 부산광역시\n", - "6 사상구 6 부산광역시\n", - "\n", - "인천광역시\n", - " 시군구명 od_cnts 시도명\n", - "4 서구 132 인천광역시\n", - "1 남동구 72 인천광역시\n", - "5 연수구 24 인천광역시\n", - "6 중구 15 인천광역시\n", - "0 계양구 12 인천광역시\n", - "3 부평구 11 인천광역시\n", - "2 미추홀구 10 인천광역시\n", - "\n", - "강원특별자치도\n", - " 시군구명 od_cnts 시도명\n", - "0 원주시 92 강원특별자치도\n", - "1 인제군 50 강원특별자치도\n", - "\n", - "울산광역시\n", - " 시군구명 od_cnts 시도명\n", - "2 북구 50 울산광역시\n", - "0 남구 25 울산광역시\n", - "3 중구 21 울산광역시\n", - "1 동구 20 울산광역시\n", - "\n" - ] - } - ], - "source": [ - "map_filtered = pd.DataFrame()\n", - "for i in festival_df_grouped_sido['시도명']:\n", - " # '시도명'별로 'od_cnts' 합계를 구한 후 'od_cnts' 기준으로 정렬\n", - " junbok_filtered = festival_df_grouped_2[festival_df_grouped_2['시도명'] == i]\n", - " if i == '세종특별자치시':\n", - " festival_df_grouped_junbok = junbok_filtered.groupby('시도명')['od_cnts'].sum().reset_index()\n", - " else:\n", - " festival_df_grouped_junbok = junbok_filtered.groupby('시군구명')['od_cnts'].sum().reset_index()\n", - " festival_df_grouped_junbok['시도명'] = i\n", - " # 'od_cnts' 기준으로 내림차순 정렬\n", - " festival_df_grouped_junbok = festival_df_grouped_junbok.sort_values(by='od_cnts', ascending=False)\n", - "\n", - " map_filtered = pd.concat([map_filtered, festival_df_grouped_junbok])\n", - " # 결과 출력\n", - " print(i)\n", - " print(festival_df_grouped_junbok)\n", - " print()\n", - "\n", - "#map_filtered.drop(columns=['시도명'], inplace=True)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
시도명od_cnts
13전라북도44405
6대전광역시5737
2경상남도3235
14충청남도2413
15충청북도2314
1경기도1832
5대구광역시1686
3경상북도1666
4광주광역시1092
9세종특별자치시999
12전라남도635
8서울특별시577
7부산광역시371
11인천광역시276
0강원특별자치도142
10울산광역시116
\n", - "
" - ], - "text/plain": [ - " 시도명 od_cnts\n", - "13 전라북도 44405\n", - "6 대전광역시 5737\n", - "2 경상남도 3235\n", - "14 충청남도 2413\n", - "15 충청북도 2314\n", - "1 경기도 1832\n", - "5 대구광역시 1686\n", - "3 경상북도 1666\n", - "4 광주광역시 1092\n", - "9 세종특별자치시 999\n", - "12 전라남도 635\n", - "8 서울특별시 577\n", - "7 부산광역시 371\n", - "11 인천광역시 276\n", - "0 강원특별자치도 142\n", - "10 울산광역시 116" - ] - }, - "execution_count": 80, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "od_cnts_sum_by_sido = map_filtered.groupby('시도명')['od_cnts'].sum().reset_index().sort_values(by='od_cnts', ascending=False)\n", - "\n", - "od_cnts_sum_by_sido" - ] - }, - { - "cell_type": "code", - "execution_count": 91, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
시도명od_cnts
2전라북도44405
3충남/대전 지역9149
0전라북도 무주군 (개최지)8863
1기타8393
0경상남도3235
4충청북도2314
\n", - "
" - ], - "text/plain": [ - " 시도명 od_cnts\n", - "2 전라북도 44405\n", - "3 충남/대전 지역 9149\n", - "0 전라북도 무주군 (개최지) 8863\n", - "1 기타 8393\n", - "0 경상남도 3235\n", - "4 충청북도 2314" - ] - }, - "execution_count": 91, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# 대전광역시, 충청남도, 세종특별자치시를 '충남/대전 지역'으로 묶기\n", - "od_cnts_sum_by_sido['시도명'] = od_cnts_sum_by_sido['시도명'].replace({\n", - " '대전광역시': '충남/대전 지역', \n", - " '충청남도': '충남/대전 지역', \n", - " '세종특별자치시': '충남/대전 지역'\n", - "})\n", - "\n", - "# 시도명별 od_cnts 합계 계산\n", - "od_cnts_sum_grouped = od_cnts_sum_by_sido.groupby('시도명')['od_cnts'].sum().reset_index()\n", - "\n", - "# od_cnts 값이 2000 미만인 항목을 '기타'로 묶기\n", - "od_cnts_sum_grouped['시도명'] = od_cnts_sum_grouped['시도명'].where(od_cnts_sum_grouped['od_cnts'] >= 2000, '기타')\n", - "\n", - "\n", - "# 시도명별 od_cnts 합계 계산\n", - "od_cnts_sum_grouped = od_cnts_sum_grouped.groupby('시도명')['od_cnts'].sum().reset_index()\n", - "new_data = pd.DataFrame({'시도명': ['전라북도 무주군 (개최지)'], 'od_cnts': [8863]})\n", - "od_cnts_sum_grouped = pd.concat([od_cnts_sum_grouped, new_data]).sort_values(by='od_cnts', ascending=False)\n", - "od_cnts_sum_grouped" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 내/외부인 비율 시각화" - ] - }, - { - "cell_type": "code", - "execution_count": 92, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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j5GUlITrr7++TudkZ2fjt99+w/PPP4+xY8finXfegclkwjPPPIOuXbvi22+/xX333VfhvBUrVuDZZ5+t9X369euH1atXl9s2d+5cLF++vNLju3fvjvXr19fptRARuRqOeCIiIiIiIo/37LPPIiYmpsqpcl988QVuuOEGDBw4sNz2Xbt2oUmTJmjdujUWLlwIhUKBcePGVXuvzp07Y/HixXWeYufv74/4+Pg695ciInJlHPFEREREREQeKScnB7NmzcKOHTtw7NgxqNVqbNy4EXfccQeefPJJ+Pn5lR376quvlv38xx9/xPz585GQkACr1QqFQoGWLVti0qRJGDFihIiXQkTktuSiAxAREREREdmb0WjETTfdBJPJhLlz5yItLQ1XrlzBV199hTNnzuCee+6p9LxPPvkEn332GT799FPExsYiLi4Oly9fxscff4wPPvig0n5ML7zwAqKiosq+srOzMXToUERFRSEsLAy9e/e262s7efIk3nnnnVofL0kSpk2bVuf75OTk4LPPPqvzeUREV2PhiYiIiIiIPM6pU6eg0+nw0UcfoW3bttBqtfDz80Pnzp0xZ84c7Ny5E7m5uRXOW7t2LSZPnoyePXuW296zZ088//zzWLt2bYVzZs6cifT09Eq//tvTCbA1Fb+6UPXPV2lpKTp16gSFQgG5XF72pVarceutt5adn5mZid27d1f6umNiYpCenl5um8ViKTei6x+bN2/GgAEDKr0OAOTl5WH27NmV7ps2bRp8fHwQHh5e6dcbb7xR5XWJyLtwqh0REREREXmctm3borS0FHPnzsUjjzxStnpdfn4+pk+fjjZt2iA4OLjCecOGDcMXX3yBdu3alRWfJEnC3r17MX36dEyaNKnB2SZPnlxlr6nKvPbaa5DJZLjhhhuQmJgIk8lUZQP1kpISFBcXNzhjbUycOJEr8hFRjTjiiYiIiIiIPE5gYCB2796No0eP4vrrr0erVq3QqlUr9O3bF8XFxdi6dSvk8opvh15//XW8/vrreOONNxATE4NmzZqhZcuW+OCDDzBlyhQ8//zzTn0dubm5WLx4MR599FEcPXoU2dnZ+PXXXys99sqVK8jOzsbOnTvtdv/s7GyMGzcOb775pt2uSUTehSOeiIiIiIjII7Vp0wZz586t83kPPfQQHnroIbtkaN++PT788MN6nWsymfDwww9j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LJxrAMZRPPmyUfc27gQFAL7EFiSd/7QUygUt4M1sVoseurC7EiKcpqMAfm9NyT6VV1eZjuE3bFapb1vWdQK8Be9EX2ZzSBOekSLbmE4CAM13+h+ktJ6mUwBohsSIYD10QXfZGIoBuN3sJYVy2HivuUKPnChFhbEmLuAtKJ58XXiiNOl5yR5iOgkAnFincVLfK02nANAC/XLidPOo9qZjAH5vT0W11u4uNx3D57VNDVVafLDpGAB+huLJH6R2l85+0HQKADi+hPbSGP6uAnzRZadk66K+GaZjAH7vrx+uk9PBqKeTlRzjVIf0cNMxAPwPiid/0WWC1H+66RQAcHThydJFL0tOTgYBX3XH2R01KD/edAzAr9XWN2jRuu2mY/ikyBC7eudFspg44IUonvzJiN9LuaeZTgEAhwsKly56SYpON50EQCvYbVY9clEP5SdSIAPu9OpXW2W3m07hW5wOq/q1i5bd5r7L2127dqmsrMxtrw/4M4onf2K1SeP/LsVkm04CAI2s9sYd7FK6mk4CwAUigx36+9Teig8PMh0F8GvPLlsvxu00j91qUf920QoLtrn1OHfddZceffTRVr1GfX29Zs6c6aJEjU455RS9//77LXrOwoULNXToUJccf+rUqZo3b16Ln7d27VoVFha6JAO8H8WTvwmJkS54QQqOMp0EAKSz/iLlDzedAoALpceG6olf9FaIw70XeUAg+2LTHlWr1nQMr2exSH3aRikm3PU72F188cVKTk5u+nr00Ud15513HnbfTTfddNhzioqKlJWVdczXrK+v15/+9KdmZ7BYLIcdLzk5WZI0a9YszZo166jP+cc//qHo6GhFR0crJCREwcHBTbdfeumlZh9bkmbMmNF03ODgYMXExDTd/uSTT4773PXr12vcuHFKS0tTWlqaRowYoS+++OKwnM8++2yL8sB3UTz5o8T20kWvNE5vAQBTBv1W6jnVdAoAbtA1PVoPX9hdNitjMgB3eej9Qt5jJ9AjJ1JJ0U63vPazzz6rHTt2aMeOHVq4cKEiIyOVlJSkFStWNN3fkhKpOerq6pSTk6N169Y13ffTsX76OpELL7xQZWVlKisr08yZMzV9+vSm2xMmTGhRnjlz5jQdd/jw4XryySebbg8YMOCYzztw4IBOP/10jRo1Sps3b9amTZs0fvx49evXT3FxcUpOTtZ9993X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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "\n", - "\n", - "plt.rcParams['font.family'] ='Malgun Gothic'\n", - "plt.rcParams['axes.unicode_minus'] =False\n", - "\n", - "# 서브플롯 생성\n", - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))\n", - "\n", - "# 왼쪽 그래프: in_out_df (30도 회전)\n", - "ax1.pie(in_out_df['od_cnts'], \n", - " labels=in_out_df['내/외부인'], \n", - " autopct='%1.1f%%', \n", - " startangle=210, \n", - " colors=['#ff7f0e', '#1f77b4']) # 내부인: 주황색, 외부인: 파란색\n", - "ax1.set_title('내/외부인 비율')\n", - "\n", - "# 오른쪽 그래프: od_cnts_sum_grouped\n", - "ax2.pie(od_cnts_sum_grouped['od_cnts'], labels=od_cnts_sum_grouped['시도명'], autopct='%1.1f%%', startangle=90, colors=plt.cm.tab20.colors)\n", - "ax2.set_title('시도별 세부 비율')\n", - "\n", - "\n", - "# 그래프 표시\n", - "plt.tight_layout()\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "map_filtered = pd.DataFrame()\n", - "for i in festival_df_grouped_sido['시도명']:\n", - " # '시도명'별로 'od_cnts' 합계를 구한 후 'od_cnts' 기준으로 정렬\n", - " junbok_filtered = festival_df_grouped_2[festival_df_grouped_2['시도명'] == i]\n", - " if i == '세종특별자치시':\n", - " festival_df_grouped_junbok = junbok_filtered.groupby('시도명')['od_cnts'].sum().reset_index()\n", - " else:\n", - " festival_df_grouped_junbok = junbok_filtered.groupby('시군구명')['od_cnts'].sum().reset_index()\n", - " festival_df_grouped_junbok['시도명'] = i\n", - " # 'od_cnts' 기준으로 내림차순 정렬\n", - " festival_df_grouped_junbok = festival_df_grouped_junbok.sort_values(by='od_cnts', ascending=False)\n", - "\n", - " map_filtered = pd.concat([map_filtered, festival_df_grouped_junbok[festival_df_grouped_junbok['od_cnts'] >= 500]])\n", - " # 결과 출력\n", - " print(i)\n", - " print(festival_df_grouped_junbok[festival_df_grouped_junbok['od_cnts'] >= 500])\n", - " print()\n", - "\n", - "#map_filtered.drop(columns=['시도명'], inplace=True)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -}