Open In Colab

地理データの可視化#

本節では緯度・経度の情報をもつ、地理データを可視化する手法を学びます。

国土交通省のGISホームページから、滋賀県の地価公示データを取得します。

https://nlftp.mlit.go.jp/ksj/old/datalist/old_KsjTmplt-L01.html

!wget https://nlftp.mlit.go.jp/ksj/old/data/L01/L01-2022P/L01-2022P-25-01.0a.zip
!unzip -o L01-2022P-25-01.0a.zip
--2022-11-19 05:51:51--  https://nlftp.mlit.go.jp/ksj/old/data/L01/L01-2022P/L01-2022P-25-01.0a.zip
Resolving nlftp.mlit.go.jp (nlftp.mlit.go.jp)... 192.29.39.162, 192.29.39.48, 192.29.41.65
Connecting to nlftp.mlit.go.jp (nlftp.mlit.go.jp)|192.29.39.162|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 55443 (54K) [application/zip]
Saving to: ‘L01-2022P-25-01.0a.zip’

L01-2022P-25-01.0a. 100%[===================>]  54.14K  --.-KB/s    in 0.005s  

2022-11-19 05:51:51 (11.7 MB/s) - ‘L01-2022P-25-01.0a.zip’ saved [55443/55443]

Archive:  L01-2022P-25-01.0a.zip
   creating: L01-2022P-25-01.0a/
  inflating: L01-2022P-25-01.0a/KS-META-L01-2022P-2K_25.xml  
  inflating: L01-2022P-25-01.0a/L01-2022P-25-01.0a.html  
  inflating: L01-2022P-25-01.0a/L01-2022P-2K_25.csv  
from io import StringIO
import json

import pandas as pd
import plotly.express as px
shiga_land_price = pd.read_csv("./L01-2022P-25-01.0a/L01-2022P-2K_25.csv", encoding="cp932")
shiga_land_price.loc[:, ["経度", "緯度"]] = shiga_land_price.loc[:, ["経度", "緯度"]] / 3600
shiga_land_price.head()
経度 緯度 所在地コード 用途 連番 年次 前年所在地コード 前年用途 前年連番 市区町村名 ... 属性移動H25 属性移動H26 属性移動H27 属性移動H28 属性移動H29 属性移動H30 属性移動H31 属性移動R2 属性移動R3 属性移動R4
0 135.911983 35.108868 25201 0 1 2022 25201 0 1 大津 ... 40000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000
1 135.915560 35.122949 25201 0 2 2022 25201 0 2 大津 ... 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000
2 135.900566 35.097219 25201 0 3 2022 25201 0 3 大津 ... 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000
3 135.876694 35.074805 25201 0 4 2022 25201 0 4 大津 ... 10000000000000 10100000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000
4 135.972987 35.251884 25201 0 5 2022 25201 0 5 大津 ... 20000000000000 20000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000 10000000000000

5 rows × 126 columns

散布図#

Plotly Expressの scatter_mapbox 関数を利用して、地図上に散布図を描画します。

  • data_frame(第1引数): 描画対象のDataFrame

  • lat: 緯度が格納されている列名

  • lon: 経度が格納されている列名

  • size: 要素の大きさをとなる列名

  • color: 要素の色となる列名

  • hover_name: ホバーツールのタイトルとなる列名

  • hover_data: ホバーツールに表示するデータとなる列名

  • height: 描画領域の高さ

  • center: 中心となる位置を辞書で指定(lat: 緯度, lon: 経度)

  • zoom: 縮尺

scatter_mapbox 関数はmapboxというサービスのAPIを利用しています。デフォルトを含め、いくつかのスタイルはmapboxのアクセストークンを渡す必要がありますが、トークンを必要としないスタイル( layout.mapbox.style )もあります。

layout.mapbox.style

https://plotly.com/python/mapbox-layers/

mapboxアカウントは無料で作成でき、アカウント画面からアクセストークンが生成できます。

https://account.mapbox.com

px.scatter_mapbox(
    shiga_land_price,
    lat="緯度",
    lon="経度",
    size="R4価格",
    color="用途区分",
    hover_name="市区町村名",
    hover_data=["所在並びに地番"],
    center={"lat": 35.09, "lon": 136.18},
    opacity=0.5,
    width=800,
    height=800,
    mapbox_style="carto-positron",
).show()
shiga_land_price.columns
Index(['経度', '緯度', '所在地コード', '用途', '連番', '年次', '前年所在地コード', '前年用途', '前年連番',
       '市区町村名',
       ...
       '属性移動H25', '属性移動H26', '属性移動H27', '属性移動H28', '属性移動H29', '属性移動H30',
       '属性移動H31', '属性移動R2', '属性移動R3', '属性移動R4'],
      dtype='object', length=126)
