Python Folium Relearning(4) from May 2025(続き:国土数値情報と総務省e-Statを活用した都道府県別Choropleth Mapの描画)

 前回:https://shikuuk.blogspot.com/2025/05/pythongisfoliumfolium-in-python_27.html

ChoroplethMapに苦戦しましたが、何とか関東およびその近辺の都道府県のDataについてMapをDrawすることができました。こうなるともう一度全国版にChallageしたいというのが人情だと思います。

そこで、Gemini先生にGeoJSON形式でMultiPolygonを扱うことができるのかを、改めて質問しました。回答は以下の通りです。

はい、GeoJSON形式はMultiPolygonを扱うことができます。

GeoJSON仕様では、MultiPolygon は複数の Polygon オブジェクトの集合を表すジオメトリタイプとして明確に定義されています。

以下に MultiPolygon のGeoJSON形式の例を示します。

JSON
{
  "type": "MultiPolygon",
  "coordinates": [
    [
      [
        [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]
      ]
    ],
    [
      [
        [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]
      ],
      [
        [100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]
      ]
    ]
  ]
}

この例では、coordinates プロパティは複数のPolygonの配列となっており、それぞれのPolygonはさらに外部リングと内部リング(穴)の配列として表現されています。

GeoJson形式でMultiPolygonをCan't UseなのでErrorがOccurしているのかとSeemしていましたが、どうも原因がことなるようです。ですので、これまでMake&UseしてきたCodeも含めて、AllJapanVersionのPopulation of Each PrefectureのThe Rate of Total PopulationをDrawするコードをCreatします。

>Code< まずは国土数値情報の行政区域から自治体境界情報をGetします。

import urllib.request
import zipfile
import geopandas as gpd

def download_map_data(url, zip_file_name, shp_file_name):
    zip_file_path = 'unzipped_data/' + zip_file_name

# URLからデータをダウンロード
    urllib.request.urlretrieve(url, zip_file_name)

# ZIPファイルを解凍
    with zipfile.ZipFile(zip_file_name, 'r') as zip_ref:
        zip_ref.extractall('unzipped_data')  # 解凍先のディレクトリを指定

# Shapefileを読み込む
#shapefile_path = 'unzipped_data/W05-06_36-g_Stream.shp'
    shp_file_path = 'unzipped_data/' + shp_file_name
    gdf = gpd.read_file(shp_file_path)

    return gdf

# ダウンロードとデータ読み込みのループ
url = 'http://nlftp.mlit.go.jp/ksj/gml/data/N03/N03-2024/N03-20240101_GML.zip'
zip_file_name = 'N03-20240101_GML.zip'
shp_file_name = 'N03-20240101.shp'

# データをダウンロードして読み込む
gdf = download_map_data(url, zip_file_name, shp_file_name)

# データを表示
gdf

>Output<

