안녕하세요~ 꽁냥이에요. 얼마 전에 두 개 또는 세 개 범주형 변수를 시각화하는 모자이크 플롯(Mozaic Plot)이 있다는 것을 알게 되었는데요. 안타깝게도 Matplotlib에서는 자체적으로 모자이크 플롯(Mozaic Plot)을 그려주진 않더라고요.
그래서 꽁냥이가 자체적으로 Matplotlib을 이용해서 모자이크 플롯을 그리는 방법을 소개하려고 합니다.
꽁 냥이는 2개 범주형 변수에 대해서만 고려했어요.
3개가 되면 좀 지저분해 보이기도 하고 구현하기 귀찮기도 했어요 ㅎㅎ;
모자이크 플롯(Mozaic Plot) 함수
먼저 아래는 모자이크 플롯을 그려주는 함수입니다.
이 함수는 데이터프레임 df, x축에 표시할 범주형 칼럼 x_class_label, y축에 표시할 범주형 칼럼, y_class_label, 그리고 인접한 상자의 간격을 설정하는 padding인자가 있습니다. 이때 x_class_label, y_class_label은 데이터프레임의 칼럼 속에 포함되어야 하며 범주형 칼럼이라고 가정하고 있습니다. 예를 들면 아래와 같습니다.

그럼 이 함수에 대해서 알아보겠습니다. 핵심 부분이라고 생각되는 것만 설명하고 나머지는 주석을 참고해주세요.
def mozaic_plot(df, x_class_label, y_class_label, padding=0.025): fig = plt.figure(figsize=(10, 10)) fig.set_facecolor('white') ax = fig.add_subplot() y_class = df[y_class_label].unique() # y축에 표시할 class 유니크 원소 x_class = df[x_class_label].unique() # x축에 표시할 class 유니크 원소 num_y_box = len(y_class) # y_class 범주 개수 num_x_box = len(x_class) # x_class 범주 개수 colors = sns.color_palette('hls', num_y_box) # 색상은 y 클래스 개수만큼 만들어준다. x_class_to_colors = dict(zip(y_class, colors)) # x_class를 key, 색상을 value로 하는 딕셔너리 ## 가로 길이 비율 width_weights = df[x_class_label].value_counts()/len(df) width_weights = width_weights*(1-padding*(num_y_box-1)) # 패딩을 고려하여 비율 조절 width_weights = width_weights.sort_index() # 순서 바꿈 left_lower_x = 0 # 상자 좌측 하단 x좌표 xticks = [] # x 눈금 좌표 for i in range(num_x_box): target_x_class = x_class[i] target_df = df[df[x_class_label]==target_x_class] height_weights = target_df[y_class_label].value_counts()/len(target_df) height_weights = height_weights*(1-padding*(len(height_weights)-1)) height_weights = height_weights.sort_index() if i > 0: left_lower_x += width_weights[i-1]+padding # 상자 좌측 하단 x 좌표를 오른쪽으로 이동 left_lower_y = 0 # 상자 좌측 하단 y좌표 add_xtick = True # x 눈금 좌표를 xticks에 추가할지 말지 여부 for j in range(len(height_weights)): target_y_class = height_weights.index[j] if j > 0: left_lower_y += height_weights[j-1]+padding # 상자 좌측 하단 y 좌표를 위로 이동 # 상자 추가 rect = patches.Rectangle( (left_lower_x, left_lower_y), # 좌측 하단 시작점 width_weights[i], # 가로 길이 height_weights[j], # 새로 길이 facecolor = x_class_to_colors[target_y_class], # 면색상 edgecolor='k', # 테두리 색상 fill=True, # 색칠 여부 alpha=0.5 # 투명도 ) ax.add_patch(rect) ## x class 텍스트 표시 rx, ry = rect.get_xy() cx = rx+rect.get_width()/2 if add_xtick: xticks.append(cx) cy = ry+rect.get_height()/2 ax.annotate(target_y_class, (cx, cy), weight='bold', fontsize=15, ha='center', va='center') add_xtick = False # 한번 추가한 경우 중복되지 않도록 False로 바꿈 ax.xaxis.tick_top() # x축 눈금과 라벨을 위쪽에 표시한다. ax.set_xticks(xticks) # x축 눈금 좌표 설정 ax.set_xticklabels(x_class) # x축 눈금 라벨 설정 ## x축 눈금 라벨 폰트 사이즈 및 눈금은 표시안되게~ ax.tick_params(axis='x', which='major', labelsize=15, length=0) ax.tick_params(axis='y', which='major', length=0, labelsize=0) ## axis 축 제거 for direction in ax.spines.keys(): ax.spines[direction].set_visible(False) ## lim 조정 ax.set_xlim(-padding/2, 1+padding/2) ax.set_ylim(-padding/2, 1+padding/2) plt.show()
line 15~17
먼저 x_class_label의 범주 빈도수 비율에 따라 가로길이의 비율을 정해줍니다(line 15). 그리고 전체 길이에서 패딩 만큼을 빼고 나서 가로길이의 비율을 보정해줍니다(line 16). value_counts는 index 순서가 원하는 대로 나오지 않을 수 있어서 sort_index()로 고정시켰습니다(line 17).
line 19~57
이제 이중 루프를 돌면서 상자를 하나씩 그려나갈 것입니다. x축 범주형 원소를 하나 고정시켜놓으면 y축 범주의 빈도수 비율에 따라 세로 길이 비율을 정해줍니다(line 22~26). 그러고 나서 y축에 나타낼 2번째 범주형 변수의 원소 개수만큼 상자를 아래에서 위로 그려나갑니다(line 32~57).
예제
이제 샘플 데이터를 이용하여 모자이크 플롯(Mozaic Plot)을 그려봅시다. 여기서는 두 범주의 조합이 모두 포함된 경우와 아닌 경우 모자이크 플롯이 제대로 그려지는지 확인할 거예요.
a. 두 범주의 조합이 모두 포함된 경우
먼저 다음과 같이 Year, Class 두 범주형 변수의 조합이 모두 포함된 경우에 대해서 모자이크 플롯을 그려볼게요.
df = pd.DataFrame() df['Year'] = ['1960s']*20+['1970s']*35+['1980s']*50 df['Class'] = ['A']*10+['B']*5+['C']*5 + ['A']*10+['B']*20+['C']*5+['A']*15+['B']*19+['C']*16
x 축에 표시할 칼럼은 Year, y 축에 표시할 칼럼은 Class입니다.
x_class_label = 'Year' y_class_label = 'Class' padding = 0.025 mozaic_plot(df, x_class_label, y_class_label, padding=0.025)

