0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

はじめてのアドベントカレンダーAdvent Calendar 2023

Day 17

年齢別人口の男女比を都道府県ごとにクラスタリング分析してみる。

Posted at

目的

年齢別人口の男女比を都道府県ごとにクラスタリング分析してみる。

前回記事2つを踏まえて、今回の分析を実施しましたので、適宜前記事を参照ください。

国勢調査データのダウンロードと前処理

前回記事と同じ。詳細は前回記事を参照

import pandas as pd
import numpy as np
import unicodedata

def prepro_kokusei(data_path: str) -> pd.DataFrame:
    '''
    国勢調査の統計データを前処理する関数
    Params:
        data_path: str
        zip_fileのあるパス
    Returns:
        df: pd.DataFrame
        前処理済みのデータ

    '''
    df = pd.read_csv(data_path, encoding='cp932', header=[0, 1])
    front_col = [col for col in df.columns.get_level_values(0) if not col.startswith('T00')]
    back_col = [col for col in df.columns.get_level_values(1) if not col.startswith("Unnamed:")]
    new_col = front_col + back_col
    df.columns = new_col
    for col in df.loc[:, '総数、年齢「不詳」含む':].columns:
        df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0).astype(int)

    # 変更用の辞書を作成
    henko_dict = {
        '年齢「不詳」含む': 'all',
        '総数': 'total_',
        '': 'men_',
        '': 'women_',
        '': '',
        '': '',
        '': '_age',
        '未満': '_less',
        '以上': '_over',
        '': '_'
    }
    # 変更したいカラム名のリストを変数に格納
    cols = list(df.columns)

    # 変更用の辞書を反映する
    for k, v in henko_dict.items():
        cols = [col.replace(k, v) for col in cols]
    # 全角の数値を半角に変換
    cols = [unicodedata.normalize('NFKC', col) for col in cols]
    # カラム名を作成したものに置き換え
    df.columns = cols
 
    # 市区町村のみ抽出
    df = df[df['HYOSYO']==1]

    # 不要なデータを削除
    df.drop(['HYOSYO','NAME','HTKSYORI','HTKSAKI', 'GASSAN'],axis=1,inplace=True)

    return df

人口データ処理

取得できたデータフレームを処理し、男女比を計算する。

  • 年齢別男女別の人口のみが必要なため、それ以外のデータ列を削除
  • 男女別のデータフレームに分離(のちのため、総人口としてもデータフレーム化)
  • 男女差、男女比を計算
# 人口データ処理
def get_men_women_ratio(df):
  # men women のみ必要なので、それ以外は削除する
  # 削除するカラム名(menを含まないカラム名、なお、womenにmenは含まれる)
  del_columns = [ col for col in df.columns if not 'men' in col]
  # 削除するカラム名(総人口のallを含むカラム名)
  del_columns += [ col for col in df.columns if 'all' in col]
  # 削除するカラム名(5歳刻み以外のカラム名)
  del_columns += [ col for col in df.columns if '15_age_less' in col]
  del_columns += [ col for col in df.columns if '15_64_age' in col]
  del_columns += [ col for col in df.columns if '65_age_over' in col]
  # 不要なカラムを削除
  df.drop( del_columns, axis=1, inplace=True)
  # _age は不要なので削除
  cols = [col.replace('_age', '') for col in df.columns]
  df.columns = cols
  # 男女別に分離して合計
  # 男性
  del_columns = [ col for col in df.columns if 'women' in col]
  df_men = df.drop( del_columns, axis=1)
  cols = [col.replace('men_', '') for col in df_men.columns]
  df_men.columns = cols
  df_men_sum = df_men.sum(axis=0)
  # 女性
  del_columns = [ col for col in df.columns if not 'women' in col]
  df_women = df.drop( del_columns, axis=1)
  cols = [col.replace('women_', '') for col in df_women.columns]
  df_women.columns = cols
  df_women_sum = df_women.sum(axis=0)
  # 男女別人口をマージ
  df_sum = pd.DataFrame(data=[df_men_sum,df_women_sum],index=['men','women']).T
  df_sum
  # 合計、男女差、男女比を計算
  df_sum['total'] = df_sum.sum(axis=1)
  df_sum['diff'] = df_sum['men'] - df_sum['women']
  df_sum['ratio'] = df_sum['men'] / df_sum['women'] * 100
  return df_sum

データ確認

福島県と大阪府のデータを見てみる。

df_07 = prepro_kokusei('./data/tblT001082C07.zip') # 福島県
df_07 = get_men_women_ratio(df_07)
df_07
	men	women	total	diff	ratio
