EP.35 で に触れましたが、この回は面への割り当てに絞って扱います。
「この店舗はどの区にあるか」「この地点はどの商圏に入るか」。単純に見えて、つまずく箇所が決まっています。
1. 最初に確認する3つ
実務では、処理を書く前に確認すべきことがあります。ここを飛ばすと、静かに誤った結果が出ます。
| 確認事項 | 外すとどうなるか |
|---|---|
| 座標系が揃っているか | 境界付近で誤った割り当てになる |
| 面が重なっていないか | 1つの点が複数に割り当てられる |
| 面に隙間がないか | どこにも割り当てられない点が出る |
1行目が最も多い落とし穴です。同じ「緯度経度」でも、基準が違えば実際の位置がずれます。日本国内で使われる基準の間でも、数百メートルの差が出ることがあります。
座標系が違っても、数値としては有効なので処理は通ります。結果として、境界付近の点だけが誤って割り当てられる。全体の件数は合っているので、気づきにくい。前処理の現場 EP.22 で扱った「静かに壊れる」の典型例です。
2. 座標系を揃える
を確認し、明示的に揃えます。「たぶん同じだろう」で進めないでください。
import geopandas as gpd
points = gpd.read_file("stores.geojson")areas = gpd.read_file("admin_areas.geojson")
# 1. まず確認する。None なら定義されていない(要注意)print(f"点の座標系: {points.crs}")print(f"面の座標系: {areas.crs}")
if points.crs is None or areas.crs is None: raise ValueError("座標系が定義されていません。元データの仕様を確認してください")
# 2. 揃える(面の側に合わせる例)if points.crs != areas.crs: print(f"揃えます: {points.crs} → {areas.crs}") points = points.to_crs(areas.crs)
# 3. 揃ったことを確認assert points.crs == areas.crs, "座標系が揃っていません"座標系が `None`(未定義)の場合を弾いているのが要点です。未定義のまま処理すると、変換されないまま重ねられます。元データの仕様を確認して、明示的に設定してください。
なお、距離や面積を計算する場合は別の変換が要ります。緯度経度のままでは正しく計算できません。その地域に適した座標系へ変換してから計算してください。緯度経度のまま距離を計算すると、緯度によって1度あたりの実距離が変わるため、南北と東西で尺度が食い違います。
3. 割り当ての実行と確認
座標系が揃ったら割り当てます。重要なのは、実行後の確認です。
import geopandas as gpd
def assign_area(points, areas, area_key="area_name"): """点を面に割り当て、取りこぼしと重複を確認する。""" before = len(points)
joined = gpd.sjoin(points, areas[[area_key, "geometry"]], how="left", predicate="within")
# 1. どこにも割り当てられなかった点 unassigned = joined[joined[area_key].isna()]
# 2. 複数に割り当てられた点(面が重なっている) dup_ids = joined.index[joined.index.duplicated()].unique()
print(f" 入力 {before:,} 件 → 結果 {len(joined):,} 件") print(f" 割り当てなし: {len(unassigned):,} 件 " f"({len(unassigned) / before:.1%})") print(f" 複数に割り当て: {len(dup_ids):,} 件")
if len(joined) != before: print(" ※ 件数が変わっています。面の重なりを確認してください")
return joined, unassigned, dup_ids件数が変わっていないかを確認するのが最重要です。面が重なっていると、1つの点が複数行に増えます。そのまま集計すると、同じ点が二重に数えられます。
これは データ基盤トラブル事件簿 EP.07 で扱った結合の多重度と同じ問題です。空間結合でも、件数の確認は必須です。
4. 割り当てられない点を調べる
どこにも割り当てられなかった点は、必ず中身を確認してください。原因によって対処が変わります。
| 原因 | 確認方法 | 対処 |
|---|---|---|
| 座標が範囲外 | 緯度経度の値を見る | 元データの誤りを修正 |
| 面に隙間がある | 近くの面との距離を測る | 最も近い面に割り当てる |
| 海上・境界外 | 地図に重ねて見る | 正当な除外 |
| 座標系が違ったまま | ずれの傾向を見る | 変換を確認 |
| 面のデータが古い | 合併などの履歴を確認 | 面のデータを更新 |
4行目が疑わしい場合、ずれ方に規則性があります。全部が同じ方向に一定距離ずれているなら、座標系の問題です。ばらばらにずれているなら、元データの誤りの可能性が高い。
import geopandas as gpd
def assign_nearest(unassigned, areas, area_key="area_name", max_distance=100.0): """最も近い面に割り当てる。ただし距離の上限を設ける。
距離を扱うので、事前に適切な座標系へ変換しておくこと。 """ if unassigned.empty: return unassigned
result = gpd.sjoin_nearest( unassigned, areas[[area_key, "geometry"]], how="left", max_distance=max_distance, distance_col="distance_m", )
still_none = result[result[area_key].isna()] print(f" 最寄りに割り当て: {len(result) - len(still_none):,} 件") print(f" それでも割り当て不可: {len(still_none):,} 件") if not result.empty and "distance_m" in result: print(f" 割り当てた距離の最大: {result['distance_m'].max():.1f} m")
return result距離の上限を設けるのが要点です。上限なしで最寄りに割り当てると、遠く離れた場所に割り当てられます。海上の点が数キロ先の区に入る、といったことが起きます。上限を超えたものは割り当てられないまま残すほうが、誤った割り当てより誠実です。
「面の中にあったから割り当てた」のか「最寄りだから割り当てた」のかを、列として残してください。前処理の現場 EP.17 で扱った「推定値であることを保つ」と同じ考え方です。後から精度を判断できます。
5. 境界上の点
ちょうど境界線上にある点の扱いは、実装によって挙動が異なります。件数は少ないものの、再現性に影響します。
- 判定の方法によって結果が変わる — 内包の定義が実装で異なる
- 両方に含まれることがある — 隣接する2つの面の境界
- どちらにも含まれないことがある — わずかな隙間
- 座標の丸め方で変わる — 小数点以下の桁数
実務的な対処は、件数を確認して、少なければ個別に決めることです。数件なら手作業でも構いません。多いなら、面のデータ側に問題があります(隙間や重なり)。 などで配布されている境界データは、提供元によって精度が異なるため、複数の候補があるなら比べてみる価値があります。
そして、同じ処理を再実行したときに同じ結果になるかを確認してください。境界上の点の扱いが不安定だと、実行のたびに結果が変わります。
6. 手順の整理
実務の手順として、まとめます。確認が処理の大半です。
- 1座標系を確認し、明示的に揃える — 未定義なら止める
- 2面の重なりと隙間を確認する — 事前に把握
- 3割り当てを実行する
- 4件数が変わっていないか確認 — 増えていれば重なり
- 5割り当てられなかった点を調べる — 原因ごとに対処
- 6割り当て方を記録に残す — 内包か、最寄りか
1・2・4・5 が確認で、実際の処理は3だけです。この比率が、空間結合の実態をよく表しています。処理は1行でも、確認しなければ結果を信じられません。そして確認を省いてもエラーは出ないため、省略しても気づかない ── という構造も、この領域の厄介さです。
座標系の不一致は、エラーにならず境界付近だけ誤る(最も気づきにくい)。処理後は件数が変わっていないかを必ず確認する(面の重なりで二重に数える)。割り当てられない点は原因を調べてから対処し、最寄りに割り当てるなら距離の上限を設ける。そして内包で割り当てたか、最寄りで割り当てたかを記録に残してください。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。