융무의 기술블로그
article thumbnail

앞에서 설명한 geopandas를 이용한 서울시 시각화 (1)에 이어서 시각화를 진행하고자 합니다. 

https://mjs1995.tistory.com/186

 

[python][pydeck] geopandas를 이용한 서울시 시각화 (1)

지도 시각화를 하는데 있어서 folium, plotly, dash 등 다양한 라이브러리가 존재하는데 이번에는 pydeck을 이용한 지도시각화를 해보려고 합니다. pydeck의 장점은 여러 레이러를 쌓을수 있다는 점과 geo

mjs1995.tistory.com

geopandas를 이용하여 pydeck 라이브러리를 통해서 서울시 지도 시각화에 대해 공부하였습니다. 이를 응용하고자 서울시 상권 영역 레이어를 추가하여 데이터 분석을 하고자 합니다. 먼저 데이터에 대해서 설명하겠습니다.

https://www.data.go.kr/tcs/dss/selectFileDataDetailView.do?publicDataPk=15083033 

 

소상공인시장진흥공단_상가(상권)정보_20210331


영업 중인 전국 상가업소 데이터를 제공합니다.
(상호명, 업종코드, 업종명, 지번주소, 도로명주소, 경도, 위도 등)

www.data.go.kr

geocoding

소상공인시장진흥공단에서 제공하는 서울시의 상가(상권) 데이터를 통해서 여러 데이터를 전처리 하고자 합니다. 이 데이터의 기준연도는 2021년 3월입니다.

store.head(1)

 

데이터를 살펴보면 커피전문점, 카페, 다방을 비롯하여 편의점과 학원, 마트, 백화점의 지리정보와 위치가 담긴 데이터를 확인할 수 있습니다. 서울시의 주요 상권 데이터의 정보를 확인할 수 있었으며 pydeck을 통해 레이어를 쌓으려면 하나하나 데이터를 만들면 될 거 같습니다. 

print('---------- 카페_편의점_학원_세탁소 ----------')
print('커피전문점/카페/다방', ":", store[store.상권업종소분류명 == '커피전문점/카페/다방'].shape[0])

print('편의점', ":", store[(store.상권업종소분류명 == '편의점') & (store.상호명.str.contains('GS25|세븐일레븐|CU'))].shape[0] + \
      store[(store.상호명.str.contains('이마트')) & (store.지점명.str.contains('24'))].shape[0] + \
      store[store.상호명 == '이마트24'].shape[0])

print('학원', ":", store[(store.상권업종소분류명.str.contains('학원'))].shape[0])

print('세탁소/빨래방', ":", store[store.상권업종소분류명 == '세탁소/빨래방'].shape[0], '\n')

# 마트 
print('---------- 마트 ----------')
print('이마트' , ":", store[(store.지점명.str.contains('24') == False) & (store.상호명 == '이마트')].shape[0])
print('홈플러스' , ":", store[store.상호명 == '홈플러스'].shape[0])
print('롯데마트' , ":", store[store.상호명 == '롯데마트'].shape[0])
print('하나로마트' , ":", store[store.상호명 == '하나로마트'].shape[0], '\n')

# 백화점(건축,면서,일반백화점)
print('---------- 백화점 ----------')
print('AK플라자' , ":", store[store.상호명 == 'AK플라자'].shape[0])
print('NC백화점' , ":", store[(store.상호명.str.contains('NC')) & (store.상권업종소분류명 == '백화점')].shape[0])
print('갤러리아백화점' , ":", store[store.상호명 == '갤러리아백화점'].shape[0])
print('롯데백화점' , ":", store[(store.상호명.str.contains('롯데')) & (store.상권업종소분류명 == '백화점')].shape[0])
print('신세계백화점' , ":", store[(store.상호명.str.contains('신세계')) & (store.상권업종소분류명 == '백화점')].shape[0])
print('현대백화점' , ":", store[(store.상호명.str.contains('현대')) & (store.상권업종소분류명 == '백화점')].shape[0])

Pydeck을 통한 서울 편의점 위치 지도 시각화
PolygonLayer + ScatterplotLayer

이번에는 서울시에 있는 편의점에 대해 데이터 분석을 하고자 합니다. 편의점의 구분을 세븐일레븐, CU, GS, 이마트24로 나누어서 서울에 얼마나 분포하는지 확인해 보려고 합니다. 서울시의 행정동 경계 layer 위에 ScatterplotLayer를 쌓아서 편의점의 분포를 확인해 보겠습니다.

