안녕하세요~ 꽁냥이에요. 얼마 전에 두 개 또는 세 개 범주형 변수를 시각화하는 모자이크 플롯(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 |
댓글