0_4	30690	29909	60599	781	102.611254
5_9	35789	33911	69700	1878	105.538026
10_14	38889	36964	75853	1925	105.207770
15_19	42169	39011	81180	3158	108.095153
20_24	37448	32551	69999	4897	115.044085
25_29	41082	36357	77439	4725	112.996122
30_34	45940	41989	87929	3951	109.409607
35_39	52093	48246	100339	3847	107.973718
40_44	59183	54559	113742	4624	108.475229
45_49	64456	59844	124300	4612	107.706704
50_54	57946	55647	113593	2299	104.131400
55_59	61032	59410	120442	1622	102.730180
60_64	65995	65283	131278	712	101.090636
65_69	72211	73120	145331	-909	98.756838
70_74	67159	69280	136439	-2121	96.938510
75_over	111044	180011	291055	-68967	61.687341
df_27 = prepro_kokusei('./data/tblT001082C27.zip') # 大阪府
df_27 = get_men_women_ratio(df_27)
df_27
	men	women	total	diff	ratio
0_4	162354	155060	317414	7294	104.703986
5_9	176474	169466	345940	7008	104.135343
10_14	187534	178611	366145	8923	104.995773
15_19	203718	197103	400821	6615	103.356113
20_24	231542	235653	467195	-4111	98.255486
25_29	224908	233048	457956	-8140	96.507157
30_34	230653	236371	467024	-5718	97.580922
35_39	247335	254147	501482	-6812	97.319661
40_44	280736	291626	572362	-10890	96.265765
45_49	350481	361438	711919	-10957	96.968498
50_54	311958	321721	633679	-9763	96.965383
55_59	264101	273984	538085	-9883	96.392855
60_64	218762	230219	448981	-11457	95.023434
65_69	237306	261943	499249	-24637	90.594519
70_74	284877	333855	618732	-48978	85.329559
75_over	493162	750580	1243742	-257418	65.704122

男女比(ratio)の傾向が違うようだ。
福島県は、60代まで100%以上なのに対し、大阪府は、20代で早々100%を下回る。
この要因を探るべく、全都道府県のデータを可視化して、分析してみる。

全都道府県の処理

pref_list.csv については、前記事参照。
都道府県ごとにデータを読み込み、前処理し、男女比、男女別人口をデータフレーム化する。

pref_list = pd.read_csv('./data/pref_list.csv',index_col=0)
# 都道府県ごとの年齢別人口男女比のデータフレーム
df_ratio = pd.DataFrame()
# 都道府県ごとの年齢別男女別人口のデータフレーム
df_men = pd.DataFrame()
df_women = pd.DataFrame()

# 都道府県のループ
for pref_no, row in pref_list.iterrows():
  # 都道府県名称
  pref_name = row['都道府県名']
  # ファイル名称
  filename = './data/tblT001082C' + str(pref_no).zfill(2) + '.zip'
  # 読み込みと前処理
  df = prepro_kokusei(filename)
  # 人口データ処理
  df = get_men_women_ratio(df)
  # 男女比
  df_ratio[pref_name] = df['ratio']
  # 男女別人口
  df_men[pref_name] = df['men']
  df_women[pref_name] = df['women']

print(df_ratio)
                北海道         青森県         岩手県         宮城県         秋田県  \
0_4      104.579874  103.410701  104.232978  105.498728  103.962873   
5_9      104.393506  105.360513  104.281249  105.564115  104.094406   
10_14    105.551300  103.895555  105.590726  104.717378  106.062336   
15_19    105.252171  107.265637  106.175072  105.957728  106.155330   
20_24    104.474317  110.310762  110.124739  107.047419  112.151163   
25_29    100.935962  107.391422  106.817981  102.284923  108.706159   
30_34    100.444689  103.293929  105.171951  102.384538  103.849200   
35_39     97.978531  100.938865  103.356171  100.365235  104.477612   
40_44     97.769903  101.477859  104.656374  102.261284  105.514589   
45_49     97.300952   99.062150  105.771977  102.509347  101.473243   
50_54     92.765815   95.326722  100.525141  102.097266   97.674419   
55_59     91.209515   93.466124  100.526117   99.044007   94.416626   
60_64     91.875965   90.324438   98.213912   96.773974   94.110750   
65_69     88.379142   89.508252   95.870718   94.629748   92.568437   
70_74     82.406836   85.384755   90.875134   91.975451   88.998000   
75_over   60.812486   55.569690   58.513527   64.023940   56.012671

可視化

全都道府県をまとめて可視化する。

