본문 바로가기
Programming/Python

Streamlit - Kaggle DALYs 데이터 분석 & 시각화 - 3

by BTC_박현영 2023. 9. 15.

베하~ 안녕하세요! 현상수배범입니다.

지난 시간에는 streamlit을 DALYs 데이터의 각각의 변수(질병/재해) 간의 상관 관계 확인 및 시각화를 해보았습니다.

이번 시간에는 이번에는 좀 더 발전된 시각화를 위해 GeoPandas와 Pydeck 라이브러리를 활용해보려고 합니다.

 

GeoPandas란?

GeoPandas는 기본적으로 GeoDataFrame이라는 데이터 구조를 사용합니다. 이것은 Pandas의 DataFrame을 확장한 것으로, 각 행이 공간적 객체와 연관된 데이터를 가질 수 있습니다. 공간적 객체는 주로 포인트, 라인, 폴리곤 등을 의미합니다.

주요 기능
1. 공간 데이터 로딩: 다양한 형식의 공간 데이터 파일 (예: GeoJSON, Shapefile)을 쉽게 읽을 수 있습니다.
2. 데이터 조작: Pandas와 같은 편리한 데이터 조작 메서드를 제공하며, 이에 추가로 공간 데이터에 특화된 여러 가지 메서드도 제공합니다.
3. 공간 연산: 두 개의 공간 데이터 집합 사이의 교집합, 합집합, 차집합 등의 공간 연산을 수행할 수 있습니다.
4. 시각화: Matplotlib을 기반으로한 간단하면서도 효과적인 공간 데이터 시각화 기능을 제공합니다.

 

Pydeck이란?

Pydeck은 Uber사에서 개발한 Deck.gl을 기반으로 하는 Python 라이브러리입니다. Deck.gl은 자바스크립트 공간 데이터 시각화 라이브러리로, 대용량 데이터도 거뜬하게 렌더링할 수 있으며, 렌더링이 좀 버거울 수도 있는 일부 레이어는 GPU 연산을 제공하기 때문에 대용량 처리 및 렌더링에 매우 용이합니다. Pydeck에서의 각 '레이어'는 공간 데이터의 한 형태를 나타내며, 여러 레이어를 조합하여 복잡한 시각화를 만들 수 있습니다.

 

주요 기능

1. 대규모 데이터 렌더링: WebGL을 활용해 브라우저에서 대규모의 공간 데이터를 빠르게 렌더링할 수 있습니다.

2. 다양한 레이어 타입: 점, 라인, 폴리곤, 히트맵 등 다양한 형태의 레이어를 지원합니다.

3. 인터랙티브: 마우스 이벤트나 키보드 이벤트에 반응하는 인터랙티브한 시각화를 생성할 수 있습니다.

4. 직관적인 API: Pydeck은 간단하면서도 강력한 Python API를 제공하여, 복잡한 시각화도 쉽게 구성할 수 있습니다.

 

위 두 라이브러리를 활용해 지도에 대규모 데이터를 시각화한다거나, 최적의 경로를 계산하고 비용 분석 및 시각화가 가능한 등, 함께 사용하면 많은 시너지를 발휘할 수 있습니다. 이번에는 저도 처음 사용해보는 것이기 때문에, 간단하게 사용해보도록 하겠습니다. Pydeck의 경우 데이터를 사용할 때 API 키가 필요한데, 저는 GeoPandas에서 맵 데이터를 가지고 온 뒤, DALY 데이터와 병합하여 사용할 것이고, Pydeck으로는 시각화만 해보도록 하겠습니다.

 

1. 데이터 전처리

먼저 GeoPandas 라이브러리에서 세계 지도 데이터를 가지고 온 뒤, 국가명이 같은지 확인해보았습니다.

일부 데이터(United States, World mean ...)등 기존 데이터에서 존재하지만 세계 지도 데이터에는 없는 목록은 다음과 같습니다.

