Pythonを使って住んでいる地域の人口予測をしてみる

はじめに

この記事は、Python初心者である私が、現在の居住地である茨城県鹿嶋市の人口推移データを参考に、2050年までの人口予測を出してみた、という内容になります。

勉強したての機械学習の知識を用いて行っていますので、今後この分野を学ぶ人の何かしらの参考になれば幸いです。

用いたデータ

市のホームページに人口推移のエクセル資料がダウンロードできるようになっていたので、そちらを利用しました。(人口・世帯数の推移)

このデータは大正9年(1920年)という100年以上の前の人口から記載されていますが、途中までが5年刻みで記載されており、1年ごとのデータが記載されるようになるのは、平成5年(1993年)からになります。

データの分析の精度、整合性という観点から今回は1年ごとの推移がわかる平成5年からのデータを用いて予測をしています。

機械学習の精度を高めるにはより細かいスケールのデータの方が望ましく、理想は大正9年からの月ごとの人口推移がわかればベストでしたが、今回は平成に入って以降の月ごとのデータすらも見つからなかったため、やむなく平成5年からの1年ごとのデータを材料に予測を出しています。

コードを書いてみる

結果

まず最初に、それらしきアウトプットを出すことができたので、そのコードを記載します。

今回、ARIMAモデルという時系列データの予測に使われる統計手法を用いて出力をしました。ARIMAモデルの特徴として、時系列データに特化しており、時間的な依存性を考慮して予測を出力してくれます。

後述しますが、他のモデルを選択したところ、直近の実データが人口減少傾向になっているにも関わらず、今後2050年に向けて人口が増加し続ける、という結果が出ました。

唯一、ARIMAモデルだけは直近の人口減少を考慮した予測をしており、一番現実的な推移になったと感じています。これが、機械学習を行う上で、最適なモデルを選択する重要性だと実感しました。

 #Google  ドライブへのマウント
from google.colab import drive
drive.mount('/content/drive')
 #モジュールのインポート 
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA

# データの読み込み
df_kashima = pd.read_csv('/content/drive//My Drive/output/d01.csv')

# 年度と人口を抽出
X_kashima = df_kashima[['Year']]
y_kashima = df_kashima['Population_kashima']

# ARIMA モデルの適用
model = ARIMA(y_kashima, order=(5, 1, 0)) 
model_fit = model.fit()

# 予測の実行
future_years = np.arange(2023, 2051)
forecast = model_fit.forecast(steps=len(future_years))

# 予測結果をプロット
plt.figure(figsize=(10, 6))
plt.plot(X_kashima['Year'], y_kashima, label='Actual Population', color='black')
plt.plot(future_years, forecast, label='ARIMA Prediction', color='blue')
plt.xlabel('Year')
plt.ylabel('Population')
plt.title('Population Prediction of Kashima City until 2050 using ARIMA')
plt.legend()
plt.grid(True)
plt.show()

# 予測結果を確認
for year, pred in zip(future_years, forecast):
    print(f"Year: {year}, Predicted Population: {pred}")
 #予測後した人口推移 
Year: 2023, Predicted Population: 65297.87133969325
Year: 2024, Predicted Population: 65188.0804997717
Year: 2025, Predicted Population: 65049.70925085108
Year: 2026, Predicted Population: 64906.27564408506
Year: 2027, Predicted Population: 64823.8615368327
Year: 2028, Predicted Population: 64789.30701536866
Year: 2029, Predicted Population: 64755.25409486184
Year: 2030, Predicted Population: 64719.69340606067
Year: 2031, Predicted Population: 64692.635343738555
Year: 2032, Predicted Population: 64677.59765166622
Year: 2033, Predicted Population: 64668.03182151857
Year: 2034, Predicted Population: 64658.976843951394
Year: 2035, Predicted Population: 64651.21979411395
Year: 2036, Predicted Population: 64645.99048702786
Year: 2037, Predicted Population: 64642.749190335344
Year: 2038, Predicted Population: 64640.2746543454
Year: 2039, Predicted Population: 64638.13911745685
Year: 2040, Predicted Population: 64636.518590153
Year: 2041, Predicted Population: 64635.43835752328
Year: 2042, Predicted Population: 64634.68793057895
Year: 2043, Predicted Population: 64634.09112811765
Year: 2044, Predicted Population: 64633.616599202185
Year: 2045, Predicted Population: 64633.27517570626
Year: 2046, Predicted Population: 64633.03905853387
Year: 2047, Predicted Population: 64632.86409813691
Year: 2048, Predicted Population: 64632.72703829638
Year: 2049, Predicted Population: 64632.62365149892
Year: 2050, Predicted Population: 64632.549910043075