import matplotlib.pyplot as plt
import japanize_matplotlib

fig_size_x = 12
fig_size_y = 5
fig = plt.figure(figsize=(fig_size_x, fig_size_y))
plt.plot(df_ratio)
plt.title( '年齢別人口男女比(都道府県別)')
plt.xlabel('年代')
plt.ylabel('男性/女性の比率(%)')
plt.grid(axis='y')
plt.grid(axis='x')
plt.show()

output1.png

男性/女性の比率(%)は、女性100人いた場合、男性が何人いるか、を示している、と考えやすい。
100%以上の場合は、男性が多く(女性が少ない)、100%以下の場合は、男性が少ない(女性が多い)。
10代前半までは、105%前後で、各都道府県でのばらつきが少ない。これは以下の理由であろう。

  • 遺伝的に男が生まれやすい(105%であること理由)
    • 乳幼児死亡率は男が高い(体が弱い)ため、
    • 狩りや戦争で男が死ぬ確率が高いため、
    • ⇒種を保存するための遺伝的な傾向である
  • 現代では乳幼児死亡率は下がり、男女差もない(10代前半まで変わらない理由1)
  • 10代前半までは生まれた土地(都道府県)から移動することが少ない(10代前半まで変わらない理由2)
    • 仮に親の転勤があっても単身赴任するため、同じ土地に残ることが多いのだろう。

ということが要因と考えられる。

10代後半以降は、都道府県でばらつきが多いが、全体的に、年齢を追うごとに、男性の比率が下がっていき、75歳以上となると、極端に下がり、60%~70%まで下がる。
これは、乳幼児死亡率だけでなく、20代以降にも男性の死亡率が高いのだろうか?乳幼児死亡率は現代では大きく改善したが、20代以上の男性の死亡率は改善しないのか?
20代以降の多くの男性が海外に移住する、もしくは、20代以降の外国人女性が日本に移住してくる、ということがこの数値にまで表れてくるとは考えにくい。
ましてや、(世界的に見れば戦争はなくなってはいないが)日本では戦争はないので男性が戦争で亡くなるということもないだろう。

次に、男女比率をクラスタリング分析してみる。

クラスタリング分析

クラスタリング分析については、前記事を参照。

from scipy.cluster import hierarchy
import japanize_matplotlib
fig = plt.figure(figsize=(fig_size_x, fig_size_y))
method = 'ward'
Z = hierarchy.linkage(df_ratio.T, method=method)
hierarchy.dendrogram(Z, color_threshold=0,labels=df_ratio.columns)
plt.ylabel('distance')
plt.title( '年齢別人口男女比')
plt.xticks(rotation=90)
plt.show()

output2.png

地理的な条件は全く考慮していないはずであるのに、近隣地域が、近くに配置されているのが不思議である。

例えば、左側には神奈川、埼玉、千葉、東京の首都圏が、中央右側には、奈良県、京都府、大阪府の関西圏が隣接している。

これを、当てはまりのよさそうな、5クラスタに分類してみる。

from sklearn.cluster import AgglomerativeClustering
n_clusters = 5
clustering = AgglomerativeClustering(linkage=method, n_clusters=n_clusters)
clustering.fit(df_ratio.T)
df_clustering_result = pd.DataFrame( clustering.labels_, index=df_ratio.columns, columns=['label'])
print(df_clustering_result.head())
     label
北海道      0
青森県      0
岩手県      3
宮城県      0
秋田県      3

各クラスタ毎に可視化、及び、考察

全国平均、及び、各クラスタの平均をとり、可視化する。

fig = plt.figure(figsize=(fig_size_x, fig_size_y))
#全国平均
df_men_sum = df_men.sum(axis=1)
df_women_sum = df_women.sum(axis=1)
df_total_ratio = df_men_sum / df_women_sum * 100
plt.plot(df_total_ratio,label='全国平均', lw=5, ls=':')

# クラスタ毎に処理
for i in range(n_clusters):
  df_work = pd.DataFrame()
  for j, rec in df_clustering_result.iterrows():
    # 対象クラスタに分類された結果のみ抽出
    if rec.label==i:
      # インデックス(=都道府県名称)
      name = j
      df_work[name] = df_ratio[name]
  # クラスタ毎の平均をプロット
  plt.plot(df_work.mean(axis=1),label=f'#{i}クラスタ')
plt.title( '年齢別人口男女比(全国平均、クラスタ別)')
plt.xlabel('年代')
plt.ylabel('男性/女性の比率(%)')
plt.grid(axis='y')
plt.grid(axis='x')
plt.legend()
#plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=18)
plt.show()

