地理データの可視化
Contents
地理データの可視化#
本節では緯度・経度の情報をもつ、地理データを可視化する手法を学びます。
国土交通省の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.stylehttps://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パッケージがあります。下記は代表的なパッケージです。
-
leaflet.jsをPythonから扱える
地図上にマーカーを描画する際などに利用される
-
deck.glをPythonから扱える
Uber社が開発
データを3次元に表現
ビッグデータを高速に描画
!rm -rf L01-2022P-25-01.0a* *.zip* *.dbf *.geojson *.prj *.shp *.shx *.xml