Engineering
좌표계 위치데이터의 시각화
2022년 3월 16일
원문에서 보기 ↗안녕하십니까? 뉴딥기술랩 임지홍 입니다. 오늘은 위치데이터를 이용한 분석에 대해 말씀드려 보려 합니다.
위치 데이터는 크게 우리가 일상에서 사용하는 주소, 우편번호 등 특정지역을 지칭하는 분류 데이터와 WGS84 경위도로 대표되는 좌표계 데이터 2가지로 나누어 볼 수 있을 것 같은데요.
아무래도 바로 수집가능한 주소체계를 이용한 데이터 수집이 간편하고 자주 사용되는 방법이겠지만
- 도로명 주소체계로 관리할 것이냐?
- 시군구 단위로 나뉘지 않는 곳의 카테고리는 어떻게 할 것이냐?
- 우편번호가 겹치는 지역의 집계는 어찌 처리하지?
- 영문명은 별도로 저장해 놔야하나?
등 저장, 데이터 통합 그리고 분석 시점 등에 신경써야 하는 포인트가 늘어나게 됩니다.
또 처음부터 동까지만 관리하면 동보다 좀 더 세밀하게 분석을 하지 못하는 제약도 생기기 때문에 좀 더 다양한 활용을 위해서 경/위도 좌표계로 위치데이터를 기반으로 관리하는 곳이 점점 늘어나고 있습니다. 그리고 이런 정보를 활용하는 시점에 필요한 형태로 변환하여 사용하는 것이 추세이기도 하구요.
이런 것들을 고려하여 여러분들께서 수집하는 위치 데이터는 아래와 같다고 가정해 보겠습니다. 서비스 사용 고객이 서울의 오프라인 매장에서 주문한 이력데이터을 가상으로 만들어 본 것으로 컬럼형식은 "고객익명데이터, 구매금액, 경도, 위도, 주문일자" 입니다. 
데이터에 경위도 좌표가 있으니 python의 folium 라이브러리를 이용하여 지도 위에 뿌려 보겠습니다.
center = [37.541, 126.986] //대략적인 서울 중심의 경,위도
m = folium.Map(location=center, zoom_start=12) //12배율의 크기로 지도 그림
//결제 정보를 하나씩 읽으면서 지도위에 원으로 표현
for idx, geo_df_row in geo_df.iterrows() :
folium.Circle(radius=5, location=[geo_df_row[“geolocation_latitude”], \
geo_df_row[“geolocation_longitude”]], color='#FF0000').add_to(m)
m
거래 건마다 점을 찍다보니 정보가 겹쳐서 눈에 잘 들어오지 않네요.
우리가 많은 데이터를 분석할 때 합산을 하거나 평균을 내어 집계자료를 생성하고 이를 분석하고 필요 시 특정 부분에 대해서 좀 더 세밀하게 살펴보는 것처럼 (SQL 등으로 group by 가 좋은 예일 것 같네요.) 지역단위로 집계를 한 후 이것을 지도 위에 그리면 좀 더 보기 쉬워질 것 같습니다.
이를 위해 우선 필요한 데이터를 수급하여 가공해 보겠습니다. 위치데이터를 지역데이터로 변경 하기위한 정보는 SHP 데이터와 GeoJson 등으로 만들어져 있는 공공데이터를 통해 쉽게 획득 가능합니다. 저는 이 중 python에서 다루기 편한 GeoJson을 입수하여 서울 데이터만 추스려 사용했습니다. (https://github.com/vuski/admdongkor/blob/master/ver2021xxxx_for%20update/HangJeongDong_ver20210701.geojson))
살펴보니 시/구/동단위의 여러 메타정보와 지역 경계점의 경,위도 좌표 묶음으로 구성되어 있음을 알 수 있었습니다. GeoJson 포멧은 아래처럼 쉽게 folium에서 불러올 수 있습니다.
kr_geo = ‘data/seoul.geojson’
center = [37.541, 126.986] //대략 서울 중심의 경,위도
m = folium.Map(location=center, zoom_start=12)
folium.GeoJson(kr_geo).add_to(m)
m
동별로 잘 나뉘어져 있음을 확인할 수 있었습니다.
이제 우리 데이터를 동별로 집계하기 위해 기존 데이터에 동 정보를 추가해보겠습니다. 기본 아이디어는 각 동의 좌표 점들을 연결하여 폴리곤을 만들고 결제정보의 위,경도 좌표가 어느 폴리곤에 속하는지 검색하여 해당되는 동을 추가하는 방법으로 진행해 보았습니다.
좌표를 주소로 변환하는 과정(리버스 지오코딩)을 위해 우선 GeoJson에서 동이름(features.properties.adm_nm) 및 좌표를 추출하여 아래와 같이 사전을 만들어 보겠습니다.
geo_dict = {}
for geo_row in data[‘features’] :
geo_name = geo_row[‘properties’][‘adm_nm’]
geo_value = geo_row[‘geometry’][‘coordinates’][0];
//여러 폴리곤의 조합으로 만들어지는 복합지역이라면 가장 바닥의 정보를 가져옴
if geo_row[‘geometry’][‘type’] == ‘MultiPolygon’ :
geo_value = geo_row[‘geometry’][‘coordinates’][0][0];
geo_dict[geo_name] = geo_value
이렇게 만들어진 사전과 Shapely 라이브러리를 이용하여 주문데이터의 좌표가 어느 동에 속하는 지 검사하는 함수를 만들어 봤습니다.
from shapely.geometry import Polygon, Point
def get_GeoName(longi, lati) :
for geo_name, geo_poly in geo_dict.items() :
poly_obj = Polygon(geo_poly)
point_obj = Point(longi, lati)
if point_obj.within(poly_obj) :
return geo_name
return None
만들어진 함수를 활용하여 기존 데이터에 주소를 추가 해봅니다.
마지막에 동이름이 추가된 것을 보실 수 있습니다. 혹시나 다른 지역에 같은 동이름이 있을지 몰라 시, 구도 붙여놨습니다.
이제 우리데이터를 지역별로 쉽게 집계 할 수 있게 되었습니다. 
여기서 잠깐, 처음부터 주소를 넣었으면 이런 과정을 거치지 않을 것 아니냐라는 생각이 들기도 할텐데요. “최근 한 달간 코엑스에서 결제한 사람을 뽑아줘!” “위례신도시(서울 송파구, 경기 성남시, 경기 하남시로 구성), 광교신도시(경기 수원시, 용인시로 구성)에서 월 10만원 이상 결제하는 고객은 몇 명이니?” 등의 좀 더 현실적인 문제에도 유연하게 대처할 수 있게되는 강점이 생기게 됩니다.
다시 본론으로 돌아와, 이제 원하는 형태의 데이터가 준비되었으니 히트맵으로 지도위에 동별 판매금액을 그려보겠습니다. 나의 데이터 중 사용하는 컬럼을 columns에 명시해주고 컬럼의 첫번 째 정보(동이름)와 맵핑되는 GeoJson의 항목명(여기에선 feature.properties.adm_nm)이 무엇인지 설정하는 부분만 신경 써 주시면 됩니다. 
처음에 지도 위에 찍었던 점과 비교해보면 어떤가요? 저는 우리 서비스가 어디에서 많이 사용되는지 한 눈에 볼 수 있어 마음에 드네요.
지금 껏 설명드린 과정을 요약 정리해보면
- 원하는 지역의 GeoJson을 확보하고
- GeoJson을 이용하여 구매데이터의 경위도 좌표를 동이름으로 리버스 지오코딩 한 후
- 동 단위로 합산하여
- 지도에 표시
정도가 될 것 같습니다.
여러분께서 보유하고 계신 데이터가 주소단위라면 GeoJson의 주소체계와 보유 데이터의 주소체계를 통일 시키는 작업을 진행하신 후 3의 과정부터 수행하면 동일한 결과를 만들어 볼 수 있을 것 입니다.
과정이 그리 어렵지 않고 또 많은 데이터를 어떻게 깔끔하게 표현할 수 있을까라는 고민에 대한 해답도 될 수도 있으며 상권분석이나 물류분석 등의 실무를 담당하시는 여러 곳에서 활용할 수 있는 좋은 예시 이기도 하여 간략하게나마 정리해 봤습니다.
긴글 읽어주셔서 감사드리구요, 앞으로도 종종 간단하며 흥미롭고 활용하기 좋은 아이템을 들고 찾아뵙겠습니다. 어려운 시기에 건강 챙기시길 기원합니다.