layer_poly = pdk.Layer(
 'PolygonLayer',
 df,
 get_polygon = 'coordinates',
 get_fill_color = '[0,0,0,50]',
 pickable = True,
 auto_highlight = True
)
layer_7 = pdk.Layer(
 'ScatterplotLayer',
 df_7,
 get_position = '[경도, 위도]',
 get_radius = 50,
 get_fill_color = '[255,0,0]',
 pickable = True,
 auto_highlight = True
)
layer_C = pdk.Layer(
 'ScatterplotLayer',
 df_C,
 get_position = '[경도, 위도]',
 get_radius = 50,
 get_fill_color = '[64, 255, 0]',
 pickable = True,
 auto_highlight = True
)
layer_G = pdk.Layer(
 'ScatterplotLayer',
 df_G,
 get_position = '[경도, 위도]',
 get_radius = 50,
 get_fill_color = '[0, 128, 200]',
 pickable = True,
 auto_highlight = True
)
layer_M = pdk.Layer(
 'ScatterplotLayer',
 df_M,
 get_position='[경도, 위도]',
 get_radius = 50,
 get_fill_color = '[255, 140, 0]',
 pickable = True,
 auto_highlight = True
)

r = pdk.Deck(layers=[layer_poly, layer_7 , layer_C, layer_G, layer_M ], 
            map_style='mapbox://styles/mapbox/outdoors-v11',
             mapbox_key=MAPBOX_API_KEY,
             initial_view_state = view_state)
r.to_html()

여기서 빨간색은 세븐일레븐을, 초록색은 CU, 파란색은 GS, 주황색은 이마트24를 도시화하였습니다. 또한 각 구역에 어떤 편의점이 분포하였는지 스크롤을 통해서 확인할 수 있었습니다.

Pydeck을 통한 서울 편의점과 상권 영역 시각화

우리마을가게 상권분석서비스를 확인하면 초록색이 골목상권 그리고 보라색이 발달 상권으로 표현되어있습니다. 또한 웹에서는 다양한 데이터를 통해서 반응형 그래프를 확인할 수 있는데 이러한 과정을 머신러닝을 이용한 상권분석 및 상권 추천 시스템과 결과물을 Dash를 이용하여 대시보드로 구현하려고 생각을 하고 있습니다. 이를 위해 현재 추천시스템과 그리고 Dash에 대해서 더 깊이 공부하고 있습니다.

상권 영역에 대한 레이어를 쌓아야 하기 때문에 먼저 geocoding을 하려고 합니다. 데이터를 확인해보면 경곗값과 시군구 코드 그리고 행정동 코드로 변환이 가능해 보이는 것을 확인할 수 있었습니다.

df_TB = gpd.read_file('TBGIS.geojson')
df_TB.head(1)

데이터의 경곗값이 POLYGON 형태로 되어있어서 이를 리스트 형태로 바꾸는 전처리가 필요할 거 같습니다.

def polygon_to_coordinates(x):
    lon, lat = x.exterior.xy
    return [[x, y] for x, y in zip(lon, lat)]

df_TB['coordinates'] = df_TB['geometry'].apply(polygon_to_coordinates)
del df_TB['geometry']
df_TB = pd.DataFrame(df_TB)
df_TB.head(1)

이를 통해서 pydeck으로 상권 영역을 시각화해 보았습니다. 현재 상태로는 골목상권과 발달 상권의 분리가 필요해 보이며 서울시의 행정동 경계가 뚜렷하게 보이지 않아서 PolygonLayer를 하나 더 쌓아햐 할 거 같습니다.

layer_TB = pdk.Layer(
    'PolygonLayer',
    df_TB,
    get_polygon='coordinates',
    get_fill_color='[190, 10, 230, 140]',
    pickable=True,
    auto_highlight=True
)

layer_nodes = pdk.Layer(
    'ScatterplotLayer',
    df,
    get_position='[경도, 위도]',
    get_radius=100,
    get_fill_color='[64, 255, 0]',
    pickable=True,
    auto_highlight=True
)

r = pdk.Deck(layers=[layer_TB, layer_nodes],
            map_style='mapbox://styles/mapbox/outdoors-v11',
            mapbox_key=MAPBOX_API_KEY,
            initial_view_state = view_state)
r.to_html()

pydeck을 이용하여 서울시 행정동 경계(회색)+ 서울시 발달 상권(보라색) 및 골목상권(초록색) + 세븐일레븐 매장 위치(빨간색)를 시각화해 보겠습니다. 

layer_poly = pdk.Layer(
    'PolygonLayer',
    df,
    get_polygon='coordinates',
    get_fill_color='[0, 0, 0, 50]',
    pickable=True,
    auto_highlight=True
)
layer_bal = pdk.Layer(
    'PolygonLayer',
    df_bal,
    get_polygon='coordinates',
    get_fill_color='[190, 10, 230, 140]',
    pickable=True,
    auto_highlight=True
)

layer_gol = pdk.Layer(
    'PolygonLayer',
    df_gol,
    get_polygon='coordinates',
    get_fill_color='[64, 255, 0]',
    pickable=True,
    auto_highlight=True
)

layer_7 = pdk.Layer(
 'ScatterplotLayer',
 df_7,
 get_position = '[경도, 위도]',
 get_radius = 80,
 get_fill_color = '[255,0,0]',
 pickable = True,
 auto_highlight = True
)


