파이썬으로 100% Stacked Bar Chart 그리기 (리커트 척도 데이터 시각화)

  • 시각화

내가 하는 일 중에 설문조사 데이터 분석하는 일이 제법 있다. 특히 리커트 척도로 수집된 데이터를 시각화해서 볼 일이 많다.

참고로 리커트 척도는 이런 거.

  1. 전혀 아니다
  2. 아니다
  3. 보통이다
  4. 그렇다
  5. 매우 그렇다

보통 이런 문항을 분석할 때, 대부분의 사람들은 해당 문항의 “평균”을 구해서 데이터를 해석하려 한다. 사실 대부분의 사람들이 아는 게 평균 뿐이기도 하고.

그런데 이렇게 대표값 하나만 가지고 해당 문항의 응답 특성을 설명하는 건 너무나 위험하다. outlier를 반영하지 못하는 문제가 있기 때문에. [5, 1, 1, 1] 과 [2, 2, 2, 2] 둘 다 평균은 2인데 분포가 완전히 다르지 않은가. (심지어 Do not use averages with Likert scale data 라는 이런 책도 있더라.)

그래서 리커트 척도로 수집한 응답을 분석하려면 무조건 분포를 시각화 해서 살펴보는 게 중요하다.

일반적인 상황에서 내가 가장 선호하는 방식은 100% 누적 막대 그래프다. 영어로는 100% Stacked Bar Chart.

어제 이걸 파이썬으로 그려보려다가 혼자 삽질을 많이 했다. 단순히 쌓아올린 Stacked Bar는 그리기 쉬운 편인데, 100% 비율로 환산해서 쉽게 그릴 수 있는 방법은 잘 없더라.

어쨌든 내가 결국 해결한 방법을 정리해서 남겨본다.


데이터

리커트로 수집한 데이터는 대부분 이렇게 생겼을 거다.

import pandas as pd
df = pd.read_excel("data/likert_data.xlsx")
df.head()
Q1Q2Q3Q4Q5
045333
143544
254555
343443
455454

long-form이 아닌 wide-form 이다.

100% Stacked Bar Chart 그리는 방법 1.

각 문항에 대한 응답 빈도(%) 데이터로 가공해서 차트 그리기

일단 원본 데이터를 가공하지 않고 곧바로 차트를 그릴 수 있는 방법은 없는 것 같다. 심지어 만능의 도구인 엑셀로 차트를 그린다고 하더라도 마찬가지다.

그래서 일단 각 문항에 대한 응답 빈도를 구한 후 비율(%)로 변환해줘야 한다. 이렇게.

# 각 문항에 대한 응답 빈도 데이터
frequencies = {}       
for i in ['Q1', 'Q2', 'Q3', 'Q4', 'Q5']:
    frequencies[i] = df[i].value_counts()
# 행/열 전환
df_freq = pd.DataFrame(frequencies).T
# 빈도를 비율로 변환 (문항별)
df_freq = df_freq.div(df_freq.sum(axis=1), axis=0)*100
# columns 순서 정렬 (응답 1,2,3,4,5 순으로)
df_freq = df_freq.reindex(sorted(df_freq.columns), axis=1)

df_freq
12345
Q12.0231212.89017319.94219741.04046234.104046
Q21.1560696.06936424.85549141.61849726.300578
Q31.7341044.62427721.09826646.24277526.300578
Q42.6011562.89017322.54335341.90751430.057803
Q51.7341047.51445121.38728344.21965325.144509
df_freq

이 데이터는 pandas의 plot() 메서드를 활용해 바로 그릴 수 있다.

df_freq.plot(kind="barh", stacked=True, colormap='RdBu')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)

matplotlib이나 seaborn로 시도해보진 않았는데, 아무튼 우아하게 그리는 방법은 없고 굳이 하려면 수동으로 레이어를 쌓아 얹어야 하는 것 같다. (참고)

그래서 요즘에 내가 많이 사용하는 시각화 라이브러리인 plotly로 그려봤다.

