파이썬 상가(상권)데이터를 이용해서 내 주변 동네 약국 위치 분석 feat.코로나19 마스크 약국
안녕하세요.
연일 코로나19 가 기승입니다.
마스크를 사려고 하는데 약국이 어디 있는지, 찾아볼 수 있는 파이썬 데이터 분석을 해봤습니다.
사실.... 카카오맵으로 검색하면 잘 뜨죠~~ ㅎㅎ 그리고 마스크를 사실 때.. 약국이 어디 있는지 몰라서 못 사는 경우는 별로 없을 겁니다...ㅠㅠ
약국에 마스크가 없어서 문제죠...ㅠㅠ
아무튼, 그래도 뭔가 이 시국에 아주 조~~~~ 금이라도 의미 있는 데이터 분석을 하고 싶었고,
누군가 댓글로 지도 위에 시각화하는 것을 추천해주셔서 저도 첨으로 도전을 해봤습니다.
시키면 시키는 데로 해봅니다. 고수님들 많은 조언과 도움 주시기 바랍니다!!
아무튼, 이번 데이터 분석 실습의 개요는 서울시안에 구별로 약국이 얼마나 있는가? 그래프도 그려보고~ 서울시 지도 위에 시각화하는 것, 그리고 내 주변에 어떤 약국이 정확하게 어디 있는가? 지도 위에 나타내 보는 실습입니다.
바로 시작하겠습니다.
우선 필요한 python 라이브러리를 import 합니다.
import folium
import pymysql
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set()
그리고, 또 그래프 그릴 때 한글 깨지면 안되닌깐...
plt.rc('font', family='NanumGothic')
print(plt.rcParams['font.family'])
다음은, 데이터 가지고 올 DB를 연결합니다.
이젠 몇 번 해봤다고 손에 익습니다..ㅎㅎㅎ 뿌듯하네요.
# db connect and select
conn = pymysql.connect(host='localhost',
user = 'dba_xxxxx', password='xxxxx', db = 'store_svc_m',charset = 'utf8')
curs = conn.cursor(pymysql.cursors.DictCursor)
이렇게 디비를 연결하고요, 상가 DB에서 약국 데이터를 각 구별로 count 하여 검색해 옵니다.
상가 DB가 없으신분은 아래 링크 타고 가셔서 상가DB 구축하시면 됩니다. 금방 해요~
sql = "select sgg_nm, count(*) as cnt from store_svc_m.store_info_m where bsn_sector_small_cd = 'S02A01' and sd_nm = '서울특별시' group by sgg_nm"
curs.execute(sql)
gu_info = curs.fetchall()
gu_parmc_cnt = pd.DataFrame(data=gu_info)
이렇게 상가 데이터에서 약국만 검색해서 카운트 해왔습니다.
그리고 다음은 지도 위에 시각화를 위해서 각 서울시 각 구 의 중심 위치의 위경도 데이터를 가지고 옵니다. 전 미리 데이터를 다운로드하여서 DB에 넣어 뒀습니다.
위 데이터의 테이블 create문과 insert문을 공유합니다.
create table seoul_gu_xy
(
sgg_nm varchar(30) null,
inf_longtitude varchar(500) null,
inf_latitude varchar(500) null
);
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('도봉구', '37.6658609', '127.0317674');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('은평구', '37.6176125', '126.9227004');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('동대문구', '37.5838012', '127.0507003');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('동작구', '37.4965037', '126.9443073');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('금천구', '37.4600969', '126.9001546');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('구로구', '37.4954856', '126.858121');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('종로구', '37.5990998', '126.9861493');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('강북구', '37.6469954', '127.0147158');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('중랑구', '37.5953795', '127.0939669');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('강남구', '37.4959854', '127.0664091');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('강서구', '37.5657617', '126.8226561');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('중구', '37.5579452', '126.9941904');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('강동구', '37.5492077', '127.1464824');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('광진구', '37.5481445', '127.0857528');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('마포구', '37.5622906', '126.9087803');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('서초구', '37.4769528', '127.0378103');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('성북구', '37.606991', '127.0232185');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('노원구', '37.655264', '127.0771201');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('송파구', '37.5048534', '127.1144822');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('서대문구', '37.5820369', '126.9356665');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('양천구', '37.5270616', '126.8561534');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('영등포구', '37.520641', '126.9139242');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('관악구', '37.4653993', '126.9438071');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('성동구', '37.5506753', '127.0409622');
INSERT INTO address_svc_m.seoul_gu_xy (sgg_nm, inf_longtitude, inf_latitude) VALUES ('용산구', '37.5311008', '126.9810742');
이 데이터를 이용하시면 됩니다. 데이터를 가지고 옵니다.
sql = "select * from address_svc_m.seoul_gu_xy"
curs.execute(sql)
gu_info = curs.fetchall()
gu_info = pd.DataFrame(data=gu_info)
잘 가지고 온 걸 확인할 수 있습니다.
그러면 앞에서 가지고 온 각 구별 약국의 수와, 방금 가지고온 서울시 각 구별 중심 위치의 위경도 값을 구이름을 기준으로 합쳐 줍니다.
gu_parmc_info=pd.merge(gu_parmc_cnt,gu_info,on='sgg_nm', right_index=True)
데이터가 잘 합쳐졌는지 확인합니다.
예쁘게 잘 합쳐졌습니다.
이번엔 각 구별로 약국수를 막대그래프로 나타내 보겠습니다.
plt.figure(figsize=(12,8)) #size 조절
plt.xticks(rotation = 50) #x축 조절
sns.barplot(
data = gu_parmc_info.sort_values(by='cnt', ascending=False),
x = "sgg_nm",
y = "cnt"
)
plt.show()
그리고, 이젠 이 데이터로 서울시 지도 위에 각 구별로 약국이 몇 개나 있는지 한번 그려 봅니다.
이걸 하기 위해서 데이터 타입을 좀 변경해줘야 합니다. 위도, 경도, cnt를 float 타입으로 변경합니다.
gu_parmc_info['inf_longtitude'] = gu_parmc_info.inf_longtitude.astype(float)
gu_parmc_info['inf_latitude'] = gu_parmc_info.inf_latitude.astype(float)
gu_parmc_info['cnt'] = gu_parmc_info.cnt.astype(float)
그리고 아래 코드와 같이 folium의 circlemarker 라이브러리를 이용해서 지도 위에 그림을 그려 줍니다.
map_center = folium.Map(location=[37.566659527,126.978346859], zoom_start=11)
for item in gu_parmc_info.index:
latitude = gu_parmc_info.loc[item,'inf_latitude']
longtitude = gu_parmc_info.loc[item,'inf_longtitude']
folium.CircleMarker([longtitude,latitude],
radius = gu_parmc_info.loc[item,'cnt']/10,
popup = gu_parmc_info.loc[item,'sgg_nm'],
color = 'red',
fill = True).add_to(map_center)
map_center.save('pharmacy.html')
map_center
이렇게 하면 위 그림과 같이 지도 위에 그림이 그려지고 각 구별로 원이 생깁니다. 물론 원이 클수록 해당 구에 약국이 많이 있다는 의미가 됩니다. 그냥 봐도 강남 쪽에 약국이 많이 있습니다.
그런데 지금 popup창에 있는 한글이 깨지는데, 제가 지금 jupyter notebook을 쓰고 있어서 그렇다고 하더라고요. 코드를 보시면 해당 결과를 pharmacy.html로 저장하는데, 저장된 파일을 열면 아래 그림처럼 한글이 깨지지 않습니다.
그럼 이번에는 내 주변에 어떤 약국이 어디에 정확하게 있는지 확인해보겠습니다.
일단 서울시내 모든 약국의 데이터를 가지고 와서 예전에 제가 만들었던 주소 DB와 조인하여 해당 주소의 위경도 값을 가지고 온 뒤, 그 값을 통해서 지도 위에 약국별로 마크를 하고, 주소까지 popup으로 띄워 보겠습니다.
#주소 DB 구축 방법
2020/01/17 - [DB엔지니어가 공부하는 python] - [python_주소DB가지고놀기]공공데이터포털 에서 주소DB 다운 받아 DB에 insert 하기 #1
2020/01/20 - [DB엔지니어가 공부하는 python] - [python_주소DB가지고놀기] 파이썬 으로 주소DB에 위도 경도 값 api로 받아 update 하기 #2
위 포스팅 대로 하시면 주소 DB를 구축하고, 각 주소별 위경도 값을 update 할 수 있습니다.
서울시 약국 데이터를 주소 DB와 조인해서 가지고 옵니다.
sql = "select s.store_nm, concat('<',s.store_nm,'> ',j.inf_full_road_addr) as title, j.inf_latitude, j.inf_longtitude from store_svc_m.store_info_m s, address_svc_m.juso_info_m j where s.road_adrs = j.inf_full_road_addr and s.bsn_sector_small_cd = 'S02A01' and s.sd_nm = '서울특별시' and j.inf_latitude is not null"
curs.execute(sql)
tot_pamc_list_info = curs.fetchall()
tot_pamc_list = pd.DataFrame(data=tot_pamc_list_info)
데이터가 잘 왔습니다.
그럼, 다음은 위도, 경도 값의 형 변환을 합니다.
tot_pamc_list['inf_latitude'] = tot_pamc_list.inf_latitude.astype(float)
tot_pamc_list['inf_longtitude'] = tot_pamc_list.inf_longtitude.astype(float)
그리고 나면 이젠 지도를 그려주면 됩니다.
map_list = folium.Map(location=[37.566659527,126.978346859], zoom_start=11)
for item in tot_pamc_list.index:
latitude = float(tot_pamc_list.loc[item,'inf_latitude'])
longtitude = float(tot_pamc_list.loc[item,'inf_longtitude'])
folium.Marker(
location=[latitude,longtitude],
popup = tot_pamc_list.loc[item,'title'],
icon=folium.Icon(color='blue',icon='star')
).add_to(map_list)
map_list.save('pharmacy_list.html')
map_list
아무래도 약국수가 많다 보니 조금 오래 걸립니다.
지도를 확대하지 않고 보니 엄청난 군집을 이루고 있는 것처럼 보입니다.
가까이 놓고 보면...
이렇게 보입니다. 이것도 마찬가지로 한글이 깨지네요...
하지만 소스코드에서 보시면 아시겠지만, phamacy_list.html로 저장을 했습니다. 해당 파일을 열면 깨지지 않은 화면을 보실 수 있습니다.
약국 이름과 주소가 잘 나오고 있는 걸 확인할 수 있습니다.
오늘의 데이터 분석은 여기까지입니다.
여러분들 코로나 19 때문에 여러 가지로 많이 힘드시죠? 우리 서로 조금만 더 배려하고, 개인위생 철저히 하면 이 난관을 잘 헤쳐 나갈 수 있으리라 생각됩니다.
여기까지 읽어주셔서 감사합니다.
by.sTricky
'DB엔지니어가 공부하는 python' 카테고리의 다른 글
파이썬 이분탐색 알고리즘 Binary search #10 (4) | 2020.03.09 |
---|---|
파이썬 병합정렬 알고리즘 merge sort #9 (8) | 2020.03.06 |
파이썬 에러 pip upgrade fail, 'NoneType' object has no attribute 'bytes' (0) | 2020.03.03 |
파이썬 상권분석 실습# 상가(상권)데이터를 이용한 데이터 분석 #1 (16) | 2020.02.28 |
파이썬 삽입정렬 알고리즘 Insertionsort #8 (2) | 2020.02.27 |