PyLearn MAP 02: 人口データとヒートマップ

ここでは、経度・緯度に対する人口データを対応づけたデータ分析方法について言及します。

人口データの入手先: 国土数値情報 ダウンロードサービスの下の方にある「1kmメッシュ別将来推計人口(H29国政局推計)(shape形式版)」を使用しています。以下は、千葉県のデータを利用しています。

0. データのロードと解釈

はじめに、データのロードを行います。人口データはshapeファイルと呼ばれるデータを使用しているので、ちょっと扱いがややこしいことに注意する必要があります。まず、以下をインストールします。

  • pip3 install pyshp (shapefileをインポートできる)
In [1]:
import shapefile

# 読み込み, SHIFT-JISで日本語対応
src = shapefile.Reader('Mesh3_POP_12.shx',encoding='SHIFT-JIS')

これでロードできました。ひとつ注意点ですが、Mesh3_POP_12.shxのみならず、dbf, prj, shxを同じディレクトリに置いておかないと、メッシュデータを使えません。

In [2]:
# 地域nにアクセス
n=0
Place = src.shape(n).points[0]
Dat = src.record(n)

print(Place)
print(Dat)
(139.775, 36.09166666666667)
Record #0: [54391612, 12208, 213.07, 193.04, 180.84, 168.56, 153.22, 137.01, 122.63, 110.55, 90.599, 84.874, 79.11, 71.911, 64.303, 57.554, 51.884, 11.02, 13.15, 14.45, 13.51, 11.87, 10.88, 9.26, 8.52, 119.328, 131.125, 122.595, 107.713, 98.73, 84.029, 77.314, 137.83, 87.52, 78.14, 72.82, 73.02, 68.52, 61.3, 47.48, 63.499, 56.693, 52.833, 52.978, 49.713, 44.475, 34.448, 64.19, 92.3, 88.17, 82.17, 68.28, 57.57, 52.02, 54.47, 143.792, 137.358, 128.011, 106.372, 89.687, 81.041, 84.857, 33.13, 37.63, 48.31, 59.83, 53.69, 44.68, 32.97, 27.27, 113.583, 145.819, 180.592, 162.059, 134.863, 99.517, 82.312]

Placeは経度緯度というのは想像がつくと思いますが、Datは数字がいっぱいあります。Datの列idはそれぞれ以下の意味を持ちます。

  • 00列目: MESH_CODE(1km四方、基準地域メッシュ(第3次地域区画)コード)
  • 01列目: CITY_CODE(市区町村を分類する5桁のコード番号)
  • 02列目〜09列目: 2010, 2020, 2025, 2030, 2035, 2040, 2045, 2050年の推定総人口(POP)
  • 10列目〜16列目: 2020, 2025, 2030, 2035, 2040, 2045, 2050年推定人口指数(INDEX)
  • 17列目〜24列目: 上記年度の00〜14歳の推定総人口(POP A)
  • 25列目〜31列目: 上記年度の00〜14歳の推定人口指数(INDEX A)
  • 32列目〜39列目: 上記年度の15〜64歳の推定総人口(POP B)
  • 40列目〜46列目: 上記年度の15〜64歳の推定人口指数(INDEX B)
  • 47列目〜54列目: 上記年度の65〜74歳の推定総人口(POP C)
  • 55列目〜61列目: 上記年度の65〜74歳の推定人口指数(INDEX C)
  • 62列目〜69列目: 上記年度の75〜00歳の推定総人口(POP D)
  • 70列目〜76列目: 上記年度の75〜00歳の推定人口指数(INDEX D)
  • 人口の単位は[人]となる。

POPは2010年分があるため7列分となりますが、INDEXは2020年からのため6列となることに注意する必要があります。なお、INDEXとは2010年の人口を100としたときの人口割合となります。 ヘッダの並びは、以下で参照できます。

db = shapefile.Reader('Mesh3_POP_12.dbf',encoding='SHIFT-JIS')
db.__geo_interface__

