練馬区の町丁目の人口を可視化してみた

## はじめに
本記事では練馬区の町丁目の人口を可視化してみます。
筆者が練馬区民のため対象を練馬区としていますが、コードの一部を変えるだけであらゆる地域の人口を可視化することができます。

今回のコードは[ヘルシンキ大学が公開しているGISに関する教材](https://autogis-site.readthedocs.io/en/2021/)を元にしています。
筆者はそちらの教材を勉強した後のアウトプットでこの記事を書いています。
GISについて勉強してみたい方はぜひそちらを使ってみてください。そちらの教材についての質問があればQiitaのコメントで議論させていただけると、筆者も勉強になり大変嬉しいです。

※本記事はGoogle Colab上で使用することを想定しています。ローカル環境でのコード実行時には事前に必要なライブラリのインストールが必要になります。

## 東京都の町丁目ごとのデータを取得する
まずは東京都の町丁目ごとの境界データと人口データを[e-Stat](https://www.e-stat.go.jp/)から取得します。
e-Statは日本の統計が閲覧できる政府統計のポータルサイトです。各省庁が公表する統計データがまとまっており、検索したり地図上に表示できたりします。

サイトからデータを手動でダウンロードしてくることも可能ですが、今回はPythonライブラリのdownloadを使ってデータをダウンロードします。

本記事で使うライブラリを先にまとめてインポートしておきます。
```python
!pip install geopandas
!pip install japanize_matplotlib
!pip install download
!pip install mapclassify
import mapclassify
import download
import japanize_matplotlib
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import folium
import os
```

データをダウンロードします。
```python
data_fp = "データを格納するフォルダパスをここに入力してください"

# 東京都の行政区域ごとの境界データをダウンロードする
download.download(
  "https://www.e-stat.go.jp/gis/statmap-search/data?dlserveyId=A002005212020&code=13&coordSys=1&format=shape&downloadType=5&datum=2000",
   data_fp,
   kind="zip",
   replace=True,
)

# 東京都の行政区域ごとの人口データをダウンロードする
download.download(
   "https://www.e-stat.go.jp/gis/statmap-search/data?statsId=T001081&code=13&downloadType=2",
   data_fp,
   kind="zip",
   replace=True,
)
```

下の画像のように5つのファイルがダウンロードできました。
![00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/97e0d9a6-77fe-d057-9ba3-6ed165132e17.png)

ダウンロードしてきた境界データを読み込みます。
東京都の境界データを読み込むことができているか確認するために、境界データの可視化もしてみます。
```Python
fp1 =  os.path.join(data_fp, 'r2ka13.shp')
geo = gpd.read_file(fp1)
geo.plot()
```

一見なにも表示していない気がしてしまいますが、左上に東京都っぽい形状があります。このような表示になってしまう理由は、小笠原諸島など離島地域も表示されるためです。
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/83dbfaba-b0f4-0e5b-f348-f8817b048e69.png)

上のものではわかりづらいので、練馬区だけを抜き出して表示してみます。
```Python
geo[geo['CITY_NAME'] == "練馬区"].plot()
```

確かに細かく区切られた境界データが取得できています。
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/20fb25c9-be68-c501-3332-f68cd4a5d910.png)

必要なカラムだけ取り出しておきます。
町丁目は一意のKEY_CODEが割り振られており、人口データ等の統計データとマージする際にはこのKEY_CODEをキーにすることでマージできます。
```Python
# 必要なカラムを取り出す
geo = geo.loc[:, ["geometry", 'KEY_CODE']]
geo['KEY_CODE'] = geo['KEY_CODE'].astype(str)
geo.head()
```
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/a777b816-947c-2a21-19bc-ec3851c9db83.png)

次にダウンロードしてきた人口データを読み込みます。
```Python
fp2 =  os.path.join(data_fp, 'tblT001081C13.txt')
data = pd.read_csv(fp2, encoding="CP932", dtype={'KEY_CODE': str, 'HYOSYO': str})
data.head()
```

人口データが取得できました。0行目にカラム名のようなデータが入ってしまっていますが、こちらは後ほど取り除きます。
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/55d3b323-662c-e60f-9a80-8535b66445b0.png)

必要なカラムだけ取り出し、人口のカラム名をpopに変更、NaNを含む行を消去します。
```Python
data = data.loc[:, ["KEY_CODE", "HYOSYO","CITYNAME","NAME", "T001081001"]]
data = data.rename(columns={'T001081001': 'pop'})
data['KEY_CODE'] = data['KEY_CODE'].astype(str)
data['pop'] = data['pop'].astype(float)
data.dropna(inplace=True)
data.head()
```

無事0行目のデータも取り除くことができました。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/6a060b3e-3baf-b05d-b2c4-dafec6f0284c.png)

HYOSYO列は行政区域のレベルを持ってます。今回対象としている町丁目レベルは”4”のものなので、値が4のもののみ取り出します。また、popが”-”となっている部分があります。この部分は値を0に置換しておきます。
```Python
data = data[data['HYOSYO'] == "4"]
data = data.replace('-', '0')
data.head()
```

人口データの前処理が終わりました。
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/27041cc6-0aa2-505c-a15b-54dd3150738c.png)