# https://plotly.com/python/builtin-colorscales/
scale = px.colors.sequential.RdBu

px.bar(df_freq, orientation="h", 
       color_discrete_sequence=scale)

100% Stacked Bar Chart 그리는 방법 2.

altair를 통해 데이터 가공 없이 쉽게 차트 그리기

altair는 선언적으로(?) 데이터를 시각화할 수 있는 파이썬 라이브러리다. 문법이 쉽고, 데이터 집계가 편하다는 장점이 있다는데 나도 깊이 써보진 않아서 모르겠다. 어쨌든 홈페이지에 예제가 엄청 많은 것만으로도 매우 믿음이 간다. 당분간 plotly의 대항마가 될 것 같은 느낌.

어쨌든 altair 를 사용하면 long-form이든 wide-form이든 데이터프레임에 손을 대지 않고 한 방에 그릴 수 있다.

(1) long-form

altair 홈페이지에 가면 Normalized Stacked Bar Chart 그리는 예제가 있어서 이걸 보고 따라해봤다.

일단 리커트 척도 수집 데이터는 wide-form이기 때문에 long-form으로 변환이 필요하다. 그래서 pandas melt를 사용해서 일단 데이터를 변환 시키고.

df_melt = pd.melt(frame=df,
                  id_vars=None,
                  var_name="문항",
                  value_vars=['Q1', 'Q2', 'Q3', 'Q4', 'Q5'],
                  value_name="응답")

색상 스케일을 적용해서 그려봤다.

scale = alt.Scale(scheme="redyellowblue")
# https://vega.github.io/vega/docs/schemes/

alt.Chart(df_melt).mark_bar().encode(
    x = alt.X("응답:O", aggregate="count", stack="normalize", sort="descending"),
    y = alt.Y("문항:N"),
    color = alt.Color("응답:O", scale=scale)
)

변수를 넣을 때 그 속성을 인코딩 해줄 수가 있다는 게 신기한 점이다. 이를 테면 변수 이름 뒤에 콜론(:)을 붙여 변수 유형을 명시해준다. Q는 quantitative, O는 ordinal, N은 nominal 과 같은 식이다.

그리고 집계(aggregate) 값으로도 "mean""sum""median""count" 등을 지정할 수도 있다.

아무튼 나타난 차트는 다음과 같다.

(2) wide-form

원본 데이터인 wide-form 상태에서 곧바로 그릴 수 있는 방법이 있는지 알아보니 transform_fold 메서드를 사용하면 가능하다는 걸 알았다.

import altair as alt

# https://vega.github.io/vega/docs/schemes/
scale = alt.Scale(scheme="redyellowblue")

# https://altair-viz.github.io/user_guide/transform/fold.html
alt.Chart(df).transform_fold(['Q1', 'Q2', 'Q3', 'Q4', 'Q5']).mark_bar().encode(
    x = alt.X("value:O", aggregate="count", stack="normalize", sort="descending", title="빈도(%)"),
    y = alt.Y("key:N", title="문항"),
    color = alt.Color("value:O", scale=scale, title="응답")
)

wide-form 데이터를 넣어주더라도 transform_fold를 하면 알아서 key, value 쌍을 가진 long-form으로 변환시켜주는 것 같다.

이번엔 변수 인코딩 할 때 title 속성들을 손으로 추가해봤다.

처음에 혼자 무식하게 그려보겠다고 얼마나 삽질했나 몰라. 역시 천재들이 미리 만들어놓은 라이브러리를 잘 갔다 쓰면 된다. 이번엔 altair 덕을 좀 봐서 왠지 앞으로도 애용하게 될 것 같다. (seaborn은 이제 손이 잘 안 가네.)

아무튼 파이썬으로 100% Stacked Bar Chart (100% 누적 막대 그래프) 그리기 성공.

블로그 추천 글

“파이썬으로 100% Stacked Bar Chart 그리기 (리커트 척도 데이터 시각화)”의 2개의 댓글

댓글 남기기