파이썬으로 구현하는 인구 피라미드 그래프: 2023년 연령별 사망자 수 데이터 분석

인구 통계 데이터를 시각화할 때 데이터 레이블을 추가하면 정보의 정확성과 가독성이 크게 향상됩니다. 오늘은 인구 피라미드 그래프에 데이터 레이블을 포함하여 2023년 연령별 사망자 수를 시각화하는 방법을 알아보겠습니다.
데이터 개요
위 파이썬 시각화 그래프의 기초 데이터는 ’24년 2월에 통계청에서 발표한 2023년 출생·사망통계(잠정)에 근거하고 있습니다. 해당 데이터를 표형태로 정리해보면 아래와 같습니다. 사망자 수 단위는 천명입니다.
| 연령 그룹 | 남성 사망자 수 | 여성 사망자 수 |
|---|---|---|
| 90+ | 15.3 | 42.1 |
| 80-89 | 63.5 | 69.6 |
| 70-79 | 45.5 | 24.6 |
| 60-69 | 34.3 | 13.2 |
| 50-59 | 18.0 | 7.2 |
| 40-49 | 7.0 | 3.8 |
| 30-39 | 2.8 | 1.5 |
| 20-29 | 1.7 | 0.9 |
| 10-19 | 0.4 | 0.4 |
| 1-9 | 0.2 | 0.1 |
| 0- | 0.3 | 0.3 |

위 테이블에서 나타난 2023년 연령별 사망자 수 데이터를 인구 피라미드 그래프로 표현하면서, 각 연령대별 정확한 수치를 데이터 레이블로 표시하여 더욱 상세한 정보를 제공하겠습니다.
파이썬 시각화 코드 구현
import numpy as np
import matplotlib.pyplot as plt
# 데이터 준비
age_groups = ['90+', '80-89', '70-79', '60-69', '50-59', '40-49',
'30-39', '20-29', '10-19', '1-9', '0-']
male_deaths = [15.3, 63.5, 45.5, 34.3, 18.0, 7.0, 2.8, 1.7, 0.4, 0.2, 0.3]
female_deaths = [42.1, 69.6, 24.6, 13.2, 7.2, 3.8, 1.5, 0.9, 0.4, 0.1, 0.3]
# GridSpec을 사용하여 그림 생성
fig = plt.figure(figsize=(15, 8))
gs = fig.add_gridspec(1, 3, width_ratios=[4, 0.01, 4]) # 중앙 열의 비율을 0.01로 설정
# 세 개의 서브플롯 생성
ax_left = fig.add_subplot(gs[0])
ax_center = fig.add_subplot(gs[1])
ax_right = fig.add_subplot(gs[2])
y_pos = np.arange(len(age_groups))
# 왼쪽 플롯 (남성)
male_bars = ax_left.barh(y_pos, -np.array(male_deaths), align='center',
color='#5AB1EF', height=0.7)
# 오른쪽 플롯 (여성)
female_bars = ax_right.barh(y_pos, female_deaths, align='center',
color='#FFB848', height=0.7)
# 데이터 레이블 추가
def add_labels(ax, bars):
for bar in bars:
width = bar.get_width()
x = width
value = abs(width)
ax.text(x, bar.get_y() + bar.get_height()/2, f'{value}',
ha='right' if width < 0 else 'left',
va='center',
fontsize=9,
fontweight='bold',
color='black',
bbox=dict(pad=0.4, facecolor='none', edgecolor='none'))
add_labels(ax_left, male_bars)
add_labels(ax_right, female_bars)
# 중앙 플롯 (연령 그룹)
ax_center.set_yticks(y_pos)
ax_center.tick_params(axis='y', length=0)
# 연령 레이블 가운데 정렬
ax_center.set_yticklabels(age_groups, ha='center')
# 중앙 열의 스타일 설정
ax_center.set_xlim(-0.5, 0.5) # x축 범위를 줄여서 레이블이 더 가운데에 오도록 조정
ax_center.set_xticks([])
for spine in ax_center.spines.values():
spine.set_visible(False)
# 왼쪽과 오른쪽 플롯의 스타일 설정
for ax in [ax_left, ax_right]:
ax.grid(axis='x', linestyle='-', alpha=0.1)
ax.set_yticks([])
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# x축 범위 설정
max_value = max(max(male_deaths), max(female_deaths))
ax_left.set_xlim(-max_value*1.1, 0)
ax_right.set_xlim(0, max_value*1.1)
# 축 레이블 추가
ax_left.set_xlabel('Number of Deaths (thousands)', ha='right')
ax_right.set_xlabel('Number of Deaths (thousands)', ha='left')
# 남성/여성 레이블 추가
ax_left.text(-max_value/2, len(age_groups), 'Male', ha='center', va='bottom', fontsize=12)
ax_right.text(max_value/2, len(age_groups), 'Female', ha='center', va='bottom', fontsize=12)
# x축 눈금 포맷 설정
def format_ticks(x, p):
return f'{abs(x)}'
ax_left.xaxis.set_major_formatter(plt.FuncFormatter(format_ticks))
ax_right.xaxis.set_major_formatter(plt.FuncFormatter(format_ticks))
# 그래프 제목 추가
fig.suptitle('Age-Specific Mortality by Gender', y=0.95, fontsize=14)
plt.tight_layout()
plt.subplots_adjust(top=0.9) # 제목을 위한 여백 조정
plt.show()심층 데이터 분석 인사이트
1. 연령대별 사망률 격차
- 80-89세 구간: 남성(63.5천 명), 여성(69.6천 명)으로 최고점
- 70대 이하에서는 전반적으로 남성 사망률이 높음
- 연령 증가에 따른 성별 격차 패턴 변화 관찰
2. 생애주기별 특징
- 영유아기(0세): 남녀 동일한 사망률(0.3천 명)
- 청장년층(20-49세): 남성 사망률이 여성의 약 2배
- 노년층(80세 이상): 여성 사망률 급증
3. 정책적 시사점
- 성별·연령별 맞춤형 보건의료 정책 필요성
- 고령층 여성 대상 의료서비스 확충 요구
- 중년 남성 사망률 감소를 위한 예방의료 강화

