meta.kimura

感情の率直と、思索の明澄と、語と文との簡潔とです。

【python】geopandasで市町村別の塗り分け地図を描く

f:id:meta-kimura:20191129104536j:plain

 いわゆる白地図に色を塗ったもの。最近めっきりpythonを使わなくなってしまったのもあるし、そういえばGeo的なことにも全く触れていなかったので、なんかしてみようという気になった。こういうときは、とりあえずほんとに簡単なものから手をつけるべし、である。久しぶりにjupyterを起動した。

<環境>
・Windows10
python 3.7
・Anaconda

 調べてみると、こういうのはコロプレス図っていうらしい。pythonで描く場合には、foliumかgeopandasを使う。foliumは前職でも少しだけお世話になった。たぶん、見ばえ的にもfoliumの方がいい。OpenStreetMapを下敷きにできるのは素晴らしいと思う。
 が、今回はgeopandasを使った。国交省のgeojsonファイルを読み込むところで詰まってしまったのである。shpファイルなら読み込めたのかもしれないけど、試してない。。。あと、余談だけど、こういうデータが国土地理院じゃないのが、ちょっと意外だ。なぜだろう。
 というわけで、以下のサイトから岐阜県の地図情報をダウンロードする。形式とかはいろいろとよくわかんないけど、欲しいのは市町村別の地図だから行政区域ってやつから最新のデータをもってきた。
nlftp.mlit.go.jp
 まずはインポートして、ファイルを読み込む。geopandasはシェープファイルを使うらしい。

import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt

fp = r"N03-20210101_21_GML/N03-21_21_210101.shp"
geo_data = gpd.read_file(fp, encoding="sjis")
geo_data.head()

f:id:meta-kimura:20210626073511p:plain

 頭出しをしてみるとこんな感じ。シェープファイルの中身って見たことがなかった。geometryってのに緯度経度っぽいものが入ってる。境界線を緯度経度で指定して描いてるってことなのかも。だとしたら、結構なデータ量になりそうなもんだが、どうなんだろう。
 あと、どうやらencodingはsjis指定が必要だった。ここで1つ詰まった。
 一旦、地図にしてみる。シェープファイルをプロットするだけの簡単操作。これはお手軽だった。

geo_data.plot(color="lightseagreen", linewidth=0.5, edgecolor="lightgray", figsize=(5, 5))

f:id:meta-kimura:20210626075255p:plain

 ここまでくればなんとなくわかる。たぶん、geopandasのデータフレームに数値くっつけたらいいのだろう。ってことで、岐阜県のページから人口データをダウンロードしてみた。
gifu-opendata.pref.gifu.lg.jp
 ちょっと予想を上まわる形式でダウンロードできた。使用したのは平成27年国勢調査人口等基本集計結果ってやつだが、エクセルでこんなのになっている。
f:id:meta-kimura:20210626080207p:plain

 まぁ、いろんな種類のデータを載せるから仕方ないのかもしれないが、ちょっとこのままでは読み込めない。仕方がないので、エクセルで形を整えて読み込んだ。

pop_data = pd.read_csv("kokuchokihon2015-5.csv")
pop_data.head()

f:id:meta-kimura:20210626080601p:plain

 あとはこのデータをシェープファイル読み込んだデータフレーム(今回はgeo_data)にマージ(くっつける)する。結合のキー情報が市町村名しかなさそうなので、日本語でjoinってのはちょっと気がひけるけど仕方なし。
 気をつけなきゃいけないのは、地図のデータと人口データの行数が違うこと。市町村の飛び地なんかもあるので、地図データの方が行数が多い。例えば「大垣市」が複数行ある。今回はあくまで市町村別での塗り分けを目指すってことで、left outer joinな感じにしている。

map_data = geo_data.merge(pop_data, left_on='N03_004', right_on='市町村', how="left")
map_data.head()

f:id:meta-kimura:20210626081552p:plain

 最後にplotするだけ。タイトルとか凡例とかをつけてるので、ちょっと細かくなってるが、基本的にはplotにcolumnを指定してあげるだけで色を塗ってくれる。

fig, ax = plt.subplots(1, 1, figsize=(10, 8))
ax.set_title('population of Gifu pref. 2015', fontsize=18)

map_data.plot(column='人口_H27', ax=ax, cmap='OrRd', edgecolor='gray', legend=True )
fig.savefig('population of Gifu pref .jpg', dpi=200)

f:id:meta-kimura:20210626081929j:plain

 うーん、これで完成なんだけど、なんかおもしろくない。結局岐阜市に人口が集中し過ぎているので、あんまり塗り分けられてない。なので、ここからはアディショナルタイムとして、人口増減率で塗ってみることしにした。

map_data['人口増減'] = map_data['人口_H27'] - map_data['人口_H22']
map_data['人口増減率'] = ( map_data['人口増減'] / map_data['人口_H22'] ) * 100


fig, ax = plt.subplots(1, 1, figsize=(10, 8))
ax.set_title('increase - decrease rate of population 2010 to 2015', fontsize=18)

map_data.plot(column='人口増減率', ax=ax, cmap='coolwarm', edgecolor='gray', legend=True )

f:id:meta-kimura:20210626082818p:plain

 カラフルになった。けど、けど、なんか違う。もっと人口は減ってるはずなのである。凡例見ればわかるけど「0.0」がオレンジじゃいけない。そこは白にすべきだっ!

import matplotlib.colors as mcolors

# normalize color
vmin, vmax, vcenter = -15, 15, 0
norm = mcolors.TwoSlopeNorm(vmin=vmin, vcenter=vcenter, vmax=vmax)

# create a normalized colorbar
cmap = 'coolwarm'
cbar = plt.cm.ScalarMappable(norm=norm, cmap=cmap)

# create a map
fig, ax = plt.subplots(1, 1, figsize=(10, 8))
ax.set_title('increase - decrease rate of population 2010 to 2015', fontsize=18)
map_data.plot(column='人口増減率', ax=ax, cmap='coolwarm', edgecolor='gray', norm=norm, legend=False)

# add colorbar
fig.colorbar(cbar, ax=ax)

fig.savefig('increase - decrease rate of population.jpg', dpi=200)

f:id:meta-kimura:20210626083340j:plain

 結局、まっ青になった。。。(これが現実でもある)



m(_ _)m