이번 글에서는, 데이터에는 없는 지역구에 대한 데이터를 얻는 방법을 다룬다.
기본적으로, 대여소명을 활용하고, 다음의 과정을 거친다.
- 공개된 대여소 데이터로 지역구 데이터를 얻는다.
- 대여소명과 카카오맵 rest api를 통해 얻는 방법에 대해 다룬다.
- 이 과정 중에, 카카오맵 rest api 사용법도 간략히 살펴보게 된다.
- 결과적으로
{대여소 이름: 지역구}
인 dictionary를 얻는다.
예를 들어 아래와 같다.
{'MCM 본사 직영점 앞': '강남구',
'교보타워 버스정류장(신논현역 3번출구 후면)': '강남구',
'논현역 7번출구': '강남구',
'신영 ROYAL PALACE 앞': '강남구',
'압구정 한양 3차 아파트': '강남구',
'압구정역 2번 출구 옆': '강남구',
'압구정파출소 앞': '강남구',
'청담동 맥도날드 옆(위치)': '강남구',
'청담역(우리들병원 앞)': '강남구',
'학동로 래미안 아파트 앞': '강남구',
'현대고등학교 건너편': '강남구'}
1. 대여소 정보 데이터를 통해, 대여소의 지역구 데이터 얻기.
첫 번째 방법은, 공공 데이터 포탈에 있는 서울특별시 공공자전거 대여소 정보.csv
를 먼저 활용하는 거다.
여기서 다운받을 수 있다.
나는 19.5.31 에 올라온 데이터를 받았다.
import pandas as pd
rentals = pd.read_csv("data/서울특별시 공공자전거 대여소 정보.csv")
rentals.head()
구명 | 대여소ID | 대여소번호 | 대여소명 | 대여소 주소 | 거치대수 | 위도 | 경도 | |
---|---|---|---|---|---|---|---|---|
0 | 강남구 | ST-777 | 2301 | 현대고등학교 건너편 | 서울특별시 강남구 압구정로 134현대고등학교 건너편 | 10 | 37.524071 | 127.021790 |
1 | 강남구 | ST-787 | 2302 | 교보타워 버스정류장(신논현역 3번출구 후면) | 서울특별시 강남구 봉은사로 지하 102교보타워 버스정류장 | 10 | 37.505581 | 127.024277 |
2 | 강남구 | ST-788 | 2303 | 논현역 7번출구 | 서울특별시 강남구 학동로 지하 102논현역 7번출구 | 15 | 37.511517 | 127.021477 |
3 | 강남구 | ST-789 | 2304 | 신영 ROYAL PALACE 앞 | 서울특별시 강남구 언주로 626신영 ROYAL PALACE앞 | 10 | 37.512527 | 127.035835 |
4 | 강남구 | ST-790 | 2305 | MCM 본사 직영점 앞 | 서울특별시 강남구 언주로 734MCM 본사 직영점 앞 | 10 | 37.520641 | 127.034508 |
여기에는 대여소별 지역구에 대한 정보가 있다.
즉, 현대고등학교 건너편
이 강남구
에 속해있다는 정보를 알 수 있다.
len(set(rentals['대여소명']))
1460
1460개에 대한 정보가 있다고 한다.
19년도 5월 데이터이니, 해당 날짜 기준 대여소만 있고, 아무래도 이전에 폐쇄된 대여소 정보는 없을 것이다.
그리고, 이제 내가 사용할 데이터프레임을 가져와보자.
df = pd.read_pickle('data/dataframes/2017-2018.pkl')
df.head()
대여일자 | 대여시간 | 대여소번호 | 대여소 | 대여구분코드 | 성별 | 연령대코드 | 이용건수 | 운동량 | 탄소량 | 이동거리(M) | 이동시간(분) | 년 | 월 | 일 | 요일 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2017-01-01 | 0 | 230 | 영등포구청역 1번출구 | 정기권 | F | ~10대 | 1 | 31.270000 | 0.39 | 1680 | 155 | 2017 | 1 | 1 | 6 |
1 | 2017-01-01 | 0 | 315 | 신한은행 안국역지점 옆 | 정기권 | F | 20대 | 1 | 47.320000 | 0.55 | 2390 | 15 | 2017 | 1 | 1 | 6 |
2 | 2017-01-01 | 0 | 328 | 탑골공원 앞 | 정기권 | F | 20대 | 1 | 57.919998 | 0.52 | 2250 | 13 | 2017 | 1 | 1 | 6 |
3 | 2017-01-01 | 0 | 175 | 홍연2교옆 | 정기권 | F | 20대 | 1 | 133.289993 | 1.53 | 6600 | 43 | 2017 | 1 | 1 | 6 |
4 | 2017-01-01 | 0 | 817 | 삼각지역 4번출구 앞 | 정기권 | F | 20대 | 1 | 33.880001 | 0.32 | 1380 | 8 | 2017 | 1 | 1 | 6 |
내 데이터프레임에는, 지역구에 대한 정보가 없다. 근데 나는 지역구를 여기다가 추가하고싶다. 이게 문제의 시작이다.
이 데이터프레임에는 대여소 종류가 몇 개인지 확인해보면,
len(set(df['대여소']))
1514
1514개란다. 이 데이터프레임은 2017-2018년의 데이터를 담고있는데, 19년도 5월 데이터의 대여소개수가 1460개인 것과 비교하면, 이 데이터프레임에는 최소 54개 정도의 대여소가 없다.
위에서 로드한 대여소 데이터를 데이터프레임에 대여소 이름을 기준으로 매핑하면, 얼마만큼의 데이터가 손실되는지 보자.
before = df.shape[0]
after = pd.merge(df, rentals[['대여소번호', '구명']], on='대여소번호', how='inner').shape[0]
print("매핑하기 이전:", before)
print("매핑하기 이후:", after)
매핑하기 이전: 10183216
매핑하기 이후: 5909173
(before - after) / before * 100
41.971445955776645
약 40% 의 손실이 나니.. 그냥 무시하고 갈 수가 없다.
이에, 대여소 데이터 뿐이 아니라, 별도의 써드파티 api 를 이용하여 지역구 데이터를 얻어오기로 해보자.
2. 카카오맵 rest api 를 이용해서 대여소의 지역구 얻어오기
아이디어는 이렇다.
- 우리는 데이터프레임에서 각 대여소의 지역구를 얻고 싶은데, 현재 지역구가 없다.
- 대여소 데이터에 일부 대여소의 지역구 정보가 있지만, 없는 것도 많다.
- 하지만, 대여소 이름을 보면 어느정도 지역을 추론할 수 있는데, 대여소 이름을 카카오 지도에서 검색하여 주소를 알아낼 수 있다.
- 해당 주소에서 지역구 값만 가져오면 된다.
얼마나 잘될지는 모르겠지만, 일단 해보자.
2.1) Kakao rest api 사용법 알아보기
kakao developers 공식 docu 에 있는 내용을 토대로 먼저, 카카오 rest api 사용해보자.
아래를 보기 전에, 아래와 같은 사항이 준비되어있어야 한다.
- kakao developers 에서 계정 생성 후, 내 어플리케이션 만들기.
- 내 어플리케이션의 REST API key 를 찾을 것.
- 애플리케이션 - 설정 - 일반에 가면 볼 수 있다.
my_rest_api_key = "여기에 자신의 어플리케이션 REST API key를 입력해준다."
import requests
url = "https://dapi.kakao.com/v2/local/search/keyword.json"
headers = {"Authorization": "KakaoAK " + my_rest_api_key}
params = {"query": "영등포구청역 1번출구"}
res = requests.get(url=url, params=params, headers=headers)
import pprint as pp
pp.pprint(res.json())
{'documents': [{'address_name': '서울 영등포구 당산동3가 375',
'category_group_code': '',
'category_group_name': '',
'category_name': '스포츠,레저 > 자전거,싸이클 > 자전거대여소',
'distance': '',
'id': '1911311374',
'phone': '',
'place_name': '영등포구청역 1번출구 대여소',
'place_url': 'http://place.map.kakao.com/1911311374',
'road_address_name': '서울 영등포구 당산로 111-2',
'x': '126.896217493004',
'y': '37.5246335974008'},
{'address_name': '서울 영등포구 당산동3가',
'category_group_code': '',
'category_group_name': '',
'category_name': '교통,수송 > 지하철,전철',
'distance': '',
'id': '10642080',
'phone': '',
'place_name': '영등포구청역 2호선 1번출구',
'place_url': 'http://place.map.kakao.com/10642080',
'road_address_name': '',
'x': '126.896285209314',
'y': '37.5247489851369'}],
'meta': {'is_end': True,
'pageable_count': 2,
'same_name': {'keyword': '영등포구청역 1번출구',
'region': [],
'selected_region': ''},
'total_count': 2}}
보면, res['documents']
이 list
형식으로, 검색결과를 받아오는 것을 알 수 있다.
검색결과 데이터 중 가장 첫 번째의 결과의 adderss_name
에 지역구가 들어있을 것이다.
result = res.json()['documents'][0]
result['address_name']
'서울 영등포구 당산동3가 375'
지역구는 아래와 같이 추출할 수 있다.
result['address_name'].split()[1]
'영등포구'
2.2) {대여소 이름: 지역구} 를 반환하는 함수 만들기.
def get_region_from_rental_number(rental_names, default_data=False, verbose=True, process_unit=300):
total = len(rental_names)
print("total_size :", total)
print("-------------------")
d = {}
# 먼저 대여소 데이터에서 얻을 수 있는 데이터는 얻자.
if default_data:
try:
rentals = pd.read_csv("data/서울특별시 공공자전거 대여소 정보.csv")
for i, row in rentals.iterrows():
d[row['대여소명']] = row['구명']
except e:
print(e)
print("'서울특별시 공공자전거 대여소 정보.csv' 를 'data/' 내에 넣어주세요.")
# 대여소 이름을 카카오맵 rest api query에 보내, 지역구를 받자.
fail, success = 0, 0
fail_list = set()
for i, rental_name in enumerate(rental_names, 1):
# 진행속도 출력
if verbose and i % process_unit == 0:
print("%d / %d (%d%%) processed.." %(i, total, float(i)/total*100))
# 이미 있으면 스킵.
if rental_name in d:
continue
# api 에 쿼리 날리기.
params = {"query": "서울 " + rental_name}
res = requests.get(url=url, params=params, headers=headers)
result = res.json()['documents']
if result:
region = result[0]['address_name'].split()[1]
d[rental_name] = region
success += 1
else:
# 실패했을 경우, 다음으로 한번 더 시도해본다.
if len(rental_name.split()) >= 2:
params = {"query": "서울 " + "".join(rental_name.split()[:-1])}
res = requests.get(url=url, params=params, headers=headers)
result = res.json()['documents']
if result:
region = result[0]['address_name'].split()[1]
d[rental_name] = region
success += 1
continue
# 그래도 실패한 경우.
fail_list.add(rental_name)
fail += 1
print("sucess: %d(%.1f%%)" %(success, float(success)/(fail+success)*100))
print("fail: %d(%.1f%%)" %(fail, float(fail)/(fail+success)*100))
# dictionary, fail_list 반환
return d, fail_list
테스트 해보자.
rental_names = df['대여소'][:10]
get_region_from_rental_number(rental_names)
total_size : 10
-------------------
sucess: 10(100.0%)
fail: 0(0.0%)
({' 영등포구청역 1번출구': '영등포구',
' 신한은행 안국역지점 옆': '종로구',
' 탑골공원 앞': '종로구',
' 홍연2교옆': '서대문구',
' 삼각지역 4번출구 앞': '용산구',
' 연신내역 5번출구150M 아래': '은평구',
' 연신내역 3번출구 인근': '은평구',
' 서울역사박물관 앞': '종로구',
' 낙원상가 옆': '종로구',
' 종로3가역 15번출구 앞': '종로구'},
set())
잘 뽑히는걸 확인할 수 있다.
3. 함수 사용하여 {대여소: 지역구} 만들고, 피클라이즈 하기.
이제 만들어놓은 함수로, 지역구 데이터를 데이터프레임에 추가하자.
rental_names = set(df['대여소'].dropna())
region, fail_list = get_region_from_rental_number(rental_names, default_data=True)
total_size : 1513
-------------------
300 / 1513 (19%) processed..
600 / 1513 (39%) processed..
900 / 1513 (59%) processed..
1200 / 1513 (79%) processed..
1500 / 1513 (99%) processed..
sucess: 1481(99.6%)
fail: 6(0.4%)
fail_list
{' 구로구배드민턴실내체육관 앞',
' 구일우성(아) 육교 밑',
' 센서텍㈜',
'8.삼호@ 2동 ( 간선도로)',
'위트콤공장',
'이동정비'}
일부 실패한 애들은, 카카오맵(https://map.kakao.com/) 에서 직접 찾아서 입력해주자.
region[' 구로구배드민턴실내체육관 앞'] = '구로구'
region[' 구일우성(아) 육교 밑'] = '구로구'
region[' 센서텍㈜'] = '강서구'
region['8.삼호@ 2동 ( 간선도로)'] = '강남구'
region['위트콤공장'] = '서초구'
region
을 살짝 살펴보면, 아래와 같은 포맷이다.
i = 0
region_temp = {}
for k, v in region.items():
region_temp[k] = v
i += 1
if i > 10:
break
pp.pprint(region_temp)
{'MCM 본사 직영점 앞': '강남구',
'교보타워 버스정류장(신논현역 3번출구 후면)': '강남구',
'논현역 7번출구': '강남구',
'신영 ROYAL PALACE 앞': '강남구',
'압구정 한양 3차 아파트': '강남구',
'압구정역 2번 출구 옆': '강남구',
'압구정파출소 앞': '강남구',
'청담동 맥도날드 옆(위치)': '강남구',
'청담역(우리들병원 앞)': '강남구',
'학동로 래미안 아파트 앞': '강남구',
'현대고등학교 건너편': '강남구'}
import pickle
with open('data/rental_region.pkl', 'wb') as f:
pickle.dump(region, f)
이제 pickled 된 rental_region.pkl
을 필요한 데이터프레임에서 활용하면 된다.
이 글에 대한 노트북은 여기서 다운받을 수 있다.
'프로젝트들' 카테고리의 다른 글
데이터로 내 티스토리 블로그 EDA 하기 (4) | 2020.03.03 |
---|---|
스프링 부트를 활용한 간단한 웹 사이트 (3) | 2020.02.24 |
[All about 따릉이 EDA, 6편] 대여소별 따릉이 대여건수 예측 (2) | 2019.07.19 |
[All about 따릉이 EDA, 5편] 마포구, 따릉이는 얼마나 어떻게 이용되고 있을까? (6) | 2019.07.19 |
[All about 따릉이 EDA, 4편] 따릉이, 유저 분석해보기 (0) | 2019.07.18 |