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

[도넛 차트(Donut chart)] 1. Matplotlib을 이용한 도넛 차트 그리기

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

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

 

카테고리별 또는 그룹별로 집계를 하고 이를 백분율로 표시할 때 도넛 차트를 많이 활용합니다. 도넛 차트는 파이 차트에서 중간에 구멍이 난 그래프예요. 마치 도넛처럼요.

 

이번 포스팅에서는 Matplotlib을 이용하여 도넛 차트(Donut chart)를 그리는 방법에 대해서 알아보려고 해요.


도넛 차트는 이전 포스팅에서 파이 차트를 그릴 때 사용했던 pie 함수를 사용하고요. 추가적으로 wedgeprops라는 인자를 사용해야 합니다.

 

도넛 차트를 만들기 위해서 wedgeprops인자에 아래와 같이 width 키와 이에 해당하는 값으로 이루어진 딕셔너리를 넣어줘야 합니다. 여기서 width는 도넛 중앙의 반지름 길이라고 생각하시면 됩니다.


wedgeprops = { 'width' : 도넛 중앙의 반지름 길이 0~1 값}


 

아래의 코드를 살펴보겠습니다.

 

import matplotlib.pyplot as plt
import numpy as np

## 데이터 준비
labels = ['Apple','Banana','Grape','Pear','Peach'] ## 라벨
frequency = [120,120,380,240,200] ## 빈도

fig = plt.figure(figsize=(8,8)) ## 캔버스 생성
fig.set_facecolor('white') ## 캔버스 배경색을 하얀색으로 설정
ax = fig.add_subplot() ## 프레임 생성

pie = ax.pie(frequency, ## 파이차트 출력
       startangle=90, ## 시작점을 90도(degree)로 지정
       counterclock=False, ## 시계 방향으로 그린다.
       autopct=lambda p : '{:.2f}%'.format(p), ## 퍼센티지 출력
       wedgeprops=dict(width=0.5) ## 중간의 반지름 0.5만큼 구멍을 뚫어준다.
       )

plt.legend(pie[0],labels) ## 범례 표시
plt.show()

 

line 16

도넛 중앙에 전체 반지름의 절반 사이즈의 구멍을 만들기 위해서 width값을 0.5로 가지는 딕셔너리를 만들고요. 이를 wedgeprops인자에 넣었습니다.

 

여기서 설명하지 않는 부분은 주석이나 이전 포스팅을 참고하세요~

 

[파이 차트(Pie chart)] 1. Matplotlib을 이용하여 파이 차트 그리기

[파이 차트(Pie chart)] 2. Matplotlib을 이용하여 파이차트 꾸미기 - 파이 차트 시작점 지정하기

[파이 차트(Pie chart)] 3. Matplotlib을 이용하여 파이차트 꾸미기 - 시계 방향, 반시계 방향으로 그리기

[파이 차트(Pie chart)] 5. Matplotlib을 이용하여 파이차트 꾸미기 - 범례 표시

 

위 코드를 실행해볼까요?

 

 

보시는 바와 같이 도넛 차트가 그려진 것을 알 수 있습니다. 

 

하지만 뭔가 찝찝하다는 생각이 들 거예요. 바로 백분율 라벨의 위치가 중앙으로 쏠려 있기 때문이지요. 따라서 백분율 라벨의 위치를 수정할 필요가 있습니다.

 

백분율의 라벨을 조정하기 위해선 다른 간단한 방법이 없고 직접 좌표를 설정해서 텍스트를 입력하는 수밖에 없습니다(혹시 꽁냥이의 방법 말고 다른 더 간단한 방법을 알고 계신 분은 알려주세요).

 

먼저 아래 그림을 살펴보세요.

 

 

꽁냥이가 라벨을 표시할 텍스트의 위치는 파이 조각의 정중앙이에요. 따라서 파이 조각 정중앙의 x좌표와  y좌표를 구할 거예요. 그러기 위해서는 원점과 정중앙 사이의 거리를 알아야 하고요. 원점과 정중앙을 잇는 직선과 x축 사이의 각을 알아야 해요. 

 

먼저 원점과 정중앙 사이의 거리는 안쪽 반지름과 바깥쪽 반지름을 더한 다음 나누기 2를 해주면 구할 수 있고요.

 

그리고 원점과 정중앙을 잇는 직선과 x축 사이의 각도는 각 1, 각 2를 더해주고 나누기 2를 해주면 구할 수 있다는 것을 알 수 있어요.

 

그렇다면 정중앙의 위치, 즉 텍스트가 위치할 x좌표와 y좌표는 아래와 같다는 것을 알 수 있어요.


정중앙 x좌표 = (원점과 정중앙 사이의 거리) X cos{ (각1 + 각2) / 2 }