'United States'의 경우 세계 지도 데이터에서는 'United States of America'를 의미합니다. 이처럼 국가명이 조금 다른 경우에는 수작업으로 일치시켜주었습니다. 그외에 지역을 나타내는 경우('Western Europe', World', ...) 등의 경우에는 제외하였습니다. 이후 년도별로 구분된 값을 변수를 기준으로 평균(mean)으로 수정했습니다. 

전처리된 데이터를 세계 지도 데이터와 병합하고, Min-Max Scaling을 했습니다. 나중에 pdk를 통해 시각화를 할 때, RGB 채널 값을 입력하게 되는데, 이 때 Min-Max Scaling을 통해 채널 값이 가지는 최소~최댓값인 0~255로 매핑해주기 위해서였습니다. 이렇게 전처리를 마친 뒤, 지난 시간에 활용했던 st.tabs를 활용하여 시각화하였습니다.

 

2. 시각화

시각화는 이전에 사용했던 tabs element를 활용하였습니다. 각 변수마다 tab들이 생기고, tab마다 boxplot과 세계지도 시각화 결과를 보여주게 됩니다.

 

def main():
    st.set_page_config(layout="wide")

    raw_data = file_upload()
    
    if raw_data != None:
        raw_data = pd.read_csv(raw_data)
        raw_data = raw_data.drop(columns=['Unnamed: 0'])
        
        raw_columns = raw_data.columns.tolist()
        exclude_columns = ['Entity', 'Code', 'Year']
        columns = [column for column in raw_columns if column not in exclude_columns]
        
        countries = raw_data['Entity'].unique().tolist()
        selected_entities = st.sidebar.multiselect(label='Select Entities',
                                                    options=countries,
                                                    key='selected_entities',
                                                    help='Please select Entities to analyze. If nothing is selected, it is considered as selecting all.') or countries
        selected_DALY = st.sidebar.multiselect(label='Select DALY options',
                                                  options=columns,
                                                  key='selected_DALY',
                                                  help='Please select options to perform DALY analysis. If nothing is selected, it is considered as selecting all. "Entity", "Code", "Year" Columns are always included') or columns
        if st.sidebar.button('submit'):
            data = preprocess_data(raw_data, selected_entities, selected_DALY, exclude_columns)

            # 세계 지도 데이터 로드
            world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
            
            # boxplot
            tabs = st.tabs(selected_DALY)
            for i in range(len(selected_DALY)):
                chart = alt.Chart(data).mark_boxplot(extent='min-max').encode(
                    x='Entity:N',
                    y=f'{selected_DALY[i]}:Q',
                    color='Entity:N'
                )
                with tabs[i]:
                    st.altair_chart(chart, theme="streamlit", use_container_width=True)
                    
                    column = selected_DALY[i]
                    col_rename = column.replace(" ", "_")
                    average_by_country = data.groupby("Entity")[f"{column}"].mean().reset_index()
                    average_by_country.rename(columns={f'{column}': f'{col_rename}'}, inplace=True)
                    average_by_country['Entity'] = average_by_country['Entity'].replace('United States', 'United States of America')
                    
                    # 세계 지도 데이터와 병합
                    merged_data = world.set_index('name').join(average_by_country.set_index('Entity'))
                    
                    # Min-Max Scaling(RGB 값인 0 ~ 255로 변환하기 위함)
                    Min = merged_data[col_rename].min()
                    Max = merged_data[col_rename].max()
                    # GeoDataFrame을 Pydeck 레이어로 변환
                    layer = pdk.Layer("GeoJsonLayer",
                                      data=merged_data,
                                      get_fill_color=f'{col_rename} == null ? [128, 128, 128] : [0, ({col_rename} - {Min}) / ({Max} - {Min}) * 255, 0]',
                                      get_line_color=[0, 0, 0],
                                      opacity=0.8,
                                      stroked=True,
                                      filled=True,
                                      extruded=True,
                                      auto_highlight=True,
                                      pickable=True)

                    # Pydeck 차트 생성
                    view_state = pdk.ViewState(latitude=0, longitude=0, zoom=1)
                    r = pdk.Deck(layers=[layer], initial_view_state=view_state)

                    st.pydeck_chart(r)

 

위처럼 코드를 작성하고, streamlit run ~~~.py를 실행하면 되겠습니다.

 

3. 실행결과

좌측 sidebar에서 변수들을 선택한 뒤 submit 버튼을 클릭하면, 선택된 컬럼에 대해 tab이 생성되고 각 컬럼에 대한 Boxplot과 world map이 표시되는 것을 확인할 수 있었습니다.

댓글