## 地図を表示する
境界データ(geo)と人口データ(data)をマージします。上でも述べたように、行政区域コードである”KEY_CODE”をキーにしてマージします。
```Python
geodata =  geo.merge(data, on='KEY_CODE')
geodata.head()
```

マージすることができました。
![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/9b04f58c-fce3-c7d9-e76f-7562cc40cbeb.png)

練馬区の人口データをプロットしてみます。
```Python
fig, ax = plt.subplots(1, figsize=(10, 8))
geodata[geodata["CITYNAME"]=="練馬区"].plot(ax=ax, column="pop", legend=True)
```

町丁目ごとに色が塗られた図を得ることができました。
これだけでも町丁目ごとの人口の関係がわかりますが、その他のよりわかりやすい可視化方法を試してみたいと思います。
![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/028baf59-e326-f430-8abf-476190354cd7.png)

plotの引数にscheme="quantiles"、k=4、cmap=”Reds”を加えてみます。
```Python
fig, ax = plt.subplots(1, figsize=(10, 8))
geodata[geodata["CITYNAME"]=="練馬区"].plot(ax=ax, column="pop", scheme="quantiles",k=4 ,cmap="Reds", legend=True)
```

4つの区分に色分けされた図を得ることができました。今回引数で与えたscheme="quantiles"、k=4は四分位数で分類するように指定したものです。その他の分類手法は[geopandasのリファレンス](https://geopandas.org/en/stable/gallery/choropleths.html)を参照してみてください。
今回のような表示をすることで、どの地域が人口上位25%の地域なのかなどがわかりやすくなります。しかし、最も人口が多い地域はどこかわからなくなる欠点もあります。自分の目的にあった可視化方法が重要ということがわかります。

![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/57d40ef2-6d54-296d-9d9e-8e0c27b9e8f8.png)

次にFoliumを使ってインタラクティブな地図を表示してみます。
Foliumは、インタラクティブなデータを視覚化できるようにする Pythonライブラリです。
地図表示の細かな設定は[Foliumのドキュメント](https://python-visualization.github.io/folium/)を参照してください。
```Python
geodata['geoid'] = geodata.index.astype(str)

m = folium.Map(location=[35.75,139.62], tiles = 'cartodbpositron', zoom_start=13, control_scale=True)

folium.Choropleth(geo_data = geodata[geodata["CITYNAME"]=="練馬区"],
                 data = geodata[geodata["CITYNAME"]=="練馬区"],
                 columns=['geoid','pop'],
                 key_on='feature.id',
                 fill_color='YlOrRd',
                 line_color='white',
                 line_weight=0,
                 legend_name= 'poplation').add_to(m)

m
```


Qiitaではhtmlを埋め込むことができなさそうであったため、githubに表示した地図のhtmlをあげています。[こちら](https://oz-oz-oz-oz.github.io/nerima_pop/choropleth_map1.html)をご覧ください。
背景の地図から町丁目名を読み取ることができ、地域名を確認しながらの人口比較が用意になりました。

地域の上にカーソルを持っていったときに人口が数値で表示されるようにします。
先ほど定義したmapに以下の設定を追記します。
```Python
folium.features.GeoJson(geodata, name='Labels',
              style_function=lambda x: {'color':'transparent','fillColor':'transparent','weight':0},
               tooltip=folium.features.GeoJsonTooltip(fields=['pop'],
                                             aliases = ['Poplation'],
                                             labels=True,
                                             sticky=False
                                            )
                      ).add_to(m)

m
```

表示された地図は[こちら](https://oz-oz-oz-oz.github.io/nerima_pop/choropleth_map2.html)をご覧ください。
カーソルのある地域の人口が表示されるようになりました。この他にも様々な表示方法がありますので、foliumのドキュメントや冒頭で紹介したヘルシンキ大学の資料をぜひ参照してみてください。

最後に練馬区の町丁目のうち、人口上位５地域、下位５地域のグラフを表示します。
```Python
geodata_nerima = geodata[geodata["CITYNAME"]=="練馬区"]
data_nerima = pd.DataFrame(geodata_nerima.drop(columns='geometry'))
data_nerima.sort_values(by='pop',ascending=True,inplace=True)
data_nerima_top_worst_5 = pd.merge(data_nerima[:5], data_nerima[-5:], how='outer')
data_nerima_top_worst_5.set_index('NAME', inplace=True)
data_nerima_top_worst_5.plot.barh()
```

表示することができました。
光が丘四丁目の人口が０人で驚きましたが、調べてみると丸ごと都立光が丘公園のようです。そのようなこともあるのですね。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2152045/bb90150a-fed4-5db3-395e-0d86a963dd3c.png)

## まとめ
本記事ではGeopandas、folium等を使って練馬区の町丁目の人口を可視化しました。今回は可視化したデータを使って何かを考察するといったことは行いませんでしたが、今後何か目的を決めて考察できればと思っています。

これからも勉強しながら成果を記事にしていこうと思うので、よろしくお願いします。

## 参考文献
ヘルシンキ大学GIS資料
https://autogis-site.readthedocs.io/en/2021

e-Stat 政府統計の総合窓口
https://www.e-stat.go.jp/

Geopandasリファレンス
https://geopandas.org/en/stable/gallery/choropleths.html

Foliumドキュメント
https://python-visualization.github.io/folium/