詳細は、以下を参照(ただ、データの並びとか結構わかりにくい)。

国土数値情報 1kmメッシュ別将来推計人口(H29国政局推計)(shape形式版)

これが理解できると、以下のように地域コードに対する人口を得ることができます。

In [3]:
n=0
print("座標:", src.shape(n).points[0])
print("基準地域メッシュコード:", src.record(n)[0])
print("市町村コード:", src.record(n)[1])
print("2010年総人口:", src.record(n)[2], "人")
print("2020年推定総人口:", src.record(n)[3], "人")
print("2025年推定総人口:", src.record(n)[4], "人")
print("...")
座標: (139.775, 36.09166666666667)
基準地域メッシュコード: 54391612
市町村コード: 12208
2010年総人口: 213.07 人
2020年推定総人口: 193.04 人
2025年推定総人口: 180.84 人
...

理解を終えたところで、Foliumで使用しやすいようにデータを加工しています。さて、勘のいい人なら気がついたかもしれませんが、経度と緯度の並びが、実は逆になっています。そのため、これに注意して変数を作っていきます。

今回は、2010年の総人口を解析に使用することにします。

In [4]:
Place = []
MeshCode = []
Population = []

# (なぜか0番目に埼玉が混ざっているので1から)
for i in range(1, len(src)): # len(src)は千葉県内のメッシュの数 

    # 座標の取得
    a = src.shape(i).points[0][0]
    b = src.shape(i).points[0][1]
    Place.append([float(b), float(a)]) # 経度と緯度を逆にし、Foliumの順に
    
    # メッシュコードの取得
    MeshCode.append(str(src.record(i)[0]))
    
    # 2010年の総人口を取得
    Population.append(src.record(i)[2])

1. Foliumとの統合

続いて、地図を作成し、マーカの上に人口を表示するプログラムを組んでみます。

In [5]:
import folium as fl
import geocoder as ge
from folium.features import DivIcon

# 千葉県の中心を取得
Central = ge.arcgis("千葉県")
Central = Central.latlng

def MakeBox(in_text):
    out_text = '''
            <div style="text-align: center;
            font-size: 5pt;
            color: black;
            padding-top: 1px;
            height: 30px;
            width: 100px;
            background: white;
            border: 2px solid;
            border-radius: 30%;">'''
    out_text = out_text+ in_text + '</div>'
    return out_text

# マップ作成
map1 = fl.Map(location = Central,
                        zoom_start = 14,
                        tiles = 'openstreetmap')

# マーカ設置
for i in range(0, len(Place)):
    html_text = "MeshCode: " + MeshCode[i] + "</br>"+ "Population: " + str(Population[i])
    fl.Marker(location = Place[i], icon=DivIcon(html=MakeBox(html_text))).add_to(map1)

map1
Out[5]:

どこに人口が多いのか、わかるようになりました。Zoomが小さいと重いので注意してください。駅付近は人口が多いとか、海や山は人口が少ないとか、そんなことがわかります。

もうちょっとわかりやすくみたい人は、HeatMapを利用してください。HeatMap関数の第一引数に経度、緯度、人口のリストを入れます。

  • radius: 色のレンジのようなもの。大きくすると小さい値でも色が濃くなる(標準25)。
  • blur: ぼかす度合い(標準15)

と、ドキュメントに書いているが、なかなか調整が難しいので思考錯誤で...

In [6]:
from folium.plugins import HeatMap

# マップ作成
map2 = fl.Map(location = Central,
                        zoom_start = 9,
                        tiles = 'openstreetmap')

# 経度、緯度、人口の並びのリストを作成
heatmap_dat = []
for i in range(0, len(Place)):
    heatmap_dat.append([Place[i][0], Place[i][1], Population[i]])
    
# ヒートマップ作成
HeatMap(heatmap_dat, radius=30, blur=60).add_to(map2)

map2
Out[6]:

みやすいと思いきや、みにくいとも言えます。使えそうなときに使う感じで良いでしょう。