본문 바로가기
데이터 분석/시각화

[바 차트(Bar chart)] 4. Matplotlib을 이용하여 그룹 바 차트(Grouped bar chart) 그리기

by 부자 꽁냥이 2020. 6. 22.

안녕하세요~ 꽁냥이에요.

 

이번 포스팅에서는 바 차트 4편으로 Matplotlib을 이용하여 그룹 바 차트(Grouped bar chart)를 그려볼 거예요. 바 차트에 대한 내용은 다음과 같이 5편으로 나누어져 있습니다.

 


1. Matplotlib을 이용하여 바 차트, 수평 바 차트 그리기

2. Matplotlib을 이용하여 바 차트 꾸미기

3. Matplotlib 바 차트 번외 - 막대에 그라데이션 적용하기

4. Matplotlib을 이용하여 그룹 바 차트(Grouped bar chart) 그리기

5. Matplotlib을 이용하여 스택 바 차트(Stacked bar chart) 그리기


먼저 다음의 상황을 보겠습니다.


나팀장 : 꽁냥씨~ 어디가여?

 

꽁냥이 : 아 팀장님! 이제 퇴근하려고 합니다.

 

나팀장 : 아 그렇군요. 다름이 아니고 방금 A, B, C, D 영업점에서 분기별 매출 데이터를 받았어요. 분기별로 4개 영업점의 매출을 정리해서 오늘 중으로 보내주세요. 내가 눈이 안 좋으니 표 말고 그림으로 정리해서 보내주세요.

 

꽁냥이 : 네! 알겠습니다.

 

꽁냥이는 오늘도 야근을 합니다.


위 상황에서 꽁냥이가 해야 할 일은 팀장님께 분기별로 4개 영업점의 매출을 그림으로 보여줘야 하는 거예요. 여기서 주목해야 할 것은 매출에 대한 카테고리가 2개가 있어요. 바로 '분기''영업점'이지요. 이처럼 2개의 카테고리로 세분화해서 각종 통계치를 확인하고 싶을 때 사용되는 그래프가 그룹 바 차트(Grouped bar chart)입니다.

 

지금부터 Matplotlib을 이용하여 그룹 바 차트를 만드는 방법을 소개합니다. 

 

1. 목표

2. 데이터 준비

3. 핵심 전략

4. 그룹 바 차트 그리기

LIST

   1. 목표

꽁냥이의 목표는 그룹 바 차트를 이용해서 분기별 매출을 A, B, C, D로 세분화하여 그릴 거예요.

 


   2. 데이터 준비

먼저  A, B, C, D 영업점의 분기별 매출이 다음과 같다고 할게요.

Quarter A B C D
1Q 300 400 250 100
2Q 100 200 500 200
3Q 300 400 200 300
4Q 250 100 300 400

 

위 데이터를 만들어보겠습니다. 아래 코드를 실행해주세요.

import pandas as pd
### 데이터 
df = pd.DataFrame()
df['Quarter'] = ['1Q','2Q','3Q','4Q']
df['A'] = [300,100,300,250]
df['B'] = [400,200,400,100]
df['C'] = [250,500,200,300]
df['D'] = [100,200,300,400]
반응형

   3. 핵심 전략

그룹 바 차트를 그리기 위한 꽁냥이의 전략은 다음과 같아요.

 


1) 메인 카테고리와 서브 카테고리를 지정

2) 막대기 폭 계산

3) 메인 카테고리가 표시될 좌표 계산

4) 서브 카테고리에 대응하는 막대기의 좌표 계산

5) 여백 조정


 

1) 메인 카테고리와 서브 카테고리를 지정

 

 

먼저 x축에 표시할 메인 카테고리를 지정합니다. 여기서는 '분기'가 메인 카테고리입니다. 그리고 서브 카테고리를 지정해서 이에 대응하는 막대기를 그릴 거예요. 여기서는 'A, B, C, D' 즉, 영업점이 서브 카테고리입니다.

 

2) 막대기 폭 계산

 

Matplotlib에서 막대기 폭은 x축 눈금 하나의 범위를 기준으로 했을 때 상대적인 비율로 구하게 되어 있지요. 아래의 그림은 이 비율에 따른 폭의 변화를 보여주고 있어요.

 

