Seaborn 라이브러리의 FacetGrid
는 복잡한 데이터를 다양한 각도에서 시각화할 수 있게 해줍니다. FacetGrid
는 하나의 그래프에 여러 서브 플롯을 만들어 각각 다른 변수의 조합을 시각적으로 표현할 수 있게 해줍니다. 예를 들어, 다양한 범주형 변수에 따른 데이터의 분포를 동시에 비교하고 싶을 때 FacetGrid
를 사용할 수 있습니다.
# FacetGrid 사용 예시
g = sns.FacetGrid(data, col='범주형 변수')
g.map(plt.hist, '연속형 변수')
plt.show()
이 코드는 데이터셋 내의 '범주형 변수'에 따라 서로 다른 히스토그램을 그려주는 예시입니다. 이렇게 FacetGrid
를 사용하면 데이터의 다양한 측면을 한눈에 볼 수 있어, 데이터 분석 과정에서 매우 유용합니다. 이번 포스트에서는 FacetGrid
를 활용해 콘텐츠 이용률 데이터를 살펴보도록 하겠습니다.
시각화 연습을 위해, 저는 콘텐츠 이용률과 관련된 데이터 세트를 선택했습니다. 이 데이터는 과기부에서 '스마트폰과의존실태조사'를 위해 수집한 데이터로 연도별로 스마트폰을 이용한 다양한 콘텐츠의 이용률을 보여주며, 응답자의 특성(예: 연령대, 과의존 수준)에 따라 세분화되어 있습니다. 각각의 특성별로 데이터를 분석하고 시각화하면, 시간의 흐름에 따라 변화하는 이용률의 트렌드를 파악할 수 있습니다.
이 데이터 세트는 통계청에서 제공한 것으로, 데이터 출처는 통계청 웹사이트에서 확인할 수 있습니다.
응답자특성별(1) | 응답자특성별(2) | 응답자특성별(3) | 2020 | 2020.1 | 2020.2 | 2020.3 | 2020.4 | 2020.5 | 2020.6 | ... | 2022.15 | 2022.16 | 2022.17 | 2022.18 | 2022.19 | 2022.20 | 2022.21 | 2022.22 | 2022.23 | 2022.24 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 응답자특성별(1) | 응답자특성별(2) | 응답자특성별(3) | 뉴스 보기 | 학업/업무용 검색 | 관심사(취미)검색 | 상품/서비스 정보 검색 | 교통 및 위치정보 검색 | 기타 일반적인 웹서핑 | 게임 | ... | SNS | 새로운 친구 만남 | 상품/서비스 구매 | 상품/서비스 판매 | 금융 | 생활관리 | 건강관리 | 화상회의/원격근무 | 필수교육 | 사설교육 |
1 | 스마트폰 과의존 수준별 | 과의존위험군 | 고위험군 | 92.8 | 89.1 | 95.8 | 92.5 | 91.5 | 88.4 | 88.3 | ... | 87.4 | 59.1 | 87.3 | 81.2 | 87 | 78.3 | 76.4 | 55 | 41.1 | 40.3 |
2 | NaN | NaN | 잠재적위험군 | 91.3 | 88.5 | 93.2 | 90.2 | 89.6 | 84.7 | 85.8 | ... | 83.8 | 48.6 | 81.8 | 72.8 | 80.7 | 74.8 | 74.1 | 44.6 | 40.2 | 37.6 |
3 | 연령대별 | 유아동(만3~9세) | 과의존위험군 | 22.4 | 49.2 | 36.1 | 22.8 | 19.5 | 28.4 | 83.9 | ... | 18.9 | 6.4 | 5.7 | 5.5 | 5.2 | 10.3 | 11.2 | 2.8 | 31.4 | 23 |
4 | NaN | NaN | 일반사용자군 | 17.9 | 45.7 | 28.6 | 16.6 | 13.6 | 20.9 | 76.4 | ... | 16.8 | 6.2 | 5.4 | 4.5 | 4.4 | 7.9 | 6.9 | 3.7 | 24.5 | 17.9 |
5 rows × 78 columns
다운 받은 데이터를 시각화에 활용하기 적합하도록 전처리 합니다.
df.columns
>output
Index(['응답자특성별(1)', '응답자특성별(2)', '응답자특성별(3)', '2020', '2020.1', '2020.2',
'2020.3', '2020.4', '2020.5', '2020.6', '2020.7', '2020.8', '2020.9',
'2020.10', '2020.11', '2020.12', '2020.13', '2020.14', '2020.15',
'2020.16', '2020.17', '2020.18', '2020.19', '2020.20', '2020.21',
'2020.22', '2020.23', '2020.24', '2021', '2021.1', '2021.2', '2021.3',
'2021.4', '2021.5', '2021.6', '2021.7', '2021.8', '2021.9', '2021.10',
'2021.11', '2021.12', '2021.13', '2021.14', '2021.15', '2021.16',
'2021.17', '2021.18', '2021.19', '2021.20', '2021.21', '2021.22',
'2021.23', '2021.24', '2022', '2022.1', '2022.2', '2022.3', '2022.4',
'2022.5', '2022.6', '2022.7', '2022.8', '2022.9', '2022.10', '2022.11',
'2022.12', '2022.13', '2022.14', '2022.15', '2022.16', '2022.17',
'2022.18', '2022.19', '2022.20', '2022.21', '2022.22', '2022.23',
'2022.24'],
dtype='object')
columns_2020 = np.concatenate([np.arange(3), np.arange(3, 28)])
columns_2021 = np.concatenate([np.arange(3), np.arange(28, 53)])
columns_2022 = np.concatenate([np.arange(3), np.arange(53, 78)])
df_2020 = df.iloc[:, columns_2020]
df_2021 = df.iloc[:, columns_2021]
df_2022 = df.iloc[:, columns_2022]
datas = [df_2020, df_2021, df_2022]
year = 2020
for data in datas:
data.columns = data.iloc[0, :]
data.drop(index=0, inplace=True)
data.loc[:, '연도'] = year
year += 1
ndf = pd.concat(datas, ignore_index=True)
ndf.fillna(method='ffill', inplace=True)
응답자특성별(1) | 응답자특성별(2) | 응답자특성별(3) | 뉴스 보기 | 학업/업무용 검색 | 관심사(취미)검색 | 상품/서비스 정보 검색 | 교통 및 위치정보 검색 | 기타 일반적인 웹서핑 | 게임 | ... | 새로운 친구 만남 | 상품/서비스 구매 | 상품/서비스 판매 | 금융 | 생활관리 | 건강관리 | 화상회의/원격근무 | 필수교육 | 사설교육 | 연도 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 스마트폰 과의존 수준별 | 과의존위험군 | 고위험군 | 92.8 | 89.1 | 95.8 | 92.5 | 91.5 | 88.4 | 88.3 | ... | 38.2 | 81.5 | 57.8 | 79.8 | 68.0 | 58.4 | 28.1 | 33.7 | 24.8 | 2020 |
1 | 스마트폰 과의존 수준별 | 과의존위험군 | 잠재적위험군 | 91.3 | 88.5 | 93.2 | 90.2 | 89.6 | 84.7 | 85.8 | ... | 33.9 | 76.9 | 49.5 | 75.7 | 56.9 | 47.7 | 23.3 | 33.7 | 24.0 | 2020 |
2 | 연령대별 | 유아동(만3~9세) | 과의존위험군 | 22.4 | 49.2 | 36.1 | 22.8 | 19.5 | 28.4 | 83.9 | ... | 9.6 | 11.1 | 7.3 | 7.7 | 9.9 | 9.8 | 1.2 | 32.0 | 14.6 | 2020 |
3 | 연령대별 | 유아동(만3~9세) | 일반사용자군 | 17.9 | 45.7 | 28.6 | 16.6 | 13.6 | 20.9 | 76.4 | ... | 5.0 | 5.1 | 3.6 | 4.1 | 5.6 | 5.4 | 0.6 | 28.5 | 11.0 | 2020 |
4 | 연령대별 | 청소년(만10~19세) | 과의존위험군 | 87.7 | 97.8 | 95.9 | 84.8 | 83.6 | 82.0 | 97.2 | ... | 34.5 | 48.8 | 27.7 | 32.1 | 42.6 | 31.1 | 2.9 | 85.8 | 46.0 | 2020 |
5 rows × 29 columns
컬럼 제목을 적절하게 설정하고, 멀티 인덱스로 되어 있던 '연도'를 컬럼으로 지정했습니다. 정리한 데이터 프레임을 사용해 FacetGrid를 이용해 시각화를 연습해 보겠습니다.
FacetGrid 기능을 활용하면, 데이터의 여러 하위 집합에 대해 복수의 그래프를 효과적으로 생성할 수 있습니다. 연령대와 스마트폰 의존도별로 나누어진 콘텐츠 이용률을 연도별로 시각화하여, 시간에 따른 변화를 관찰해보겠습니다.
다음 코드는 Seaborn의 husl 팔레트를 사용하여 '뉴스 보기', '게임', '영화,TV,동영상', 'SNS' 등 주요 콘텐츠의 연도별 이용률을 시각화합니다. 각 콘텐츠 카테고리는 고유한 마커와 라인 스타일을 가지며, 연령대와 스마트폰 의존도별로 그룹화된 서브플롯에 나타납니다.
# 연령대별 데이터 필터링
age_data = ndf[ndf['응답자특성별(1)'] == '연령대별']
age_data['연도'] = age_data['연도'].astype('str')
palette = sns.color_palette('husl', 4)
g = sns.FacetGrid(age_data, col='응답자특성별(3)', row='응답자특성별(2)', height=3, aspect=2)
g.map(sns.lineplot, '연도', '뉴스 보기', marker='o', linestyle=':', color=palette[0], label='쇼핑')
g.map(sns.lineplot, '연도', '게임', marker='^', linestyle='--', color=palette[1], label='게임')
g.map(sns.lineplot, '연도', '영화,TV,동영상', marker='>', linestyle='-.', color=palette[2], label='동영상')
g.map(sns.lineplot, '연도', 'SNS', marker='<', linestyle='-', color=palette[3], label='SNS')
g.set_ylabels('콘텐츠 이용률')
g.add_legend()
plt.show()
서브플롯의 타이틀은 연령대와 스마트폰 의존도 범주를 명시적으로 표시하여, 각 그래프가 어떤 데이터 세그먼트를 나타내는지 분명히 합니다. 이와 같은 방식으로, 복잡한 정보를 한눈에 파악할 수 있도록 합니다.
이제 비슷한 작업을 시각화 관련 코드를 함수로 작성해 데이터를 더 살펴보도록 하겠습니다.
ndf['응답자특성별(1)'].unique()
>output
array(['스마트폰 과의존 수준별', '연령대별', '성별', '학령별', '도시규모별'], dtype=object)
연령대를 기준으로 데이터를 살펴봤으니 다른 것들도 확인해 보겠습니다.
sex_data = ndf[ndf['응답자특성별(1)'] == '성별']
edu_data = ndf[ndf['응답자특성별(1)'] == '학령별']
city_data = ndf[ndf['응답자특성별(1)'] == '도시규모별']
시각화 관련 코드를 함수로 작성합니다.
def draw_FacetGrid(df):
palette = sns.color_palette('husl', 4)
g = sns.FacetGrid(df, col='응답자특성별(3)', row='응답자특성별(2)', height=3, aspect=2)
g.map(sns.lineplot, '연도', '뉴스 보기', marker='o', linestyle=':', color=palette[0], label='쇼핑')
g.map(sns.lineplot, '연도', '게임', marker='^', linestyle='--', color=palette[1], label='게임')
g.map(sns.lineplot, '연도', '영화,TV,동영상', marker='>', linestyle='-.', color=palette[2], label='동영상')
g.map(sns.lineplot, '연도', 'SNS', marker='<', linestyle='-', color=palette[3], label='SNS')
g.set_ylabels('콘텐츠 이용률')
g.set_titles(row_template="{row_name}", col_template="{col_name}")
g.add_legend()
plt.show()
함수를 활용해 특성별로 시각화를 해보겠습니다.
학령
도시규모
이번 포스트를 통해 FacetGrid를 활용한 다차원 데이터 시각화의 힘을 확인할 수 있었습니다. 데이터의 다양한 슬라이스를 한눈에 볼 수 있게 해주는 이러한 시각화 기법은, 복잡한 데이터 세트에서 의미 있는 패턴과 인사이트를 추출하는 데 있어 중요하게 사용할 수 있습니다.
데이터 시각화를 통해 숫자로는 발견할 수 없었던 새로운 측면을 발견할 수 있습니다. 그리고 그런 발견은 데이터에 기반한 결정을 내리는 데 중요한 역할을 합니다. 비즈니스 전략을 세우거나, 고객 행동을 이해하거나, 사회적 트렌드를 분석하는 데 있어, 시각화는 복잡한 정보를 단순화하고 통찰력을 제공합니다.