shiga_land_price.columns[shiga_land_price.columns.str.endswith("価格")]
Index(['S58価格', 'S59価格', 'S60価格', 'S61価格', 'S62価格', 'S63価格', 'H1価格', 'H2価格',
       'H3価格', 'H4価格', 'H5価格', 'H6価格', 'H7価格', 'H8価格', 'H9価格', 'H10価格',
       'H11価格', 'H12価格', 'H13価格', 'H14価格', 'H15価格', 'H16価格', 'H17価格', 'H18価格',
       'H19価格', 'H20価格', 'H21価格', 'H22価格', 'H23価格', 'H24価格', 'H25価格', 'H26価格',
       'H27価格', 'H28価格', 'H29価格', 'H30価格', 'H31価格', 'R2価格', 'R3価格', 'R4価格'],
      dtype='object')
shiga_land_price_split = shiga_land_price.iloc[
    :, : shiga_land_price.columns.get_loc("属性移動S59")
]
columns = list(shiga_land_price_split.columns)
columns[
    shiga_land_price_split.columns.get_loc(
        "S58価格"
    ) : shiga_land_price_split.columns.get_loc("R4価格")
    + 1
] = list(range(1983, 2023))
shiga_land_price_split.columns = columns
shiga_land_price_melt = pd.melt(
    shiga_land_price_split,
    id_vars=shiga_land_price.columns[: shiga_land_price_split.columns.get_loc(1983)],
    value_vars=list(range(1983, 2023)),
    var_name="年度",
    value_name="価格",
)
shiga_land_price_melt.iloc[:5, -5:]
共通地点区分 対前年変動率 選定年次ビット 年度 価格
0 False 0.0 0000000000000000000000000000001111111111 1983 0
1 False 0.6 1111111111111111111111111111111111111111 1983 91800
2 False -0.5 1111111111111111111111111111111111111111 1983 65900
3 True -1.3 1111111111111111111111111111111111111111 1983 85500
4 False -2.9 0000000000000000001111111111111111111111 1983 0
px.scatter_mapbox(
    shiga_land_price_melt,
    lat="緯度",
    lon="経度",
    size="価格",
    color="用途区分",
    hover_name="市区町村名",
    hover_data=["所在並びに地番"],
    animation_frame="年度",
    center={"lat": 35.09, "lon": 136.18},
    opacity=0.5,
    width=800,
    height=800,
    mapbox_style="carto-positron",
).show()

折れ線グラフ#

地図上に線を描画するには line_mapbox 関数を実行します。ここでは3つの地点をつないだ線を描画します。

tokaido_shinkansen_text = """駅名,経度,緯度
東京,139.766084,35.681382
品川,139.74044,35.630152
新横浜,139.617471434176,35.5067905836249
小田原,139.155380171724,35.2563636441241
熱海,139.07776,35.103217
三島,138.910627,35.127152
新富士,138.663199,35.142365
静岡,138.38884,34.97171
掛川,138.014928,34.769758
浜松,137.734442,34.703741
豊橋,137.381651,34.762811
三河安城,137.060662,34.96897
名古屋,136.881637,35.170694
岐阜羽島,136.685593,35.315705
米原,136.290488,35.314188
京都,135.757755,34.985458
新大阪,135.500109,34.73348"""
tokaido_shinkansen = pd.read_csv(StringIO(tokaido_shinkansen_text))
tokaido_shinkansen
駅名 経度 緯度
0 東京 139.766084 35.681382
1 品川 139.740440 35.630152
2 新横浜 139.617471 35.506791
3 小田原 139.155380 35.256364
4 熱海 139.077760 35.103217
5 三島 138.910627 35.127152
6 新富士 138.663199 35.142365
7 静岡 138.388840 34.971710
8 掛川 138.014928 34.769758
9 浜松 137.734442 34.703741
10 豊橋 137.381651 34.762811
11 三河安城 137.060662 34.968970
12 名古屋 136.881637 35.170694
13 岐阜羽島 136.685593 35.315705
14 米原 136.290488 35.314188
15 京都 135.757755 34.985458
16 新大阪 135.500109 34.733480
px.line_mapbox(
    tokaido_shinkansen,
    lat="緯度",
    lon="経度",
    hover_name="駅名",
    height=600,
    center={"lat": 35, "lon": 137.8},
    zoom=7,
    mapbox_style="carto-positron",
).show()