꽁냥이는 x축 눈금 하나에 4개(정확히 말하면 서브 카테고리 개수)의 막대기를 그릴 거니까 비율을 1/4=0.25로 잡으면 되지요. 아래 그림을 보시면 이해될 거예요.

 

3) 메인 카테고리가 표시될 좌표 계산

 

이제 메인 카테고리가 x축에 표시될 위치를 구해야 돼요. 이를 위해서는 다음의 3가지를 알아야 해요.


첫 번째 막대기의 x 좌표

막대기 폭

서브 카테고리 개수


다음은 메인 카테고리의 눈금 위치를 계산하는 방법을 그림으로 나타낸 거예요.

 

위 그림에서 첫 번째 막대기 x좌표는 막대기 가로의 중심과 같다는 것 오른쪽으로 가는 횟수가 3이고 이 숫자는 서브 카테고리 개수인 4에서 1을 뺀 값과 같다는 것을 주목하세요. 따라서 메인 카테고리 눈금의 위치가 다음과 같이 계산되는 것을 알 수 있어요.


메인 카테고리 눈금 위치 = 첫 번째 막대기 x좌표 + 막대기 폭 X (서브 카테고리 개수 - 1) / 2  ----- (★)


4) 서브 카테고리에 대응하는 막대기의 좌표 계산

 

메인 카테고리 하나에 대해서 4개의 막대기 위치도 위와 똑같이 다음의 3가지를 알아야 해요.


첫 번째 막대기의 x 좌표

막대기 폭

서브 카테고리 개수


다음은 4개의 막대기 위치를 계산하는 방법을 나타낸 그림이에요.

 

위 그림을 보면 막대기 위치는 다음과 같다는 것을 쉽게 알 수 있어요.


K 번째 막대기 위치 = 첫 번째 막대기 x좌표 + (K - 1) X 막대기 폭 ----- (★)


여기서 서브 카테고리 개수는 직접적인 계산에 포함되지는 않지만 마지막 막대기 위치를 정하는 데 사용됨을 주목하세요.

 

5) 여백 조정

 

그룹 바 차트를 그릴 때 생각할 수 있는 여백은 메인 카테고리 내 여백메인 카테고리 여백일 거예요. 다음은 이러한 여백의 개념을 표현한 그림입니다.

 

여백을 조정하는 이유는 이것을 하지 않았을 때 막대기가 서로 덕지덕지 붙어있게 되고, 그렇게 된다면 보기가 안 좋아지기 때문이에요. 따라서 꽁냥이는 추가적으로 여백을 조정하는 작업도 할 거예요.


   4. 그룹 바 차트 그리기

이제 핵심 원리를 알았으니 그룹 바 차트를 그려봐야겠죠. 이를 위한 함수를 꽁냥이가 만들어 보았어요.

