0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

警察庁 交通事故統計オープンデータを理解する — データ仕様と前処理のポイント

Posted at

はじめに

警察庁が公開している「交通事故統計情報オープンデータ」は、全国の交通事故を詳細にまとめたデータセットです。
事故の発生場所や日時、当事者の属性、道路状況などが整理されており、年ごとに更新・公開されています。

警察庁 交通事故統計情報オープンデータ

ただ、実際に扱ってみると、項目定義やデータ形式が少し分かりづらい印象を受けます。
なので本記事では、2024年版のデータを題材に、仕様の整理と処理のポイントをまとめようと思います。

2024年版データ

  • 本票(honhyo_2024.csv) … 各交通事故の基本情報
  • 補充票(hojuhyo_2024.csv) … 当事者ごとの情報
  • 高速票(kosokuhyo_2024.csv) … 高速道路上の事故情報
  • ファイル定義書(fileteigisyo_2024.pdf) … 各CSVの項目定義
  • 各種コード表(codebook_2024.pdf) … コード値と意味の対応表

データの構造

交通事故統計オープンデータは、複数のCSVファイルで構成されており、それぞれが異なる粒度の情報を持っています。
これらのファイルは相互に関連しており、内部的にはリレーショナルデータベースのような構造になっています。
つまり、単体でも利用できますが、結合することでより詳細な分析が可能になります。

主な構成は次のとおりです。

  • 本票(honhyo_2024.csv)
    各交通事故の基本情報をまとめたファイル
    発生日時、場所、事故類型、負傷者数、天候、道路形状など、事故そのものの概要が記録されている

  • 補充票(hojuhyo_2024.csv)
    事故に関与した当事者ごとの情報を収録
    車種、性別、年齢層、行動類型などが含まれており、本票と組み合わせることで当事者属性の分析ができる

  • 高速票(kosokuhyo_2024.csv)
    高速道路上で発生した事故の補足情報をまとめたもの
    該当する事故のみ存在し、一般道路のデータには含まれない

これらのファイルは次のような関係を持っています。

  • 本票:補充票 = 1:多(1件の事故に複数の当事者が存在)
  • 本票:高速票 = 1:1 または 0:1(高速事故のみ対応するレコードが存在)

データを扱う上での注意点と前処理

交通事故統計オープンデータは、実際にはそのままでは扱いづらい独自仕様がいくつか存在します。
項目の形式やコード体系が一般的な規格と異なっているため、前処理の段階で注意が必要です。

この章では、データを活用する前に理解しておきたい仕様のポイントと、それぞれに対応する前処理の例を紹介します。

都道府県コード(JISコードと異なる)

本データで使われている都道府県コードは、
JISコード(総務省などで用いられる行政コード)とは異なる独自の体系です。
他データと併せて使用する際、対応表を用意して変換すると扱いやすくなります。

# 都道府県コードをJISコードや名称に変換する例
pref_map = {
    # 一部
    10: {"jis": "01", "name": "北海道"},
    30: {"jis": "13", "name": "東京都"},
    40: {"jis": "27", "name": "大阪府"},
    50: {"jis": "23", "name": "愛知県"},
    # 
    # jisコードは通常ゼロ埋めした2桁文字列ですが、
    # ゼロ埋めせずに数字として扱っている場合もあるのでその点は注意
}

df["都道府県名"] = df["都道府県コード"].astype(int).map(lambda x: pref_map.get(x, {}).get("name"))
df["JISコード"] = df["都道府県コード"].astype(int).map(lambda x: pref_map.get(x, {}).get("jis"))

緯度・経度(右詰め構造の度分秒形式)

緯度は 9 桁、経度は 10 桁の固定長文字列で、
右から 5 桁が「秒+ミリ秒」、次の 2 桁が「分」、残りが「度」という固定構造です。

def dms_to_deg(value: int | str) -> float:
    """
    度分秒(DMS)形式を区切りなし整数から10進度に変換
    右5桁: 秒+ミリ秒(SSmmm)
    次の2桁: 分(MM)
    残り: 度(DまたはDDまたはDDD)
    """
    s = str(int(value)).rjust(10, "0")  # 緯度9桁→10桁右詰め補完
    sec = int(s[-5:]) / 1000
    minute = int(s[-7:-5])
    deg = int(s[:-7])
    return deg + minute / 60 + sec / 3600

# 例
print(dms_to_deg(430607590))   # → 43.102108333333334
print(dms_to_deg(1412109599))  # → 141.35266638888888

10 進度に変換しておくと扱いやすいです。

発生日時(日付・時刻列の統合)

発生日時は「年」「月」「日」「時」「分」がそれぞれ別の列として記録されています。
このままでは時系列分析がしづらいため、日付と時刻を結合して日時型に変換します。

