これなに
日本地図を県別に色分けする Python3 用のライブラリ(japanmap)を作成したので紹介します。
実行例は、Jupyter Notebook 上で確認しています。
インストール
pipでできます。numpy1、OpenCV、Pillowも一緒にインストールされます。xlrdは、Excelファイルの読み込みで使います。
pip install -U japanmap jupyter matplotlib pandas xlrd
県コードとは
都道府県コード(以下、県コードと略す)は、JIS X 0401 により定められた、各都道府県ごとの01から47のコードです。
プログラムでは整数で扱います(頭の0を無視します)。
県名確認
pref_namesで県コードごとの県名がわかります。
from japanmap import pref_names
pref_names[1]
>>>
'北海道'
pref_codeで県名に対する県コードがわかります。
from japanmap import pref_code
pref_code('京都'), pref_code('京都府')
>>>
(26, 26)
groupsで八地方区分ごとの県コードがわかります。
from japanmap import groups
groups['関東']
>>>
[8, 9, 10, 11, 12, 13, 14]
白地図
pictureで白地図(ラスタデータ)を取得できます。
%config InlineBackend.figure_formats = {'png', 'retina'}
%matplotlib inline
import matplotlib.pyplot as plt
from japanmap import picture
plt.rcParams['figure.figsize'] = 6, 6
plt.imshow(picture());
県を色で塗ることもできます。
plt.imshow(picture({'北海道': 'blue'}));
PNGファイルへの保存
savefig
でファイルに保存できます。
plt.imshow(picture({'北海道': 'blue'}))
plt.savefig('japan.png')
隣接情報
is_faced2sea で県コードに対して、庁所在地を含むエリアが海に面しているかわかります。
from japanmap import is_faced2sea
for i in [11, 26]:
print(pref_names[i], is_faced2sea(i))
>>>
埼玉県 False
京都府 True
is_sandwiched2sea で県コードに対して、庁所在地を含むエリアが海に挟まれているかわかります。(連続でない海辺が2つ以上あるか)
from japanmap import is_sandwiched2sea
for i in [2, 28]:
print(pref_names[i], is_sandwiched2sea(i))
>>>
青森県 False
兵庫県 True
adjacent で県コードに対して、庁所在地を含むエリアが隣接する県コードがわかります。
from japanmap import adjacent
for i in [2, 20]:
print(pref_names[i], ':', ' '.join([pref_names[j] for j in adjacent(i)]))
>>>
青森県 : 岩手県 秋田県
長野県 : 群馬県 埼玉県 新潟県 富山県 山梨県 岐阜県 静岡県 愛知県
境界線のベクトルデータ
get_data で各県の点リストと点のindexリストが取れます。これを使うと、pref_points で県の境界(index list)のリストが取れます。
from japanmap import get_data, pref_points
qpqo = get_data()
pnts = pref_points(qpqo)
pnts[0] # 北海道の境界座標(経度緯度)リスト
>>>
[[140.47133887410146, 43.08302992960164],
[140.43751046098984, 43.13755540826223],
[140.3625317793531, 43.18162745988813],
...
pref_map で境界線データを可視化できます。
from japanmap import pref_map
svg = pref_map(range(1,48), qpqo=qpqo, width=2.5)
svg
SVGファイルへの保存
pref_mapは、SVG形式です。下記のようにしてファイルに保存できます。
with open('japan.svg', 'w') as fp:
fp.write(svg.data)
関東だけをグレースケールにする例。
pref_map(groups['関東'], cols='gray', qpqo=qpqo, width=2.5)
県別データ(人口)を使って、県の面積比を変換
人口比で地図上の面積比を変換してみましょう。総務省統計局の「なるほど統計学園」の人口データの画面下部の「出典の統計表」を押して2017年の県別の人口2のファイル(a00400.xls)をダウンロードしましょう。
import pandas as pd
df = pd.read_excel('a00400.xls', usecols=[9, 12, 13, 14], skiprows=18, skipfooter=3,
names='都道府県 男女計 男 女'.split()).set_index('都道府県')
df[:3]
男女計 | 男 | 女 | |
---|---|---|---|
都道府県 | |||
北海道 | 5320 | 2506 | 2814 |
青森県 | 1278 | 600 | 678 |
岩手県 | 1255 | 604 | 651 |
人口比の多い順に表示してみましょう。東京都の比の5.09
は、県平均の5.09倍を表しています。
df['比'] = df.男女計 / df.男女計.mean()
df.sort_values('比', ascending=False)[:10]
男女計 | 男 | 女 | 比 | |
---|---|---|---|---|
都道府県 | ||||
東京都 | 13724 | 6760 | 6964 | 5.090665 |
神奈川県 | 9159 | 4569 | 4590 | 3.397362 |
大阪府 | 8823 | 4241 | 4583 | 3.272729 |
愛知県 | 7525 | 3764 | 3761 | 2.791260 |
埼玉県 | 7310 | 3648 | 3662 | 2.711510 |
千葉県 | 6246 | 3103 | 3143 | 2.316839 |
兵庫県 | 5503 | 2624 | 2879 | 2.041237 |
北海道 | 5320 | 2506 | 2814 | 1.973356 |
福岡県 | 5107 | 2415 | 2692 | 1.894348 |
静岡県 | 3675 | 1810 | 1866 | 1.363174 |
可視化してみます。
trans_area
で県の面積を指定した比率に変換できます。
例えば、県ごとの変換比率を[2, 1, 1, 1, ...]
とすると、北海道が元の面積の2倍、他県が元の面積と同じ比率になります。
下記では、人口が平均の2倍であれば面積を2倍にするように表示しています。
from japanmap import trans_area
qpqo = get_data(True, True, True)
pref_map(range(1,48), qpqo=trans_area(df.男女計, qpqo), width=2.5)
なるべく、ラフにして歪みを減らしたのですが、なかなか厳しいです。
白地図上で人口を可視化
下記のようにすると、人口の多い県を濃い赤で可視化できます。
cmap = plt.get_cmap('Reds')
norm = plt.Normalize(vmin=df.男女計.min(), vmax=df.男女計.max())
fcol = lambda x: '#' + bytes(cmap(norm(x), bytes=True)[:3]).hex()
plt.colorbar(plt.cm.ScalarMappable(norm, cmap))
plt.imshow(picture(df.男女計.apply(fcol)));
4色問題
隣接情報を使って4色問題を解いてみましょう。
1つの県を1色とし、隣接する県は異なるように、日本全体を4色で塗りましょう。このように隣り合うもの同士に違う色を割当てる問題を、頂点彩色問題とよびます。
頂点彩色問題とは、グラフ上で隣接する頂点が異なる色になるように、最小の色数で頂点に色を割り当てる問題です。
この応用としては、例えば、携帯電話の基地局ごとの周波数を決める問題があります。(異なる色→異なる周波数→電波が干渉しないので話せる)
4色問題を解く
どのような平面地図であっても隣接するエリアが異なるようにして、必ず4色以内で塗り分けられることが、数学的に証明されています。しかし、どのように塗り分けたらよいかは、自明ではありません。ここでは、数理最適化を解いて求めることにしましょう。
数理最適化は、コストの最小化などを解くときに使われますが、目的関数がない制約だけの問題でも解くことができます。数理最適化で用いるパッケージ PuLPについては、最適化におけるPythonを見てください。
- 数理モデル(1)で決めないといけないことは、変数表現、目的関数、制約条件の3つです。
- 変数表現: 47都道府県×4色で188個の0または1しか取らない変数を用意します。このような変数は、バイナリ変数とよばれます。 変数は、パッケージ pandasの表で管理します。この変数表(2)を使うことにより制約条件をわかりやすく書くことができます。
- 目的関数: 今回は最大化などを行いませんので、設定しません。PuLPでは、目的関数を設定しなくても実行できます。
- 制約条件: 1県ごとに1色とします(3)。隣接した県同士は、異なる色にします(4)。
- 数理モデルができたら、ソルバーを実行するだけで解を求めることができます(5)。 ソルバーは、数理モデルを解くソフトウェアで、pulpをインストールすると一緒にインストールされます。
- 結果は、変数に対してvalueをよぶことで確認できます。ここでは、変数表に、新しい列"Val"を追加し結果を設定しています(6)。 この新しい列が0でない行を取得することにより、県に対する塗るべき色がわかります。
実行するためには、追加で PuLPと ortoolpyが必要です(pip install pulp ortoolpy
)。
import pandas as pd
from ortoolpy import model_min, addbinvars, addvals
from pulp import lpSum
from japanmap import pref_names, adjacent, pref_map
m = model_min() # 数理モデル(1)
df = pd.DataFrame([(i, pref_names[i], j) for i in range(1, 48) for j in range(4)],
columns=['Code', '県', '色'])
addbinvars(df) # 変数表(2)
for i in range(1, 48):
m += lpSum(df[df.Code == i].Var) == 1 # 1県1色(3)
for j in adjacent(i):
for k in range(4): # 隣接県を違う色に(4)
m += lpSum(df.query('Code.isin([@i, @j]) and 色 == @k').Var) <= 1
m.solve() # 求解(5)
addvals(df) # 結果設定(6)
四色 = ['red', 'blue', 'green', 'yellow']
cols = df[df.Val > 0].色.apply(四色.__getitem__).reset_index(drop=True)
pref_map(range(1, 48), cols=cols, width=2.5)
コメント
@ty21ky0
@SaitoTsutomu0
@SaitoTsutomu0
@ty21ky0
@ty21ky0
@ty21ky
0
@SaitoTsutomu
0
@ty21ky0
@SaitoTsutomu0
@ty21ky0
@ty21ky0
@SaitoTsutomu0
@ty21ky0
@SaitoTsutomu0
@ty21ky0
@SaitoTsutomu0
@SaitoTsutomu0
@ty21ky0
@SaitoTsutomu(編集済み)
0
@ty21ky
0
@ty21ky
0
@SaitoTsutomu0
@ty21ky
0
@SaitoTsutomu0
@ty21ky0
Tsutomu Saito さま
https://qiita.com/ty21ky/items/f4af8009be4e0407b6fd
で境界線のベクトルデータを利用して、mpl_toolkits.basemapの地図に都府県境界線を追加しました。
もし、問題があればコメント欄にご連絡ください。すぐに削除しますので。
よろしくお願いします。
@ty21ky 問題ないです。
こんなのも作りました。
https://pypi.org/project/osm-graph/
Tsutomu Saito さま
ありがとう御座います。
osm-graph 0.0.3
もインストールが出来たら使ってみます。
私のOSでは、OpenCVもgccのverをサポートしていないエラーでてインストールできないんです。
スキルが不足しているので、どうしようもありません。
Tsutomu Saito さま
申し訳ないですが、データの変更をお願い出来ますでしょうか。
例として、山形県の最後のポイントが1つ抜けています。
隣接県の新潟県の方は入っています。
この他に何箇所か見つけました。
もし、変更していただけるならわかる範囲で今回のようなイメージを作成します。
よろしくお願いします。
更新しました。
pip install -U japanmap
すみません。
pip install -U japanmapで更新して、実行しようとしたらエラーが出たのですが確認していただけますか。
from japanmap import *
でエラーが出てしまいます。
よろしくお願いします。
$ ./県境関数test.py
Traceback (most recent call last):
File "./県境関数test.py", line 7, in
import japan_border #都府県境界線
File "/home/ty/python/map/japan/japanmap/関数化/japan_border.py", line 5, in
from japanmap import *
File "/home/ty/.local/lib/python3.6/site-packages/japanmap/init.py", line 77
def pref_map(ips, cols=None, width=1, qpqo=None, tostr=False, ratio=(0.812,-1)):
^
SyntaxError: invalid syntax
おぉ、すいません、更新しました。
ありがとう御座います。
動きました。
SaitoTsutomuさま
japanmapの境界線のベクトルデータを読み込んで、海岸線等の部分を削除して前回は作成したのですが、japanmapがver upされてデータ数が増減するとデータがずれてしまいました。
そこで、県境部分だけのデータを作成し、新しくライブラリを作成しました。
japanmapの境界線のベクトルデータは、SaitoTsutomuさまの著作権があると思うのですが、ずうずうしいお願いですが、このライブラリを公開しても宜しいでしょうか。
もしよければ、確認のためにコメント欄にコードを貼り付けさせていただきます。
よろしくお願いします。
こちらで、直せそうなら、見てみます。
あと、ライセンスは、Python Software Foundation Licenseです。
https://ja.wikipedia.org/wiki/Python_Software_Foundation_License
コードの文字数があまりも多いのでコメント欄に添付するのは無理でした。
一時的にQiitaに投稿して、それを見ていただけますか。
はい
https://qiita.com/ty21ky/items/a5023ed4e07128d81c7b
に投稿しました。
よろしくお願いします。
ありがとうございます。見てみます。
すいません、「japanmapがver upされてデータ数が増減するとデータがずれてしまいました。」とは、どういう不具合でしょうか?
最初のやつは、
from japanmap import *
qpqo = get_data()
pnts = pref_points(qpqo)
これで、各県のjapanmapの境界線のベクトルデータを読み込んで、最初から何個のデータを削除、最後から何個のデータ削除とやっていました。
pip install -U japanmap
を実行した後は、各県のデータの数が数個増えていたので、海岸線・県境の方にはみ出してしまいました。
japanmapとmpl_toolkits.basemapでは海岸線の位置が若干違いますので、線が2重になってしまいました。
バグではない、ということでしょうか?
ちなみに、下記の
othr
に、境界線の反対側の県コードが入っているので、境界線だけなら下記のようにかけます。バグではありません。私のスキルが不足しているため、私の出来る範囲の方法でやっているためです。
上記の方法でも出来ました。ありがとうございます。
先程のものと、上記の方法を両方記載しておいても良いでしょうか。
先程も書きましたが、十和田湖の所の県境が国交省の地図と違いました。
はい。 > 先程のものと、上記の方法を両方記載しておいても良いでしょうか。
何でだろう? > 十和田湖の所の県境が国交省の地図と違いました。
国土数値情報 ダウンロードサービス
http://nlftp.mlit.go.jp/ksj/
の行政区域のN03-17_170101.shpファイルを重ね合わせた物です。
このせいかな?(japanmapが古い)
https://ja.wikipedia.org/wiki/県境#解消された境界未定地域
これは、十和田湖の湖畔か、湖の真ん中かの違いですから、一般の人にはあまり関係ないと思いますが。