def draw_group_barchart(df,main_category,sub_category,fig_width=10,fig_height=10, \
                        bar_type='vertical', between_bar_padding=0.85,\
                        within_bar_padding=0.8, config_bar=None):
    '''
    Description :
    그룹바 차트를 그려주는 함수다. 
    
    Arguments :
    df = 메인 카테고리와 서브 카테고리로 이루어진 데이터, pd.DataFrame 객체여야 한다.
    main_category = 메인 카테고리 변수를 나타내는 문자열
    sub_category = 서브 카테고리 변수를 모아 놓은 리스트
    fig_width = 캔버스 폭
    fig_height = 캔버스 높이
    bar_type = 'vertical' 또는 'horizontal'값을 가질 수 있으며
               'vertical'은 수직 바 차트를 'horizontal'은 수평 바 차트를 그린다.
    between_bar_padding = 메인 카테고리 간 여백 조절 0~1사이의 값을 갖는다.
    within_bar_padding = 메인 카테고리 내 여백 조절 0~1사이의 값을 갖는다. 
    config_bar = 바 차트를 꾸미기 위한 옵션. 딕셔너리 형태로 넣어줘야 한다.
    
    Return : 
    그룹바 차트 출력
    '''
    
    ## Arguments 체크
    if not isinstance(main_category,str):
        print(f'main_category인자의 타입은 {type(main_category)}가 아니고 문자열 입니다.')
        return
    if not main_category in df.columns:
        print(f'데이터가 {main_category} 칼럼을 포함하고 있지 않습니다.')
        return
    if not set(sub_category).issubset(set(df.columns)):
        print(f'{set(sub_category)-set(df.columns)}가 데이터에 없습니다.')
        return
    if isinstance(bar_type,str):
        if not bar_type in ['vertical','horizontal']:
            print(f'bar_type인자에는 "vertical"과 "horizontal"만 허용됩니다.')
            return
    else:
        print(f'bar_type인자의 타입은 {type(bar_type)}가 아니고 문자열 입니다.')
        return
    
    if between_bar_padding < 0 or between_bar_padding > 1:
        print(f'between_bar_padding은 0보다 크거나 같고, 1보다 작거나 같아야합니다.')
        return
    if within_bar_padding < 0 or within_bar_padding > 1:
        print(f'within_bar_padding은 0보다 크거나 같고, 1보다 작거나 같아야합니다.')
        return
    
    ## 필요 모듈 임포트
    import matplotlib.pyplot as plt
    import seaborn as sns
    import numpy as np
    %matplotlib inline 

    num_sub_category = len(sub_category) ## 서브 카테고리 개수

    fig = plt.figure(figsize=(fig_width,fig_height)) ## 캔버스 생성
    fig.set_facecolor('white') ## 캔버스 색상 지정
    ax = fig.add_subplot() ## 그림이 그려질 축을 생성
    
    colors = sns.color_palette('hls',num_sub_category) ## 막대기 색상 지정
    
    tick_label = list(df[main_category].unique()) ## 메인 카테고리 라벨 생성
    tick_number = len(tick_label) ## 메인 카테고리 눈금 개수
    
    tick_coord = np.arange(tick_number) ## 메인 카테고리안에서 첫번째 서브 카테고리 막대기가 그려지는 x좌표

    width = 1/num_sub_category*between_bar_padding ## 막대기 폭 지정

    config_tick = dict()
    config_tick['ticks'] = [t + width*(num_sub_category-1)/2 for t in tick_coord] ## 메인 카테고리 라벨 x좌표
    config_tick['labels'] = tick_label 

    if bar_type == 'vertical': ## 수직 바 차트를 그린다.
        plt.xticks(**config_tick) ## x축 눈금 라벨 생성

        for i in range(num_sub_category):
            if config_bar: ## 바 차트 추가 옵션이 있는 경우
                ax.bar(tick_coord+width*i, df[sub_category[i]], \
                       width*within_bar_padding, label=sub_category[i], \
                       color=colors[i], **config_bar) ## 수직 바 차트 생성
            else:
                ax.bar(tick_coord+width*i, df[sub_category[i]], \
                       width*within_bar_padding, label=sub_category[i], \
                       color=colors[i]) ## 수직 바 차트 생성
        plt.legend() ## 범례 생성
        plt.savefig('fig03.png',format='png',dpi=300)
        plt.show()
    else: ## 수평 바 차트를 그린다.
        plt.yticks(**config_tick) ## x축 눈금 라벨 생성

        for i in range(num_sub_category):
            if config_bar: # 바 차트 추가 옵션이 있는 경우
                ax.barh(tick_coord+width*i, df[sub_category[i]], \
                       width*within_bar_padding, label=sub_category[i], \
                        color=colors[i], **config_bar) ## 수평 바 차트 생성
            else:
                ax.barh(tick_coord+width*i, df[sub_category[i]], \
                       width*within_bar_padding, label=sub_category[i], \
                       color=colors[i]) ## 수평 바 차트 생성
        plt.legend() ## 범례 생성
        plt.show()

위의 핵심 전략에 맞추어 이 함수를 알아볼게요. 여기서 설명하지 않는 부분은 주석을 참고하세요.

 

1) 메인 카테고리와 서브 카테고리를 지정

 

line 1

메인 카테고리는 main_category, 서브 카테고리는 sub_category에 지정합니다.

 

2) 막대기 폭 계산

 

line 68