마무리
데이터 레이블이 포함된 인구 피라미드 그래프는 연령별 사망자 수의 정확한 수치를 직관적으로 파악할 수 있게 해줍니다. 이러한 시각화는 인구통계 분석과 보건의료 정책 수립에 중요한 기초자료로 활용될 수 있습니다. 코드 상세 해설은 아래의 내용을 참고해주세요.
혹시, 사망자 수와 대척점에 있는 개념인 합계출산율에 대한 정보를 알고 싶으신 분은 합계출산율 뜻과 계산법: R로 트렌디한 시각화까지 포스트를 참고해 보시기 바랍니다.
# 코드 상세해설
1. 필요한 라이브러리 임포트
import numpy as np import matplotlib.pyplot as plt
numpy: 수치 계산을 위한 라이브러리matplotlib.pyplot: 그래프 생성을 위한 라이브러리2. 데이터 준비
age_groups = ['90+', '80-89', '70-79', '60-69', '50-59', '40-49', '30-39', '20-29', '10-19', '1-9', '0-'] male_deaths = [15.3, 63.5, 45.5, 34.3, 18.0, 7.0, 2.8, 1.7, 0.4, 0.2, 0.3] female_deaths = [42.1, 69.6, 24.6, 13.2, 7.2, 3.8, 1.5, 0.9, 0.4, 0.1, 0.3]연령 그룹과 각 성별의 사망자 수 데이터를 리스트로 정의합니다.
3. 그래프 구조 설정
fig = plt.figure(figsize=(15, 8)) gs = fig.add_gridspec(1, 3, width_ratios=[4, 0.01, 4])
figsize=(15, 8): 그래프의 전체 크기를 설정 (가로 15, 세로 8)add_gridspec: 그래프를 3개의 열로 나눔. 비율은 [4, 0.01, 4]로 설정하여 중앙 열을 매우 좁게 만듦4. 서브플롯 생성
ax_left = fig.add_subplot(gs[0]) ax_center = fig.add_subplot(gs[1]) ax_right = fig.add_subplot(gs[2])3개의 서브플롯(왼쪽, 중앙, 오른쪽)을 생성합니다.
5. 막대 그래프 그리기
y_pos = np.arange(len(age_groups)) male_bars = ax_left.barh(y_pos, -np.array(male_deaths), align='center', color='#5AB1EF', height=0.7) female_bars = ax_right.barh(y_pos, female_deaths, align='center', color='#FFB848', height=0.7)
barh: 수평 막대 그래프를 그리는 함수- 남성 데이터는 음수로 변환하여 왼쪽에 표시
- 여성 데이터는 오른쪽에 표시
6. 데이터 레이블 추가
def add_labels(ax, bars): for bar in bars: width = bar.get_width() x = width value = abs(width) ax.text(x, bar.get_y() + bar.get_height()/2, f'{value}', ha='right' if width < 0 else 'left', va='center', fontsize=9, fontweight='bold', color='black', bbox=dict(pad=0.4, facecolor='none', edgecolor='none')) add_labels(ax_left, male_bars) add_labels(ax_right, female_bars)각 막대에 해당하는 수치를 레이블로 추가합니다.
7. 중앙 열 스타일 설정
ax_center.set_yticks(y_pos) ax_center.tick_params(axis='y', length=0) ax_center.set_yticklabels(age_groups, ha='center') ax_center.set_xlim(-0.5, 0.5) ax_center.set_xticks([]) for spine in ax_center.spines.values(): spine.set_visible(False)중앙 열에 연령 그룹 레이블을 표시하고, 불필요한 축과 테두리를 제거합니다.
8. 왼쪽과 오른쪽 플롯의 스타일 설정
for ax in [ax_left, ax_right]: ax.grid(axis='x', linestyle='-', alpha=0.1) ax.set_yticks([]) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False)왼쪽과 오른쪽 플롯에 격자를 추가하고, 불필요한 축을 제거합니다.
9. x축 범위 설정 및 레이블 추가
max_value = max(max(male_deaths), max(female_deaths)) ax_left.set_xlim(-max_value*1.1, 0) ax_right.set_xlim(0, max_value*1.1) ax_left.set_xlabel('Number of Deaths (thousands)', ha='right') ax_right.set_xlabel('Number of Deaths (thousands)', ha='left') ax_left.text(-max_value/2, len(age_groups), 'Male', ha='center', va='bottom', fontsize=12) ax_right.text(max_value/2, len(age_groups), 'Female', ha='center', va='bottom', fontsize=12)x축의 범위를 설정하고, 축 레이블과 성별 레이블을 추가합니다.
10. x축 눈금 포맷 설정
def format_ticks(x, p): return f'{abs(x)}' ax_left.xaxis.set_major_formatter(plt.FuncFormatter(format_ticks)) ax_right.xaxis.set_major_formatter(plt.FuncFormatter(format_ticks))x축 눈금의 값을 절대값으로 표시합니다.
11. 그래프 마무리
fig.suptitle('Age-Specific Mortality by Gender', y=0.95, fontsize=14) plt.tight_layout() plt.subplots_adjust(top=0.9) plt.show()그래프에 제목을 추가하고, 레이아웃을 조정한 후 그래프를 표시합니다.