output3.png

男女比が上がる(男性比率が100%を超えるか)か、上がるとすればどの年代までか、により、分類されているようである。
その後は、ゆるやかに(全国平均と同様に)男性比率が下がっているようである。(一つを除いて)

クラスタ毎に傾向を分析するため、クラスタ内全都道府県のグラフを、全国平均とともに可視化する。

# クラスタ毎に処理
for i in range(n_clusters):
  fig = plt.figure(figsize=(fig_size_x, fig_size_y))
  #全国平均
  plt.plot(df_total_ratio,label='全国平均', lw=5, ls=':')
  for j, rec in df_clustering_result.iterrows():
    # 対象クラスタに分類された結果のみ抽出
    if rec.label==i:
      plt.plot(df_ratio[j],label=f'{j}')
  plt.title( f'年齢別人口男女比(#{i}クラスタ)')
  plt.xlabel('年代')
  plt.ylabel('男性/女性の比率(%)')
  plt.grid(axis='y')
  plt.grid(axis='x')
  plt.legend()
  plt.show()

#0 クラスタ

output4.png

北海道、東北(北部)、中国、四国が多い。
全国平均にもっとも近いクラスタである。
他地域からの転入や他地域への転出が少ないと考えられる。これは、首都圏から遠いためではないだろうか?

#1 クラスタ

output5.png

首都圏が多いが、沖縄県も含まれる。
50代まで、男女比率がほぼ105%である。
日本全国で見ると、男性比率は20代以降徐々に減少しているにもかかわらず、男女比一定となっている。
50代まで、男女比が変わらないのは、医療体制が整っており、男性の死亡率が低いためか、他地域から転入してくるのか、謎である。
沖縄県は、他地域からの転入は考えられないので、長寿県ということから20代以降の男性死亡率が低いためと考えると、首都圏でも男性死亡率が低いのであろうか?

#2 クラスタ

output6.png

関西と九州が多い。
20代前半に、男性比率が減少している。
大阪、福岡だけなら、20代女性が、他地域から転入してくる、とも考えられるが、
関西、九州全域で同じ傾向であることを考えると、20代の男性が、首都圏に転出する。と考えたほうが無難ではないだろうか。

#3 クラスタ

output7.png

東北(北部)、北陸、甲信などが多い。
20代前半に、男性比率が上昇している。
これも、他地域から男性が転入してくるということは考えにくいので、20代前半の女性が、首都圏に転出しているのだろう。

#4 クラスタ

output8.png

北関東や東海など、首都圏の近隣県が多い。
20代から、男性比率が上昇し、20代後半でさらに上昇している。
首都圏が近いため、20代前半はもちろん、20代後半になっても、女性の首都圏への転出が多いのだろう。
20代前半より、20代後半のほうが男性比率が高い(女性比率が低い)のは謎である。
通常、高校卒業、大学卒業で上京するだろうが、20代後半でさらに多くの女性が上京しているのだろうか?

まとめ

年齢別男女人口比で、地域特性が見られたが、解釈が難しく、いろいろな推測ができるので、面白かった。

  • 首都圏近隣から女性が首都圏に転入してくる。
    • 首都圏への距離が近い地域では、男性より、女性のほうが、首都圏への憧れが強く、上京する傾向が強い。
    • 首都圏への距離が近いと、女性でも上京しやすい(帰省しやすい、親の了承も得やすい)
  • 関西、九州から、それより多くの男性が首都圏に転入してくる。そのため、首都圏では男性比率が高い傾向となる。
    • 首都圏への距離が遠くとも、男性は首都圏へ憧れ、上京する。(関西、九州のみ)
    • 首都圏への距離が遠いと、女性は上京しにくい(帰省しにくい、親の了承も得にくい)
  • 首都圏から遠い地域からは、首都圏への転入が少ない。
    • 首都圏への距離が遠い、北海道、東北(北部)、中国、四国では、地元にとどまる傾向が強い。

九州は首都圏から遠いはずだが、それでも男性が首都圏に転入してくるということは、九州男児は、果敢に都を目指して挑戦しているのだろう。(勝手に長〇さんをイメージしてしまう。死にたいくらいに憧れた花の都"大東京"♬)

また、平均寿命は男性より女性が長いので、50代もしくは60代以降に、女性比率が高くなるのだろうと思っていたが、20代以降にその傾向が表れているということは大きな驚きであった。(死亡率が原因なのかどうかは私にはわからないが、)

以上の考察は、推測なので、実際とあっているのかはわかりません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?