r = pdk.Deck(layers=[layer_poly, layer_bal,layer_gol,layer_7],
            map_style='mapbox://styles/mapbox/outdoors-v11',
            mapbox_key=MAPBOX_API_KEY,
            initial_view_state = view_state)
r.to_html()

그래프를 확인하면 여러 레이어를 쌓아서 tooltip에 문제가 있다고 판단하였습니다. tooltip에 관한 추가 코딩을 하여 조금 더 깔끔하게 시각화하려고 합니다.

layer_poly = pdk.Layer(
 'PolygonLayer',
 df,
 get_polygon = 'coordinates',
 get_fill_color = '[0,0,0,50]',
 pickable = True,
 auto_highlight = True
)
layer_7 = pdk.Layer(
 'ScatterplotLayer',
 df_7,
 get_position = '[경도, 위도]',
 get_radius = 50,
 get_fill_color = '[255,0,0]',
 pickable = True,
 auto_highlight = True
)
layer_C = pdk.Layer(
 'ScatterplotLayer',
 df_C,
 get_position = '[경도, 위도]',
 get_radius = 50,
 get_fill_color = '[64, 255, 0]',
 pickable = True,
 auto_highlight = True
)
layer_G = pdk.Layer(
 'ScatterplotLayer',
 df_G,
 get_position = '[경도, 위도]',
 get_radius = 50,
 get_fill_color = '[0, 128, 200]',
 pickable = True,
 auto_highlight = True
)
layer_M = pdk.Layer(
 'ScatterplotLayer',
 df_M,
 get_position='[경도, 위도]',
 get_radius = 50,
 get_fill_color = '[255, 140, 0]',
 pickable = True,
 auto_highlight = True
)

r = pdk.Deck(layers=[layer_poly, layer_bal,layer_gol,layer_7 , layer_C, layer_G, layer_M ], 
             map_style='mapbox://styles/mapbox/outdoors-v11',
             mapbox_key=MAPBOX_API_KEY,
             tooltip={
            "html": "<b>주소:</b> {adm_nm}"
            "<br/> <b>상권:</b> {TRDAR_SE_1}"
            "<br/> <b>상권주소:</b> {TRDAR_CD_N} "
            "<br/> <b>상권:</b> {TRDAR_SE_1}"
            "<br/> <b>상권주소 ids:</b> {TRDAR_CD_N}"
            "<br/> <b>편의점명 ids:</b> {상호명}"
            "<br/> <b>편의점주소 ids:</b> {도로명주소}",
            }, 
            initial_view_state = view_state)
r.to_html()

서울시의 상권 영역에 대해 시각화를 해보았습니다. 상권 영역은 크게 골목상권과 발달 상권으로 나누어지는데 관련 변수 및 파생변수들이 많이 존재하고 있습니다. 데이터 분석을 통해서 유의미한 인사이트를 여러 방면으로 도출해 낼 수 있다고 생각합니다. 이번에는 pydeck을 이용하여 서울시에 존재하는 세븐일레븐, CU, GS, 이마트24에 대해 지도 시각화를 하였습니다. 그동안 folium, plotly, dash를 이용하여 지도 시각화를 하였는데 pydeck 또한 강력한 시각화 기능을 가지고 있다고 생각합니다. 또한 표시된 PolygonLayer나 ScatterplotLayer 말고도 pydeck의 여러 툴들이 있으므로 공식 홈페이지를 참고하시길 바라겠습니다. https://deckgl.readthedocs.io/en/latest/index.html

 

Gallery — pydeck 0.6.1 documentation

Better understand the main object within visualization, used to write data out to a widget in Jupyter, save it out to HTML, and configure some global parameters of a visualization, like its size or tooltip.

deckgl.readthedocs.io

QGIS 또는 mapshaper를 통한 geocoding 및 지도 데이터 전처리를 하면서 geocoding의 중요성에 대해 새삼 깨닫게 되었습니다. 또한 어떻게 유의미한 인사이트를 도출해 내고 어떤 걸 도시화할지 생각하는 자세가 중요하다고 생각합니다. 이러한 데이터를 가지고 상권분석 또는 입지 추천 분석이 가능할 것이라고 보고 있습니다. 현재 다양한 곳에서 상권분석에 관한 유용한 서비스를 제공하고 있는데 이러한 서비스와 새로운 데이터 분석 프로젝트가 어떻게 하면 차별점을 갖는지 생각을 해봐야겠습니다. 앞으로의 계획은 그동안의 지도 시각화를 바탕으로 ML 결과물을 Dash를 통해서 대시보드로 제작하려고 합니다. 물론 일전에 Dash를 통해서 대시보드를 구현하였지만 Dash의 풍부한 기능들을 제대로 잘 살리지 못했다고 생각이 들어서 조금 더 공부를 하고자 합니다. 

profile

융무의 기술블로그

@융무

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!