環境の用意

コードの内容に入っていきます。

まず、今回Pythonコードを書く環境として、GoogleColaboratoryを使用しています。同一のGoogleアカウントに接続しているGoogleDriveからデータを読み込んで、GoogleColaboratoryで分析を行うため、GoogleColaboratoryとGoogleDriveを接続する必要があります。

 #Google  ドライブへのマウント
from google.colab import drive
drive.mount('/content/drive')

接続自体は簡単にすることができました。
以下のように、ファイルの置き場所とファイル名を指定し、読み込んだ上で変数に格納しています。

# データの読み込み
df_kashima = pd.read_csv('/content/drive//My Drive/output/d01.csv')

モジュールのインポート

分析や可視化に使用するモジュールをインポートします。

 #モジュールのインポート 
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
  • pandas:CSVの読み込みや表形式のデータを取り扱いに使用

  • numpy :数値計算や統計処理を実行

  • matplotlib:データの可視化(グラフ化等)をするのに利用

  • Statsmodels:統計モデルの実装や推定をするのに利用するが、今回はARIMAモデルを使用するためにインポート

データの準備

変数「df_kashima」にcsvを読み込んだデータを格納し、年次(Year)、人口(Population_kashima)をそれぞれ変数「X_kashima」「y_kashima」に格納します。

# データの読み込み
df_kashima = pd.read_csv('/content/drive//My Drive/output/d01.csv')

# 年度と人口を抽出
X_kashima = df_kashima[['Year']]
y_kashima = df_kashima['Population_kashima']

ARIMAモデルで予測

ARIMAモデルで予測をしていきます。
「order=(5, 1, 0)」はARIMAモデルの主要なパラメータを表します。
自己回帰(AR,p=5)、差分(I,d=1)、移動平均(MA,q=0)となります。

自己回帰は過去5年間のデータを使用して、現在のデータを予測することを表します。

差分はデータのトレンドを除去し、時系列データを定常にするために用いられます。d=1の場合、前のデータとの差分を1回取ることを示します。

移動平均は、過去の予測誤差を考慮するかどうかを示しており、今回の場合はq=0のため、過去の予測誤差は考慮しません。

作成したARIMAモデルをy_kashima(人口)データにフィッティングし、データのパターンを学習します。

その上で、フィッティングしたモデルを用いて、2023年〜2050年までの人口を予測します。

# ARIMA モデルの適用
model = ARIMA(y_kashima, order=(5, 1, 0)) 
model_fit = model.fit()

# 予測の実行
future_years = np.arange(2023, 2051)
forecast = model_fit.forecast(steps=len(future_years))

データの可視化

最後に、matplotlibを用いて、データをグラフ化します。
予測したデータがわかりやすいよう、実際のデータは黒、予測値は青で表記するようにしています。

また、実際に予測した実数を確認したかったため、最後に予測値をテキストで出力するようにもしています。

# 予測結果をプロット
plt.figure(figsize=(10, 6))
plt.plot(X_kashima['Year'], y_kashima, label='Actual Population', color='black')
plt.plot(future_years, forecast, label='ARIMA Prediction', color='blue')
plt.xlabel('Year')
plt.ylabel('Population')
plt.title('Population Prediction of Kashima City until 2050 using ARIMA')
plt.legend()
plt.grid(True)
plt.show()

# 予測結果を確認
for year, pred in zip(future_years, forecast):
    print(f"Year: {year}, Predicted Population: {pred}")

うまくいかなかったこと

最初、他の人のブログなどを参考にして、線形回帰、ラッソ回帰、リッジ回帰などのモデルを使用して出力しようとしましたが、あまり現実的ではないグラフが出来上がりました。

  • 線形回帰:データの傾向を直線で予測するモデル。

  • ラッソ回帰:特徴選択をしながら重要な変数だけを使って予測するモデル。

  • リッジ回帰:過学習を防ぐために、回帰係数を抑えながら予測するモデル。