コロプレス図#

コロプレス図を描画するには choropleth_mapbox 関数を実行します。

国土交通省のGISホームページから、滋賀県の行政区域データを取得します。

https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03-v3_1.html#prefecture25

!wget https://nlftp.mlit.go.jp/ksj/gml/data/N03/N03-2022/N03-20220101_25_GML.zip
!unzip -o N03-20220101_25_GML.zip
--2022-11-19 05:51:58--  https://nlftp.mlit.go.jp/ksj/gml/data/N03/N03-2022/N03-20220101_25_GML.zip
Resolving nlftp.mlit.go.jp (nlftp.mlit.go.jp)... 192.29.41.65, 192.29.39.48, 192.29.39.162
Connecting to nlftp.mlit.go.jp (nlftp.mlit.go.jp)|192.29.41.65|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1789717 (1.7M) [application/zip]
Saving to: ‘N03-20220101_25_GML.zip’

N03-20220101_25_GML 100%[===================>]   1.71M  --.-KB/s    in 0.08s   

2022-11-19 05:51:58 (20.7 MB/s) - ‘N03-20220101_25_GML.zip’ saved [1789717/1789717]

Archive:  N03-20220101_25_GML.zip
  inflating: KS-META-N03-22_25_220101.xml  
  inflating: N03-22_25_220101.dbf    
  inflating: N03-22_25_220101.geojson  
  inflating: N03-22_25_220101.prj    
  inflating: N03-22_25_220101.shp    
  inflating: N03-22_25_220101.shx    
  inflating: N03-22_25_220101.xml    
with open("N03-22_25_220101.geojson") as f:
    geojson = json.load(f)

geojson["features"][1]["properties"]
{'N03_001': '滋賀県',
 'N03_002': None,
 'N03_003': None,
 'N03_004': '大津市',
 'N03_007': '25201'}

総務省 市区町村別の人口及び世帯数

https://www.soumu.go.jp/menu_news/s-news/17216_1.html

shiga_pop_text = """市区町村名,男,女,計,世帯数
大津市,160170,169871,330041,130143
彦根市,53908,55368,109276,41257
長浜市,39575,41263,80838,27937
近江八幡市,33530,34786,68316,25445
草津市,59131,58415,117546,46997
守山市,37379,38152,75531,26689
栗東市,31775,31670,63445,23091
甲賀市,45782,46877,92659,30640
野洲市,24889,24960,49849,17639
湖南市,27278,25621,52899,20037
高島市,26260,27599,53859,19205
東近江市,56384,57781,114165,38143
米原市,20140,20932,41072,13147
日野町,11158,11644,22802,7552
竜王町,7068,6256,13324,4412
愛荘町,9623,9833,19456,6256
豊郷町,3524,3681,7205,2574
甲良町,3792,4162,7954,2425
多賀町,3882,4251,8133,2640"""
shiga_pop = pd.read_csv(StringIO(shiga_pop_text))
shiga_pop.head()
市区町村名 世帯数
0 大津市 160170 169871 330041 130143
1 彦根市 53908 55368 109276 41257
2 長浜市 39575 41263 80838 27937
3 近江八幡市 33530 34786 68316 25445
4 草津市 59131 58415 117546 46997
px.choropleth_mapbox(
    shiga_pop,
    geojson=geojson,
    locations="市区町村名",
    color="計",
    hover_name="市区町村名",
    hover_data=["男", "女", "世帯数"],
    featureidkey="properties.N03_004",
    mapbox_style="carto-positron",
    zoom=8,
    center={"lat": 35.09, "lon": 136.18},
    opacity=0.5,
    width=800,
    height=800,
).show()

地理データを可視化するPythonパッケージ#

本節ではPlotly Expressを利用して地理データを可視化しましたが、ほかにも地理データを可視化するPythonパッケージがあります。下記は代表的なパッケージです。

  • Folium

    • leaflet.jsをPythonから扱える

    • 地図上にマーカーを描画する際などに利用される

  • pydeck

    • deck.glをPythonから扱える

    • Uber社が開発

    • データを3次元に表現

    • ビッグデータを高速に描画

!rm -rf L01-2022P-25-01.0a* *.zip* *.dbf *.geojson *.prj *.shp *.shx *.xml