# 発生日時を結合して datetime 型に変換する例
df["発生日時"] = pd.to_datetime(
    df["発生日時  年"].astype(str) + "-" +
    df["発生日時  月"].astype(str).str.zfill(2) + "-" +
    df["発生日時  日"].astype(str).str.zfill(2) + " " +
    df["発生日時  時"].astype(str).str.zfill(2) + ":" +
    df["発生日時  分"].astype(str).str.zfill(2),
    format="%Y-%m-%d %H:%M"
)

# 例: 曜日や時間帯の抽出
df["曜日"] = df["発生日時"].dt.day_name()
df["時間帯"] = pd.cut(
    df["発生日時"].dt.hour,
    bins=[0, 6, 12, 18, 24],
    labels=["深夜", "午前", "午後", "夜間"],
    right=False
)

変換後の列を datetime 型にしておくことで、事故発生の時刻帯や曜日などを容易に抽出できます。

データ型(固定長文字列とゼロ埋め)

すべての列が文字列として定義されており、数値項目も「01」や「0005」のようにゼロ埋めされている項目があります。
このままでは集計や演算ができないため、必要に応じて数値型へ変換します。

# 文字列として定義されている数値列を整数型に変換
df["負傷者数"] = df["負傷者数"].astype(int)

# 例: 負傷者数の平均を計算
print(f"平均負傷者数: {df['負傷者数'].mean():.2f}")

人数や件数などの数値列は、あらかじめ整数型に変換しておくと扱いやすくなります。

コード化項目(codebookによるラベル変換)

「天候」「道路形状」「事故類型」など、多くの列が数値コードで管理されています。
これらはファイル定義書やコード表(codebook_2024.pdf)に対応関係が示されています。
分析処理時はコードのままで良いと思いますが、可視化の際はラベルに変換しておくと分かりやすくなります。

# 天候コードをラベルに変換する例
weather_map = {1: "", 2: "", 3: "", 4: "", 5: ""}

df["天候"] = df["天候"].astype(int).map(weather_map).fillna("未定義")

変換辞書を用意して map() 関数で対応づけることで、数値のままでは読めなかった情報を理解しやすくなります。

データ確認の例

ここでは、簡単な地図表示を例にデータの中身を確認してみます。

東京都の事故発生場所をプロットする

import pandas as pd
import folium

# === 度分秒(DMS)形式を10進度に変換する関数 ===
def dms_to_deg(value: int | str) -> float:
    """
    度分秒(DMS)形式を区切りなし整数から10進度に変換
    右5桁: 秒+ミリ秒(SSmmm)
    次の2桁: 分(MM)
    残り: 度(DまたはDDまたはDDD)
    """
    s = str(int(value)).rjust(10, "0")  # 緯度9桁→10桁右詰め補完
    sec = int(s[-5:]) / 1000
    minute = int(s[-7:-5])
    deg = int(s[:-7])
    return deg + minute / 60 + sec / 3600


# データ読み込み
df = pd.read_csv("honhyo_2024.csv", encoding="shift_jis")

# 東京都(都道府県コード30)のデータを抽出
df_tokyo = df[df["都道府県コード"].astype(int) == 30].copy()

# 緯度・経度を10進度に変換
df_tokyo["lat"] = df_tokyo["地点 緯度(北緯)"].apply(dms_to_deg)
df_tokyo["lon"] = df_tokyo["地点 経度(東経)"].apply(dms_to_deg)

# 地図にプロット
m = folium.Map(location=[35.68, 139.76], zoom_start=8)
for _, row in df_tokyo.iterrows():
    folium.CircleMarker(
        [row["lat"], row["lon"]],
        radius=2,
        color="red",
        fill=True,
        fill_opacity=0.5
    ).add_to(m)

m.save("map_preview.html")

b499e85dc84a87e4b591.png

まとめ

交通事故統計オープンデータは、項目数が多く、定義や形式も独特です。
そのままでは扱いづらい部分もありますが、構造を理解して前処理を行えば、
地理的・時間的な分析に活用できる豊富な情報を含んでいます。

この記事では、主に次の点を整理しました。

  • データは本票・補充票・高速票の3構成
  • 都道府県コードや緯度経度など、独自仕様が存在
  • 分析前にデータ型やコード項目を整える必要がある
  • 前処理を終えると、地図や集計による確認が容易になる

警察庁のオープンデータは、行政統計として公開されているにもかかわらず、
内容の粒度が細かく、分析対象としての価値が高いデータです。
正しい構造理解と前処理を行うことで、事故の発生傾向や地域差の分析など、
多角的な交通安全の考察につなげることができます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?