見出し画像

Python データの可視化 人口動態予測の巻

今回は世界の人口動態のオープンデータを使用して、各国の人口ピラミッドグラフを作成する。人口動態予測のデータにはかなりたくさんの種類があるが、今回はmedian variationを使用した。データは2022年~2100年で、比較データや推移のgif画像を作成して、人口推移が直観的に分かりやすいグラフを目指した。

アウトプットデータ

プログラムは各国すべてのデータをアウトプットするが、ここでは日本のグラフを参考に貼る。

まずは2022年の人口グラフ。

次に2022年と未来の比較。薄色が2022年、濃色が未来。

最後に2022年から2100年までの推移のアニメーション。

データの出典

Department of Economic and Social Affairs Population Division World Populaion Prospects 2022 のデータを使用した。

URL : https://population.un.org/wpp/

モジュールのimport

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
from PIL import Image
#画像大量作成するときにメモリ消費が激しくなるため、以下をおまじないとして入れておく
import matplotlib
matplotlib.interactive(False)

データを読み込む。性別ごとのリストがダウンロードできる。

#index_col:最初の行を列名にする、usecols:使用する行を指定、skiprows:スキップする行を指定、skipfooter:使用する最後の行を指定
header = 16
sheet_name = 'Medium variant'
df_male = pd.read_excel('https://population.un.org/wpp/Download/Files/1_Indicators%20(Standard)/EXCEL_FILES/2_Population/WPP2022_POP_F02_2_POPULATION_5-YEAR_AGE_GROUPS_MALE.xlsx', header=header, sheet_name=sheet_name)
df_female = pd.read_excel('https://population.un.org/wpp/Download/Files/1_Indicators%20(Standard)/EXCEL_FILES/2_Population/WPP2022_POP_F02_3_POPULATION_5-YEAR_AGE_GROUPS_FEMALE.xlsx', header=header, sheet_name=sheet_name)

国名、地域名を抽出する。

country_list = df_male[df_male['Type'].str.contains('Country')]['Region, subregion, country or area *'].unique().tolist()

各国の各年の人口ピラミッド作成

2022年~2100年の年ごとの人口動態予測をすべての国に対して作成する。画像数が多く実行にも時間がかかるので注意。

for country in country_list:
    df_male_country = df_male[df_male['Region, subregion, country or area *'] == country]
    df_female_country = df_female[df_female['Region, subregion, country or area *'] == country]
    os.mkdir(country)

    # グラフの軸サイズ用のデータ取得
    if df_male_country.max()[11:].max()<df_female_country.max()[11:].max():
        graph_max = df_female_country.max()[11:].max()
    else:
        graph_max = df_male_country.max()[11:].max()

    for i in range(len(df_male_country)):
        df_male_country_period= df_male_country.iloc[i].drop(df_male_country.iloc[i].index[range(11)])
        df_female_country_period= df_female_country.iloc[i].drop(df_female_country.iloc[i].index[range(11)])
        fig, ax = plt.subplots(ncols=2, figsize=(12,8))

        # グラフ作成
        # 男性人口
        ax[0].barh(df_male_country_period.index, df_male_country_period, color='b', height=0.7, label='male', alpha = 1, edgecolor = "#000000", linewidth = 1)
        ax[0].yaxis.tick_right() # 軸を右に
        ax[0].set_yticks(np.array(range(0,21,1))) # 10歳刻み
        ax[0].set_yticklabels([]) # こちらの軸ラベルは非表示
        ax[0].set_xlim([int(graph_max+graph_max/100),0])# x軸反転
        ax[0].set_title('MALE',fontsize=18)
        ax[0].text( 0.01, 0.99, "MALE total : " + str(int(df_male_country_period.sum())) + " thousand people" ,horizontalalignment='left', verticalalignment='top', family='MS Gothic', transform=ax[0].transAxes)
        ax[0].xaxis.grid(True, which = 'major', linestyle = '--', color = '#CFCFCF')
        plt.setp(ax[0].get_xticklabels(), fontsize=14)

        # 女性人口
        ax[1].barh(df_female_country_period.index, df_female_country_period, color='r', height=0.7, label='female', alpha = 1, edgecolor = "#000000", linewidth = 1)
        ax[1].set_yticks(np.array(range(0,21,1)))
        ax[1].set_xlim([0,int(graph_max+graph_max/100)])
        ax[1].set_title('FEMALE',fontsize=18)
        ax[1].xaxis.grid(True, which = 'major', linestyle = '--', color = '#CFCFCF')
        ax[1].text( 0.99, 0.99, "FEMALE total : " + str(int(df_female_country_period.sum())) + " thousand people" ,horizontalalignment='right', verticalalignment='top', family='MS Gothic', transform=ax[1].transAxes)

        fig.suptitle(country + " in " + str(int(period_list[i])) + " " + "  Population by five-year age group [thousands]" , size=18)
        plt.yticks(fontsize=14)
        plt.setp(ax[1].get_xticklabels(), fontsize=14)
        
        plt.savefig(country + "/" + str(int(period_list[i])) + "_" + country + "_" + 'graph.png')
        plt.close(fig)

