← 網誌 · 交通事故100万件を地図にする:やってみて初めて分かった6つの落とし穴

交通事故100万件を地図にする:やってみて初めて分かった6つの落とし穴

このドメインの3つ目のツール、交通事故ヒートマップを公開しました。データは政府のオープンデータで、落として使うだけに見えます。実際にやってみて間違えた6か所の記録です。

2026-08-20 作者 William Hsu

這篇講什麼
  1. データの中身
  2. 一、1行は1件の事故ではなく1人の当事者
  3. 二、台湾最大の事故ホットスポットは新北市役所の玄関先
  4. 三、このデータで「最も危険な交差点」は決められない
  5. 四、少し動かすたびに画面全体を取り直す
  6. 五、データベースをバインドマウントに置いたら74倍遅い
  7. 六、色を2回間違えた。1回目は真っ赤、2回目は真っ青
  8. 拡大すると点が規則的な格子に並ぶ
  9. 振り返り

データの中身

台湾の内政部警政署は政府オープンデータ基盤に、死傷道路交通事故を1件ずつ公開しています。A1類は24時間以内の死亡、 A2類は負傷または24時間を超えてからの死亡です。2024年と2025年の通年に、2026年8月までの更新分を足して使いました。

ダウンロードはzipで、3年分を展開すると1.5GB。ディスクが足りないので、処理全体をzipから直接読む方式にして、 一度もディスクに落とさないようにしました。

完成したヒートマップ
成果物:101万件の事故。時間帯・車種・死亡のみで絞り込める

一、1行は1件の事故ではなく1人の当事者

事故1件につき、当事者の数だけ行があります。車がバイクにぶつかれば2行で、時刻も場所も緯度経度も同じ、 違うのは当事者の順位だけです。

228万行を重複除去すると101万件、1件あたり平均2.26人。行数をそのまま数えると、どの数字も2倍以上に膨らみます。

二、台湾最大の事故ホットスポットは新北市役所の玄関先

最初にデータを描いたとき、いちばん明るい点は新北市板橋の121.481, 25.009にありました。 そこに年間2千件以上が積み上がっています。

それは新北市政府の座標です。ジオコーディングが失敗すると既定値に戻され、その既定値がその県市の役所の位置なのです。 その点に乗っている事故の住所を調べると、三峽・八里・坪林など20の行政区にまたがっていました。

見つけ方は簡単です。同じ座標の事故住所が3つ以上の行政区にまたがっていれば、その点は偽物。 本物の交差点が三峽と八里に同時に存在することはありません。全国で128か所、17,784件、全体の1.76%が該当しました。

割合は高くありませんが、1点に集中するのでヒートマップ上ではいちばん大きな塊になります。 放置すると、地図でいちばん目立つ情報が間違いということになります。

三、このデータで「最も危険な交差点」は決められない

これはプログラムの話ではなく、データそのものの性質です。

台中市は事故件数が全国最多で年間70,765件。ところが全国のホットスポット上位200件を並べると、 新北市が121件を占め、台中市は19件しかありません。

理由は県市ごとにジオコーディングの挙動が違うからです。同じ座標が使い回される比率は、新北市が1.38、台北市は1.01。 台北市はほぼすべての事故に固有の座標があるので、そもそも「ホットスポット」が形になりません。

このデータで作った危険な交差点ランキングは、危険度ではなく各県市のジオコーディングの癖を並べているだけです。 だからこのツールにはあえてランキングを置かず、範囲統計だけにしています。 300〜500メートルの範囲なら、数十メートルの座標誤差はならされます。

さらに26.2%の住所が「○○路の前0.0メートル」と書かれています。これは道路区間の参照点で、事故が起きた地点ではありません。 街路レベルの判断には使えますが、交差点レベルの結論は出せません。

四、少し動かすたびに画面全体を取り直す

最初のAPIはbboxを受け取る方式でした。フロントが現在の地図範囲を送り、バックエンドがその範囲を返す。 動きはしますが、1ピクセル動かすだけで新しいbbox=新しいURLになるので全部取り直しになり、 しかもブラウザにもCDNにもキャッシュされません。実測で1回の応答が954KBでした。

地図を固定のタイルに切り、URLを /api/cells/{z}/{x}/{y} にしました。URLが固定ならキャッシュでき、 移動しても新しく現れた分だけ取りに行きます。

五、データベースをバインドマウントに置いたら74倍遅い

開発機では何ともないのに、本番に載せたら同じクエリが5.5秒かかりました。

本番のフォルダはWindowsホストからDockerコンテナにバインドマウントしています。SQLiteは小さなランダム読み出しを大量にするので、 そのアクセスパターンはバインドマウント上で目も当てられない遅さになります。同じクエリの3通り:

バインドマウントの性能比較
同じクエリで74倍の差——問題はコードではなく、ファイルの置き場所

解決策は、起動時にデータベースをコンテナ自身のファイルシステムへ順次コピーしてから使うこと。 順次読み出しは速く、122MBでも数秒です。この問題は開発機ではまったく見えず、デプロイして初めて現れます。

六、色を2回間違えた。1回目は真っ赤、2回目は真っ青

ヒートマップの重みは相対値なので、事故件数を0〜1の重みにどう変換するかで地図全体の見え方が決まります。

1回目は真っ赤。APIをbboxからタイルに変えたときに重みの列を落としてしまい、フロントは未設定とみなして 1件のマスも300件のマスも重みを最大にしてしまいました。

2回目は真っ青。97パーセンタイルを満点にしたのですが、この分布は極端に偏っています。 全国表示では中央値が38件、最大のマスが15,601件で400倍の差。97パーセンタイルを分母にすると、 97%のマスの重みがほぼ0になります。

最終的に最大値に対する対数を取る方式にしました。中央値のマスが0.38、500件のマスが0.64、最大が1.0。 これでようやく中間の段階が広がります。

拡大すると点が規則的な格子に並ぶ

街路レベルまで拡大すると点の並びが不自然で、道路と合っていない、という指摘がありました。

マスの中心を描いていたからです。13メートルのマスの中心を描けば、街路レベルでは格子線が見えますし、 道路がすべてのマスの中心を通るはずもありません。

データベースを作り直し、各マスに含まれる事故の緯度経度の合計も保存して、 クエリ時にそのマスの実際の重心を返すようにしました。今は点が道路の上に乗り、道路網に沿って分布します。

振り返り

6つのうち技術的な問題は4番目と5番目だけで、残りは「データはきれいだろう」という思い込みから来ています。 政府のオープンデータは実のところ品質が良く、項目も細かく更新も熱心です。ただし行政のために設計されていて、 その用途は地図を描くことではありません。座標は後から補われたもので、補い方には独自の論理があり、 その論理は統計上は無害でも、地図の上では致命的になります。

次にオープンデータを手にしたら、描き始める前にまず、いちばん極端な値がどこから来たのかを見に行くつもりです。

這篇文章寫的是本站實際的實作與量測。工具本身在老照片動起來與變老變年輕,都可以直接試。

← 上一篇把一支影片的生成時間從 80 秒壓到 43 秒:每一個試過的做法與實測數字

看其他文章