본문 바로가기
문송충의 코딩하기/파이썬 데이터 분석

카카오 API를 활용한 동네 브랜드별 편의점 점포 수 구하기 with Python

by 동장군님 2020. 7. 14.
728x90
반응형

저번 Folium을 통해 분석한 것과 비슷한 내용의 분석이다. 이번에는 제목 그대로 내가 살고 있는 주소 반경 30km 내 편의점이 몇개가 있고, 브랜드별로 어떻게 구성이 되어있는지 카카오 지도 API를 통해 살펴보고자 한다. 코드를 보면 굉장히 간단하다. 따로 패키지를 임포트할 것도 없다.

 

그럼 바로 시작하겠다.


1. 내 주소 위도/경도 구하기

우선 내가 살고 있는 집 주소의 위도/경도를 구해야되는데 이것 또한 마찬가지로 카카오 API를 사용할 것이다.

 

아래처럼 addr이라는 변수에다가 살고 있는 집 주소를 추가해서 돌리면 주소에 맞는 위도/경도가 나올 것이다.

import json
import requests
import pandas as pd

addr='인천시 남동구 운연천로11'
url = 'https://dapi.kakao.com/v2/local/search/address.json?query='+addr
headers={'Authorization':'KakaoAK ~'} # 카카오 API 키 

result = json.loads(str(requests.get(url,headers=headers).text))
x=result['documents'][0]['address']['x']
y=result['documents'][0]['address']['y']

나 같은 경우는 도로명을 검색 했기에 도로명과 이전 동 주소의 결과가 나오는 것을 확인할 수 있다.

*아래 결과에서 x가 경도 y가 위도이다

{'address': {'address_name': '인천 남동구 서창동 745',
  'b_code': '2820010500',
  'h_code': '2820065500',
  'main_address_no': '745',
  'mountain_yn': 'N',
  'region_1depth_name': '인천',
  'region_2depth_name': '남동구',
  'region_3depth_h_name': '서창2동',
  'region_3depth_name': '서창동',
  'sub_address_no': '',
  'x': '126.756260031909',
  'y': '37.4309606033878'},
 'address_name': '인천 남동구 운연천로 11',
 'address_type': 'ROAD_ADDR',
 'road_address': {'address_name': '인천 남동구 운연천로 11',
  'building_name': '',
  'main_building_no': '11',
  'region_1depth_name': '인천',
  'region_2depth_name': '남동구',
  'region_3depth_name': '서창동',
  'road_name': '운연천로',
  'sub_building_no': '',
  'underground_yn': 'N',
  'x': '126.756260031909',
  'y': '37.4309606033878',
  'zone_no': '21611'},
 'x': '126.756260031909',
 'y': '37.4309606033878'}

2. 주변 편의점 리스트 추출

내 주소 위도/경도를 받았으면 다음 스텝은 그 위치 주변 반경 30km 내 편의점 리스트를 구하는 것이다.

파라미터 설명: https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-category

 

 

아래 코드에서 문제점은 같은 편의점 이름이 중복돼서 추출된다는 것인데, 이 부분은 어떻게 해결할지 도저히 모르겠다.

그래서 아래 Set 함수로 중복을 제거했는데, 이게 번거롭지는 않지만 귀찮다.

convs={}
for i in range(1,46):
    headers = {
        "Authorization": "KakaoAK ~" #카카오 API key
    }
    params={
        'x': float(x),
        'y':float(y),
        'radius':3000,
        'page':i,
        'size':15,
        'sort':'distance'
    }
    
    keywords='편의점'
    url='https://dapi.kakao.com/v2/local/search/keyword.json?query={}'.format(keywords)
    places = requests.get(url, headers = headers,params=params).json()['documents']
    if places ==[]:
        break;
    else:
        convs[i]=places
        
#편의점 명만 따로 뽑아서 Places name이라는 리스트에 넣기        
places_name=[]
brands=[]
for i in range(1,len(convs)):
    for a in range(len(convs[i])):
        places_name.append(convs[i][a]['place_name'])

# 중복 제거
names=list(set(places_name))

#데이터 프레임
convin=pd.DataFrame()
convin['convin']=names

아래와 같이 편의점을 확인 할 수 있다.


3. 편의점 브랜드별 비중 시각화