日本全体の人口が減少傾向にあるのに、過去のデータが右肩上がりに人口増加しているために、今後も増加し続けるであろう、という予測になったものと思われます。

今回のケースで行くと、直近の人口動向を考慮してくれるARIMAモデルが適しているのだと感じました。

 #Google  ドライブへのマウント
from google.colab import drive
drive.mount('/content/drive')
 #モジュールのインポート 
import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression, Ridge, Lasso
import matplotlib.pyplot as plt

# データの読み込み
df_kashima = pd.read_csv('/content/drive/My Drive/output/d01.csv')
df_japan = pd.read_csv('/content/drive/My Drive/output/d02.csv')

# 年度と人口を抽出
X_kashima = df_kashima[['Year']]
y_kashima = df_kashima['Population_kashima']
X_japan = df_japan[['Year']]
y_japan = df_japan['Population_japan']

# 共通の年でデータを合わせる
common_years = X_kashima['Year'].isin(X_japan['Year'])
X_kashima = X_kashima[common_years]
y_kashima = y_kashima[common_years]
y_japan = y_japan[X_japan['Year'].isin(X_kashima['Year'])]
X_japan = X_japan[X_japan['Year'].isin(X_kashima['Year'])]

# 日本の人口変化率を計算して特徴量に追加
japan_population_change_rate = y_japan.pct_change().fillna(0)

# 説明変数として茨城県鹿嶋市の人口、日本全体の人口、日本の人口変化率を使用
X = pd.concat([X_kashima, y_japan.reset_index(drop=True), japan_population_change_rate.reset_index(drop=True)], axis=1)
y = y_kashima.reset_index(drop=True)

# 欠損値の処理(必要に応じて)
X = X.ffill()

# 2050年までの年を生成
future_years = np.arange(X['Year'].min(), 2051).reshape(-1, 1)

# X_japan と y_japan が同じ長さであることを確認
X_japan_years = X_japan['Year'].values
y_japan_values = y_japan.values

# 日本の人口変化率も未来の値を予測
future_japan_pop = np.interp(future_years.flatten(), X_japan_years, y_japan_values)
future_japan_change_rate = np.interp(future_years.flatten(), X_japan_years, japan_population_change_rate)

# 未来の特徴量を結合
X_future = np.hstack([future_years, future_japan_pop.reshape(-1, 1), future_japan_change_rate.reshape(-1, 1)])

# 線形回帰モデル
lin_reg = LinearRegression()
lin_reg.fit(X, y)
y_pred_linear = lin_reg.predict(X_future)

# リッジ回帰モデル
ridge_reg = Ridge(alpha=1.0)
ridge_reg.fit(X, y)
y_pred_ridge = ridge_reg.predict(X_future)

# ラッソ回帰モデル
lasso_reg = Lasso(alpha=0.1)
lasso_reg.fit(X, y)
y_pred_lasso = lasso_reg.predict(X_future)

# 結果をプロット
plt.figure(figsize=(10, 6))
plt.plot(future_years, y_pred_linear, label='Linear Regression', color='blue')
plt.plot(future_years, y_pred_ridge, label='Ridge Regression', color='green')
plt.plot(future_years, y_pred_lasso, label='Lasso Regression', color='red')
plt.scatter(X['Year'], y, label='Actual Population', color='black')
plt.xlabel('Year')
plt.ylabel('Population')
plt.title('Population Prediction of Kashima City until 2050')
plt.legend()
plt.grid(True)
plt.show()

まとめ

今回、手探りで機械学習を勉強してきましたが、やはりこうした実例を元に自分でアウトプットするのが、最も勉強になると感じました。

また今回のデータだけだと、一番まともな形になったとは言え、将来的にはもっと人口が減少していくと思われるので、よりリアルな予測をするための説明変数を追加するなど、精度のブラッシュアップをしていきたいと思いましたし、現在の知識をより掛け合わせ、深掘りしていく必要性を強く感じました。

ひとまず、一区切りということでお疲れ様でした。
最後まで見ていただき、ありがとうございました。


この記事が気に入ったらサポートをしてみませんか?