막대기의 폭을 정합니다. 위에서 말한 것처럼  한 개의 x축 눈금 안에 서브 카테고리 개수만큼 나눠줘야 하므로 막대기 폭은 1/ 서브 카테고리 개수가 됩니다(between_bar_padding인자는 일단 넘어가 주세요).

 

3) 메인 카테고리가 표시될 좌표 계산

 

line 71

메인 카테고리가 표시될 x좌표를 계산합니다. 3)에서 (★)로 표시해둔 계산식과 비교해보세요.

 

4) 서브 카테고리에 대응하는 막대기 좌표 계산

 

line 79

ax.bar에서 "tick_coord+width*i"부분을 주목하세요. 이 부분이 서브 카테고리에 대응하는 막대기 좌표를 계산해주는 곳입니다. 4)에서 ()로 표시해둔 계산식과 비교해보세요.

 

5) 여백 조정

 

line 68

기존 막대기 폭에 between_bar_padding만큼 곱해주면 메인 카테고리 간 여백을 조정할 수 있어요.

 

line 80

기존 막대기 폭에 within_bar_padding만큼 곱해주면 메인 카테고리 내 여백을 조정할 수 있어요.

 

수평 바 차트의 경우, 수직 바 차트를 생성하는 방법과 매우 유사합니다. 차이점은 메인 카테고리의 눈금이 y축에 표시된다는 점과 bar 메서드 대신 barh 메서드를 사용하는 것입니다.

 

자, 그럼 아래의 코드를 실행해볼까요?

main_category = 'Quarter' ## 메인 카테고리 지정
sub_category = ['A','B','C','D'] ## 서브카테고리 지정

draw_group_barchart(df,main_category,sub_category,\
                        bar_type='vertical', between_bar_padding=0.85,\
                        within_bar_padding=0.9, config_bar = None)

실행 결과

와우~~ 아주 잘 나온 것을 확인할 수 있어요. 이번엔 수평 바 차트도 그려볼게요. draw_group_barchart에서 bar_type인자에 'horizontal'을 넣으면 됩니다. 아래 코드를 실행하세요.

 

main_category = 'Quarter' ## 메인 카테고리 지정
sub_category = ['A','B','C','D'] ## 서브카테고리 지정

draw_group_barchart(df,main_category,sub_category,\
                        bar_type='horizontal', between_bar_padding=0.85,\
                        within_bar_padding=0.9, config_bar = None)

실행 결과

역시 결과가 잘 나온 것을 확인할 수 있어요. 마지막으로 바 차트에 옵션을 넣어볼게요. 테두리와 투명도를 넣어 보겠습니다. config_bar 딕셔너리를 아래 코드에서 line 4~6과 같이 설정해주세요. 이제 아래 코드를 실행해볼게요.

main_category = 'Quarter' ## 메인 카테고리 지정
sub_category = ['A','B','C','D'] ## 서브카테고리 지정

config_bar = dict()
config_bar['edgecolor'] = 'black' ## 테두리 색
config_bar['alpha'] = 0.7 ## 투명도

draw_group_barchart(df,main_category,sub_category,\
                        bar_type='vertical', between_bar_padding=0.85,\
                        within_bar_padding=0.9, config_bar = config_bar)

훨씬 더 예쁜 그룹 바 차트가 생성됐네요 >0< 짝짝!!

 

이번 포스팅에서는 그룹 바 차트를 그리는 법에 대해서 알아보았고요. 여러분에게 도움이 됐으면 좋겠습니다. 다음 포스팅에서는 스택 바 차트(Stacked bar chart)를 그리는 방법에 대해서 소개하겠습니다. 기대해주세요~^^

 

궁금하신 내용이나 잘못된 내용, 그 밖에 하고 싶은 말은 댓글로 남겨주세요~!!

 

지금까지 '꽁냥이의 글' 읽어 주셔서 감사합니다.

 

 

이번 포스팅에서는 아래의 자료를 참고하였습니다.

 

Grouped Barplot - The Python Graph Gallery : https://python-graph-gallery.com/11-grouped-barplot/

 

#11 Grouped barplot

        A grouped barplot is used when you have several groups, and subgroups into these groups. Here is a method to make them using the matplotlib library. Note that you can ea…

python-graph-gallery.com


댓글


맨 위로