各国の現在と未来のグラフ比較

例として2030年の人口と現在2022年の人口を比較。2022年の人口を背景に薄色で表示して、その上に2030年の人口を濃色で重ねる。

for country in country_list:
    df_male_country = df_male[df_male['Region, subregion, country or area *'] == country]
    df_female_country = df_female[df_female['Region, subregion, country or area *'] == country]

    # グラフの軸サイズ用のデータ取得
    if df_male_country.max()[11:].max()<df_female_country.max()[11:].max():
        graph_max = df_female_country.max()[11:].max()
    else:
        graph_max = df_male_country.max()[11:].max()

    # 2022年と2030年のデータを抽出。8を変更すれば他の年との比較も可能。
    df_male_country_period1= df_male_country.iloc[0].drop(df_male_country.iloc[0].index[range(11)])
    df_female_country_period1= df_female_country.iloc[0].drop(df_female_country.iloc[0].index[range(11)])
    df_male_country_period2= df_male_country.iloc[8].drop(df_male_country.iloc[8].index[range(11)])
    df_female_country_period2= df_female_country.iloc[8].drop(df_female_country.iloc[8].index[range(11)])
    fig, ax = plt.subplots(ncols=2, figsize=(12,8))

    # グラフ作成
    # 男性人口
    ax[0].barh(df_male_country_period1.index, df_male_country_period1, color='b', height=0.7, label='male', alpha = 0.3, edgecolor = "#000000", linewidth = 5)
    ax[0].barh(df_male_country_period2.index, df_male_country_period2, color='b', height=0.7, label='male', alpha = 1, edgecolor = "#000000", linewidth = 1)
    ax[0].yaxis.tick_right() # 軸を右に
    ax[0].set_yticks(np.array(range(0,21,1))) # 10歳刻み
    ax[0].set_yticklabels([]) # こちらの軸ラベルは非表示
    ax[0].set_xlim([int(graph_max+graph_max/100),0])# x軸反転
    ax[0].set_title('MALE',fontsize=18)
    ax[0].xaxis.grid(True, which = 'major', linestyle = '--', color = '#CFCFCF')
    ax[0].text( 0.01, 0.99, "MALE total in 2022 : " + str(int(df_male_country_period1.sum())) + " thousand people" ,horizontalalignment='left', verticalalignment='top', family='MS Gothic', transform=ax[0].transAxes)
    ax[0].text( 0.01, 0.95, "MALE total in 2030 : " + str(int(df_male_country_period2.sum())) + " thousand people" ,horizontalalignment='left', verticalalignment='top', family='MS Gothic', transform=ax[0].transAxes)
    
    plt.setp(ax[0].get_xticklabels(), fontsize=14)

    # 女性人口
    ax[1].barh(df_female_country_period1.index, df_female_country_period1, color='r', height=0.7, label='male', alpha = 0.3, edgecolor = "#000000", linewidth = 5)
    ax[1].barh(df_female_country_period2.index, df_female_country_period2, color='r', height=0.7, label='male', alpha = 1, edgecolor = "#000000", linewidth = 1)
    ax[1].set_yticks(np.array(range(0,21,1)))
    ax[1].set_xlim([0,int(graph_max+graph_max/100)])
    ax[1].set_title('FEMALE',fontsize=18)
    ax[1].xaxis.grid(True, which = 'major', linestyle = '--', color = '#CFCFCF')
    ax[1].text( 0.99, 0.99, "FEMALE total in 2022 : " + str(int(df_female_country_period1.sum())) + " thousand people" ,horizontalalignment='right', verticalalignment='top', family='MS Gothic', transform=ax[1].transAxes)
    ax[1].text( 0.99, 0.95, "FEMALE total in 2030 : " + str(int(df_female_country_period2.sum())) + " thousand people" ,horizontalalignment='right', verticalalignment='top', family='MS Gothic', transform=ax[1].transAxes)

    fig.suptitle(country + " Comparison of population pyramids in 2022 and 2030 [thousands]" , size=18)
    plt.yticks(fontsize=14)
    plt.setp(ax[1].get_xticklabels(), fontsize=14)

    plt.savefig(country + '/' + country + '_Comparison_in_2022_and_2030.png')
    plt.close(fig)

アニメーションgif作成

2022年から2100年までの人口動態予測をアニメーションにすると、どのような動き方をするのかが把握しやすくなる。

for country in country_list:
    pictures=[]

    # gifアニメ出力用の画像リストを作成
    for i in range(2022, 2101):
        pic_name = country + "/" + str(i) + "_" + country + "_" + 'graph.png'
        img = Image.open(pic_name).quantize()
        pictures.append(img)

    # gifアニメの出力
    pictures[0].save(country + "/" + str(i) + "_" + country + "_" + 'anime.gif',
                     save_all=True,
                     append_images=pictures[1:],
                     optimize=False,
                     duration=50,
                     loop=0,
    )

いいなと思ったら応援しよう!