마지막으로 각 브랜드 편의점 개수와 비중을 구해 파이 차트를 그리고자 한다.

 

- 브랜드 구분하기

gs25=[x for x in convin['convin'] if 'GS25' in x]
seven=[x for x in convin['convin'] if '세븐일레븐' in x]
cu=[x for x in convin['convin'] if 'CU' in x]
emart=[x for x in convin['convin'] if '이마트' in x]
mini=[x for x in convin['convin'] if '미니스톱' in x]
others =len(convin)-(len(gs25)+len(seven)+len(cu)+len(emart)+len(mini))

 

- 편의점 점포 비중 구하기

store={"GS25":len(gs25),'세븐일레븐':len(seven),'CU':len(cu),'이마트':len(emart),'미니스톱':len(mini),'기타':others}
df = pd.DataFrame(list(store.items()),columns = ['편의점','점포 수'])
df=df.sort_values(by=['점포 수'],axis=0,ascending=False)
df['비중']=df['점포 수'].apply(lambda x: x/len(convin))

 

내 동네는 GS25, CU, 이마트25 순으로 많이 있다. 유플러스 10% 할인을 받을 수 있어서 GS25를 가장 많이 이용한다.안물안궁

- 파이 차트 그리기

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import font_manager, rc
from matplotlib import style
plt.rcParams["figure.figsize"] = (16,12)
font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
style.use('ggplot')
plt.pie(df['비중'], labels=df['편의점'], autopct='%1.1f%%', shadow=True, startangle=90)

 

최종 결과물

인천 남동구 서창 2동 기준 편의점 브랜드별 점포 수 비중

 

 

 

cf. 최종 코드

#내가 살고 있는 집 주소 위도/경도
import json
import requests
import pandas as pd

addr='인천시 남동구 운연천로11'
url = 'https://dapi.kakao.com/v2/local/search/address.json?query='+addr
headers={'Authorization':'KakaoAK ~'}

result = json.loads(str(requests.get(url,headers=headers).text))
x=result['documents'][0]['address']['x']
y=result['documents'][0]['address']['y']


# 집 주소 반경 30km 이내 편의점 리스트 추출
convs={}
for i in range(1,46):
    headers = {
        "Authorization": "KakaoAK ~"
    }
    params={
        'x': float(x),
        'y':float(y),
        'radius':3000,
        'page':i,
        'size':15,
        'sort':'distance'
    }
    
    keywords='편의점'
    url='https://dapi.kakao.com/v2/local/search/keyword.json?query={}'.format(keywords)
    places = requests.get(url, headers = headers,params=params).json()['documents']
    if places ==[]:
        break;
    else:
        convs[i]=places
places_name=[]
brands=[]
for i in range(1,len(convs)):
    for a in range(len(convs[i])):
        places_name.append(convs[i][a]['place_name'])
names=list(set(places_name))
convin=pd.DataFrame()
convin['convin']=names


# 편의점 브랜드별 점포 수 및 비중 구하기
gs25=[x for x in convin['convin'] if 'GS25' in x]
seven=[x for x in convin['convin'] if '세븐일레븐' in x]
cu=[x for x in convin['convin'] if 'CU' in x]
emart=[x for x in convin['convin'] if '이마트' in x]
mini=[x for x in convin['convin'] if '미니스톱' in x]
others =len(convin)-(len(gs25)+len(seven)+len(cu)+len(emart)+len(mini))
store={"GS25":len(gs25),'세븐일레븐':len(seven),'CU':len(cu),'이마트':len(emart),'미니스톱':len(mini),'기타':others}
df = pd.DataFrame(list(store.items()),columns = ['편의점','점포 수'])
df=df.sort_values(by=['점포 수'],axis=0,ascending=False)
df['비중']=df['점포 수'].apply(lambda x: x/len(convin))


# 파이차트 시각화
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import font_manager, rc
from matplotlib import style
plt.rcParams["figure.figsize"] = (16,12)
font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
style.use('ggplot')
plt.pie(df['비중'], labels=df['편의점'], autopct='%1.1f%%', shadow=True, startangle=90)

 

 

코드가 조금 비효율적으로 작성된 것 같긴 한데, 뭐 이 정도면 나쁘지 않다. 다음 번에는 좀 더 효율적으로 작성해보겠다.

 

성의 없는 코딩 끝

728x90
반응형

댓글