보시는 바와 같이 예쁘게 잘 그려진 것을 알 수 있어요.
a. 두 범주의 조합이 모두 포함되지 않는 경우
두 범주라고 해서 모든 조합이 다 포함되지 않을 수 있습니다. 아래와 데이터프레임과 같이 말이죠. 보시면 1960s에 대하여 Class 칼럼에는 A, B를 제외한 C만 있는 것을 확인할 수 있습니다.
df = pd.DataFrame() df['Year'] = ['1960s']*20+['1970s']*35+['1980s']*50 df['Class'] = ['C']*20 + ['A']*10+['B']*25+['A']*15+['B']*19+['C']*16
이 경우에도 모자이크 플롯이 잘 그려지는지 확인해볼게요.
x_class_label = 'Year' y_class_label = 'Class' padding = 0.025 mozaic_plot(df, x_class_label, y_class_label, padding=0.025)

문제없이 잘 작동합니다~~ 짝짝!!
이번 포스팅에서는 Matplotlib을 이용하여 모자이크 플롯(Mozaic Plot)을 그리는 방법에 대해서 알아보았습니다. 부디 많은 분께 도움이 되기를 바라며 이상 포스팅 마치겠습니다. 지금까지 꽁냥이의 글 읽어주셔서 감사합니다.

'데이터 분석 > 시각화' 카테고리의 다른 글
[Matplotlip] x축 또는 y축 눈금 사이의 축 선(axis line) 색상 변경하기 (0) | 2022.07.16 |
---|---|
[Matplotlib] x축 눈금 위에 표시, y축 눈금 오른쪽에 표시하기 (390) | 2022.07.16 |
[Matplotlib] gridspec을 이용하여 여러 Axes 배치하기(feat. subplot) (413) | 2022.06.17 |
[Matplotlib] fill_between을 이용하여 두 곡선 사이 영역 색칠하기 (394) | 2022.05.24 |
[Matplotlib] mplcyberpunk 모듈 - 'cyberpunk' 스타일 소개 (404) | 2022.05.24 |
댓글