정중앙 y좌표 = (원점과 정중앙 사이의 거리) X sin{ (각1 + 각2) / 2 }


원리를 알았으니 코드로 구현해보겠습니다. 아래 코드를 살펴볼게요.

 

import matplotlib.pyplot as plt
import numpy as np

## 데이터 준비
labels = ['Apple','Banana','Grape','Pear','Peach'] ## 라벨
frequency = [120,120,380,240,200] ## 빈도

fig = plt.figure(figsize=(8,8)) ## 캔버스 생성
fig.set_facecolor('white') ## 캔버스 배경색을 하얀색으로 설정
ax = fig.add_subplot() ## 프레임 생성

pie = ax.pie(frequency, ## 파이차트 출력
       startangle=90, ## 시작점을 90도(degree)로 지정
       counterclock=False, ## 시계 방향으로 그린다.
       wedgeprops=dict(width=0.5) ## 중간의 반지름 0.5만큼 구멍을 뚫어준다.
       )

total = np.sum(frequency) ## 빈도수 총합

sum_pct = 0 ## 백분율 초기값
for i,l in enumerate(labels):
    ang1, ang2 = pie[0][i].theta1, pie[0][i].theta2 ## 각1, 각2
    r = pie[0][i].r ## 원의 반지름
    
    x = ((r+0.5)/2)*np.cos(np.pi/180*((ang1+ang2)/2)) ## 정중앙 x좌표
    y = ((r+0.5)/2)*np.sin(np.pi/180*((ang1+ang2)/2)) ## 정중앙 y좌표
    
    if i < len(labels) - 1:
        sum_pct += float(f'{frequency[i]/total*100:.2f}') ## 백분율을 누적한다.
        ax.text(x,y,f'{frequency[i]/total*100:.2f}%',ha='center',va='center') ## 백분율 텍스트 표시
    else: ## 총합을 100으로 맞추기위해 마지막 백분율은 100에서 백분율 누적값을 빼준다.
        ax.text(x,y,f'{100-sum_pct:.2f}%',ha='center',va='center') 

plt.legend(pie[0],labels) ## 범례 표시
plt.show()

 

여기서는 정중앙의 x좌표와 y좌표를 계산하는 직접적인 부분을 위주로 코드를 설명합니다. 여기서 설명하지 않는 부분은 주석을 참고해주세요.

 

line 12~16

앞서 봤던 코드와 동일하지만 백분율 라벨을 따로 표시해줘야 하기 때문에 기존 autopct 인자는 삭제하였습니다. 그리고 pie라는 변수에 ax.pie함수의 출력 결과를 담았습니다. 여기에는 matplotlib.patches.Wedge 객체가 담긴 리스트와 Text 객체가 담겨진 리스트가 있습니다.

 

matplotlib.patches.Wedge 객체와 Text 객체에 대해서 궁금하신 분들은 아래 사이트를 참고하세요.

 

matplotlib.patches.Wedge :

 

matplotlib.patches.Wedge — Matplotlib 3.3.1 documentation

hatch {'/', '\', '|', '-', '+', 'x', 'o', 'O', '.', '*'}

matplotlib.org

Text : 

 

matplotlib.text — Matplotlib 3.3.1 documentation

Parameters: textstrThe text of the annotation. s is a deprecated synonym for this parameter. xy(float, float)The point (x, y) to annotate. The coordinate system is determined by xycoords. xytext(float, float), default: xyThe position (x, y) to place the te

matplotlib.org

 

line 22~23

pie[0] 에는 matplotlib.patches.Wedge 객체가 담겨 있는데요. 이 객체에서 제공하는 theta1, theta2 필드를 이용하여 각 1, 각 2를 구할 수 있습니다(line 22). 또한 r 필드를 이용하여 반지름의 길이를 구할 수 있습니다(line 23).

 

line 25~26

꽁냥이가 구하고자 하는 정중앙의 x좌표와 y좌표를 계산해줍니다.

 

자 그럼 위 코드를 실행해볼까요?

 

 

보시는 바와 같이 라벨의 위치가 파이 조각 정중앙에 위치한 것을 알 수 있습니다. 


이번 포스팅에서는 Matplotlib을 이용하여 도넛 차트를 그리는 방법에 대해서 알아보았습니다. 다음 포스팅에서는 하위 그룹을 포함하는 도넛 차트(Nested donut chart)를 그리는 방법에 대해서 알아보겠습니다.

 

궁금한 점, 잘못된 점, 하고 싶은 말은 댓글로 남겨주세요.

 

지금까지 꽁냥이의 글 읽어주셔서 감사합니다. 안녕히 계세요~~!!


댓글


맨 위로