N03_001N03_002N03_003N03_004N03_005N03_007geometry
0北海道石狩振興局None札幌市中央区01101POLYGON ((141.25694 42.99782, 141.2563 42.9978...
1北海道石狩振興局None札幌市北区01102POLYGON ((141.33325 43.07505, 141.3326 43.0757...
2北海道石狩振興局None札幌市東区01103POLYGON ((141.37341 43.0684, 141.37333 43.0684...
3北海道石狩振興局None札幌市白石区01104POLYGON ((141.38202 43.04832, 141.38193 43.048...
4北海道石狩振興局None札幌市豊平区01105POLYGON ((141.36371 42.94154, 141.36378 42.941...
........................
124128沖縄県None八重山郡与那国町None47382POLYGON ((122.93299 24.4517, 122.93299 24.4517...
124129沖縄県None八重山郡与那国町None47382POLYGON ((122.93396 24.4482, 122.93396 24.4482...
124130沖縄県None八重山郡与那国町None47382POLYGON ((122.94213 24.44395, 122.94214 24.443...
124131沖縄県None八重山郡与那国町None47382POLYGON ((122.99876 24.43985, 122.99875 24.439...
124132沖縄県None八重山郡与那国町None47382POLYGON ((122.93308 24.45176, 122.93308 24.451...

124133 rows × 7 columns

>Code< EPSGコードを確認します。

print(gdf.crs)

>Output< EPSGコードを4326にTranslationするRequirementがあるようです。

EPSG:6668


>Code< JapaneseGov.GeneralAffairsのe-Statから都道府県別の人口比率のデータをGet

# このコードは総務省e-Statから都道府県別人口の割合(総人口比)をGetする
import requests
import pandas as pd

APP_ID = '***************' #自分のe-Stat API IDを*********に入力
API_URL = "http://api.e-stat.go.jp/rest/3.0/app/json/getStatsData"

params = {
    "appId": APP_ID,
    "statsDataId": "0003448233", #人口推計-総務省-都道府県別人口の割合(総人口)のstatDataID
    "lang": "J",  # 日本語を指定
    "cdTime": "2021000000" #対象とする時期(年)を2021年指定
}

#人口統計
response = requests.get(API_URL, params=params)
# Process the response
data = response.json()
data

>Output< 全部記載すると長いので、先頭あたりだけ掲載します。

{'GET_STATS_DATA': {'RESULT': {'STATUS': 0,
   'ERROR_MSG': '正常に終了しました。',
   'DATE': '2025-05-29T05:40:43.625+09:00'},
  'PARAMETER': {'LANG': 'J',
   'STATS_DATA_ID': '0003448233',
   'NARROWING_COND': {'CODE_TIME_SELECT': 2021000000},
   'DATA_FORMAT': 'J',
   'START_POSITION': 1,
   'METAGET_FLG': 'Y'},
  'STATISTICAL_DATA': {'RESULT_INF': {'TOTAL_NUMBER': 48,
    'FROM_NUMBER': 1,
    'TO_NUMBER': 48},
   'TABLE_INF': {'@id': '0003448233',
    'STAT_NAME': {'@code': '00200524', '$': '人口推計'},
    'GOV_ORG': {'@code': '00200', '$': '総務省'},
    'STATISTICS_NAME': '人口推計 各年10月1日現在人口 令和2年国勢調査基準 統計表',
    'TITLE': {'@no': '006', '$': '都道府県別人口の割合-総人口'},
    'CYCLE': '年次',
    'SURVEY_DATE': 0,
    'OPEN_DATE': '2025-04-14',
    'SMALL_AREA': 0,
    'COLLECT_AREA': '該当なし',
    'MAIN_CATEGORY': {'@code': '02', '$': '人口・世帯'},
    'SUB_CATEGORY': {'@code': '01', '$': '人口'},
    'OVERALL_TOTAL_NUMBER': 336,
    'UPDATED_DATE': '2025-04-14',
    'STATISTICS_NAME_SPEC': {'TABULATION_CATEGORY': '人口推計',
     'TABULATION_SUB_CATEGORY1': '各年10月1日現在人口',
     'TABULATION_SUB_CATEGORY2': '令和2年国勢調査基準',
     'TABULATION_SUB_CATEGORY3': '統計表'},
    'DESCRIPTION': '',
    'TITLE_SPEC': {'TABLE_NAME': '都道府県別人口の割合-総人口'}},
   'CLASS_INF': {'CLASS_OBJ': [{'@id': 'tab',
      '@name': '表章項目',
      'CLASS': {'@code': '003', '@name': '人口割合', '@level': '', '@unit': '%'}},
     {'@id': 'area',
      '@name': '全国・都道府県',
      'CLASS': [{'@code': '00000', '@name': '全国', '@level': '1'},
       {'@code': '01000', '@name': '北海道', '@level': '2'},
       {'@code': '02000', '@name': '青森県', '@level': '2'},
       {'@code': '03000', '@name': '岩手県', '@level': '2'},
       {'@code': '04000', '@name': '宮城県', '@level': '2'},
       {'@code': '05000', '@name': '秋田県', '@level': '2'},
       {'@code': '06000', '@name': '山形県', '@level': '2'},
       {'@code': '07000', '@name': '福島県', '@level': '2'},
       {'@code': '08000', '@name': '茨城県', '@level': '2'},
       {'@code': '09000', '@name': '栃木県', '@level': '2'},
       {'@code': '10000', '@name': '群馬県', '@level': '2'},
       {'@code': '11000', '@name': '埼玉県', '@level': '2'},
       {'@code': '12000', '@name': '千葉県', '@level': '2'},
       {'@code': '13000', '@name': '東京都', '@level': '2'},
       {'@code': '14000', '@name': '神奈川県', '@level': '2'},
       {'@code': '15000', '@name': '新潟県', '@level': '2'},
       {'@code': '16000', '@name': '富山県', '@level': '2'},
       {'@code': '17000', '@name': '石川県', '@level': '2'},
       {'@code': '18000', '@name': '福井県', '@level': '2'},
       {'@code': '19000', '@name': '山梨県', '@level': '2'},
       {'@code': '20000', '@name': '長野県', '@level': '2'},
       {'@code': '21000', '@name': '岐阜県', '@level': '2'},
       {'@code': '22000', '@name': '静岡県', '@level': '2'},
       {'@code': '23000', '@name': '愛知県', '@level': '2'},
       {'@code': '24000', '@name': '三重県', '@level': '2'},
       {'@code': '25000', '@name': '滋賀県', '@level': '2'},
       {'@code': '26000', '@name': '京都府', '@level': '2'},
       {'@code': '27000', '@name': '大阪府', '@level': '2'},
       {'@code': '28000', '@name': '兵庫県', '@level': '2'},
       {'@code': '29000', '@name': '奈良県', '@level': '2'},
       {'@code': '30000', '@name': '和歌山県', '@level': '2'},
       {'@code': '31000', '@name': '鳥取県', '@level': '2'},
       {'@code': '32000', '@name': '島根県', '@level': '2'},
       {'@code': '33000', '@name': '岡山県', '@level': '2'},
       {'@code': '34000', '@name': '広島県', '@level': '2'},
       {'@code': '35000', '@name': '山口県', '@level': '2'},
       {'@code': '36000', '@name': '徳島県', '@level': '2'},
       {'@code': '37000', '@name': '香川県', '@level': '2'},
       {'@code': '38000', '@name': '愛媛県', '@level': '2'},
       {'@code': '39000', '@name': '高知県', '@level': '2'},
       {'@code': '40000', '@name': '福岡県', '@level': '2'},
       {'@code': '41000', '@name': '佐賀県', '@level': '2'},
       {'@code': '42000', '@name': '長崎県', '@level': '2'},
       {'@code': '43000', '@name': '熊本県', '@level': '2'},
       {'@code': '44000', '@name': '大分県', '@level': '2'},
       {'@code': '45000', '@name': '宮崎県', '@level': '2'},
       {'@code': '46000', '@name': '鹿児島県', '@level': '2'},
       {'@code': '47000', '@name': '沖縄県', '@level': '2'}]},
     {'@id': 'time',
      '@name': '時間軸(年)',
      'CLASS': {'@code': '2021000000', '@name': '2021年', '@level': '1'}}]},
   'DATA_INF': {'NOTE': [{'@char': '***', '$': '該当数値がないもの'},
     {'@char': '-', '$': '該当数値がないもの\u3000'}],
    'VALUE': [{'@tab': '003',
      '@area': '00000',
      '@time': '2021000000',
      '@unit': '%',
      '$': '100.00'},
     {'@tab': '003',
      '@area': '01000',
      '@time': '2021000000',
      '@unit': '%',
      '$': '4.13'},
     {'@tab': '003',
      '@area': '02000',
      '@time': '2021000000',
      '@unit': '%',
      '$': '0.97'},


>Code< Jsonの辞書DataのKeyをわかりやすい名称にしたうえで、PandasDataFrameにTranslateします。

# このコードはe-StatからゲットしたデータをPandasDataFrameに変換
#JSONデータをPandasのDataFrameに変換
df = pd.DataFrame(data['GET_STATS_DATA']['STATISTICAL_DATA']['DATA_INF']['VALUE'])

# Pandas dataの加工
# Column名の変更
column_rename = {
    '@tab': 'タブ',
    '@area': '都道府県コード',
    '@time': '調査年',
    '@unit': '単位',
    '$': '人口比(対全国人口)'
}

df.rename(columns=column_rename, inplace=True)

df.head()

>Output<

タブ都道府県コード調査年単位人口比(対全国人口)
0003000002021000000100.00
10030100020210000004.13
20030200020210000000.97
30030300020210000000.95
40030400020210000001.82


>Code< 都道府県コードを都道府県名に紐づけて、GeoPandasDataFrameの列名に合わせて'N03_001'とします。

# このコードはe-StatのPandasDataFrameの都道府県コードに基づき都道府県名を追加
# 都道府県コードと都道府県名の対応辞書
pref_code_to_name = {
    '01000': '北海道', '02000': '青森県', '03000': '岩手県', '04000': '宮城県', '05000': '秋田県',
    '06000': '山形県', '07000': '福島県', '08000': '茨城県', '09000': '栃木県', '10000': '群馬県',
    '11000': '埼玉県', '12000': '千葉県', '13000': '東京都', '14000': '神奈川県', '15000': '新潟県',
    '16000': '富山県', '17000': '石川県', '18000': '福井県', '19000': '山梨県', '20000': '長野県',
    '21000': '岐阜県', '22000': '静岡県', '23000': '愛知県', '24000': '三重県', '25000': '滋賀県',
    '26000': '京都府', '27000': '大阪府', '28000': '兵庫県', '29000': '奈良県', '30000': '和歌山県',
    '31000': '鳥取県', '32000': '島根県', '33000': '岡山県', '34000': '広島県', '35000': '山口県',
    '36000': '徳島県', '37000': '香川県', '38000': '愛媛県', '39000': '高知県', '40000': '福岡県',
    '41000': '佐賀県', '42000': '長崎県', '43000': '熊本県', '44000': '大分県', '45000': '宮崎県',
    '46000': '鹿児島県', '47000': '沖縄県', '00000': '全国'
}

# 都道府県コードに基づいて都道府県名を追加する新しい列を作成
df['N03_001'] = df['都道府県コード'].map(pref_code_to_name)
df.head()

>Output<

タブ都道府県コード調査年単位人口比(対全国人口)N03_001
0003000002021000000100.00全国
10030100020210000004.13北海道
20030200020210000000.97青森県
30030300020210000000.95岩手県
40030400020210000001.82宮城県


>Code< 人口比の値のTypeをConfirmします。

print(df['人口比(対全国人口)'].dtype)

>Output< 'object'なので、数値型(int of float)にNeedToTranlateです。

object


>Code< 人口比の数値をFloat型にTranslateします。

# このコードはe-StatのDataから変換した'人口比(対全国人口)'の値を文字列から数値(float)に変換
# --- 必須のデータ型変換 ---
# '人口比(対全国人口)' 列を数値型に変換します。
# errors='coerce' を使用することで、数値に変換できない値は NaN (Not a Number) になります。
df['人口比(対全国人口)'] = pd.to_numeric(df['人口比(対全国人口)'], errors='coerce').astype(float)

# 「全国」の行を削除
df = df[df['N03_001'] != '全国'].copy()

print(df['人口比(対全国人口)'].dtype)

df.head()

>Output<

float64
タブ都道府県コード調査年単位人口比(対全国人口)N03_001
10030100020210000004.13北海道
20030200020210000000.97青森県
30030300020210000000.95岩手県
40030400020210000001.82宮城県
50030500020210000000.75秋田県


>Code< gdfのEPSGを4326にTranlateしたうえで、都道府県名'N03_001'に基づき市町村の境界Dataを都道府県別にして結合(disolve)し、gdf_prefに格納

import folium

# 地図の中心となる緯度経度を設定
center_lat = 35.6895
center_lon = 139.6917

# Foliumの地図オブジェクトを作成
m = folium.Map(location=[center_lat, center_lon], zoom_start=6, tiles="cartodbpositron")

# ステップ1: 都道府県コード ('pref_code') で市町村を結合(dissolve)します
# 'by' パラメータで結合の基準となるカラムを指定します。
# 他のカラムの集計方法を指定しない場合、デフォルトの集計が行われます。
gdf.to_crs(epsg=4326) # 座標系をEPGS=6668からEPGS=4326に変更

gdf_pref = gdf.dissolve(by='N03_001')#, aggfunc={'N03_001': 'first'})

# ステップ2: (オプション) インデックスをリセットして 'pref_code' を通常カラムに戻します
gdf_pref = gdf_pref.reset_index()

gdf_pref.head()

>Output<

N03_001geometryN03_002N03_003N03_004N03_005N03_007
0三重県MULTIPOLYGON (((136.02156 33.7343, 136.02154 3...None桑名郡津市None24201
1京都府MULTIPOLYGON (((134.89358 35.64858, 134.89358 ...None乙訓郡京都市北区26101
2佐賀県MULTIPOLYGON (((129.78926 33.44452, 129.7893 3...None神埼郡佐賀市None41201
3兵庫県MULTIPOLYGON (((134.38287 34.71978, 134.38288 ...None川辺郡神戸市東灘区28101
4北海道MULTIPOLYGON (((139.34092 41.49493, 139.34094 ...石狩振興局石狩郡札幌市中央区01101


>Code< さきほど作成したgdf_prefをGeoJson形式にTranslateして、FoliumでChoroplethMapをDraw

# 形状を簡略化する (toleranceの値は調整が必要)
tolerance = 0.01 #0.01  # 小さいほど元の形状に近いがデータ量は多い
#gdf['simplified_geometry'] = gdf['geometry'].apply(lambda geom: geom.simplify(tolerance))
gdf_pref['geometry'] = gdf_pref['geometry'].apply(lambda geom: geom.simplify(tolerance))

# GeoDataFrameをGeoJson形式に変換
geojson_pref = gdf_pref.to_json(ensure_ascii=False)

folium.Choropleth(
    geo_data = geojson_pref,
    name="choropleth",
    data = df,
    columns=["N03_001", "人口比(対全国人口)"],
    key_on="feature.properties.N03_001",
    fill_color="YlGn",
    fill_opacity=0.7,
    line_opacity=0,
    legend_name="Population Rate (%)",
    # threshold_scale を明示的に指定することで、色の範囲を制御します。
    # ここでは、0, 5, 10, 15, 20% で区切る例
    threshold_scale=[0, 5, 10, 15, 20] # このリストが色の境界値を決定し、0-20%に調整します。
).add_to(m)

folium.LayerControl().add_to(m)

m

>Output<


 Drawできました!ここまで至るのに3週間くらいかかりました。


前回:https://shikuuk.blogspot.com/2025/05/pythongisfoliumfolium-in-python_27.html

次回:https://shikuuk.blogspot.com/2025/05/foliumfoliumhpuser-guide-url.html


コメント