Zenn
Open13

適当GTFS

YamakyuYamakyu

DBSCANなるものがあるらしい。
各点の指定距離内の点をどんどん繋げて、
いっていい感じに点群をまとめるやつでKmeansとかよりもいい感じに分けられる感じらしい。
我らがpostgisにはst_clusterdbscan()がある。

YamakyuYamakyu

私は現在GTFSの解析をpostgresqlでやっている。
stopsテーブルが基本的に標柱で分けられている。
標柱はバスの止まる位置のことで、異なるフィード、バスの方面ごとにデータがinsertされてる。
これでは案内上不便極まりないため、複数の案内上は多分一緒にすべき標柱を探し出し、まとめねばならない。

YamakyuYamakyu

仕様的には、
(
完全同名の停留所で近くにあるやつ(同名停留所対策(新宿(しんじゅく・にいじゅく)郵便局)) or
とても近くにある標柱(会社ごとの表記揺れ対策(堀ノ内と堀の内、渋谷駅前と渋谷駅))
) and あらかじめ用意した各停留所の連続停車リストにない
でDBSCANを回す。

YamakyuYamakyu

wikiを見る。wikiって呼ぶな!
https://ja.wikipedia.org/wiki/DBSCAN

DBSCAN(D, eps, MinPts) {
   C = 0
   for each point P in dataset D {
      if P is visited
         continue next point
      mark P as visited
      NeighborPts = regionQuery(P, eps)
      if sizeof(NeighborPts) < MinPts
         mark P as NOISE
      else {
         C = next cluster
         expandCluster(P, NeighborPts, C, eps, MinPts)
      }
   }
}

expandCluster(P, NeighborPts, C, eps, MinPts) {
   add P to cluster C
   for each point P' in NeighborPts { 
      if P' is not visited {
         mark P' as visited
         NeighborPts' = regionQuery(P', eps)
         if sizeof(NeighborPts') >= MinPts
            NeighborPts = NeighborPts joined with NeighborPts'
      }
      if P' is not yet member of any cluster
         add P' to cluster C
   }
}

regionQuery(P, eps)
   return all points within P's eps-neighborhood (including P)

この疑似コードに沿って作ればいいのか。

YamakyuYamakyu

expandCluster()for inかけながらNeigborPtsが追加されてる。
plpgsqlで書く場合、最初にデータが確定するし、どうしたらいいんだ。

...

Github Copilot に丸投げしよう!

YamakyuYamakyu

o1すごい!!完璧にコード出してきた!!人間いらないじゃん

仮テーブル作ってwhile(テーブルの中身が無くなるまで)の中でfor inかける二重ループを使いなさいとのこと。なるほどこれは驚いたけど確かに動く...。

コードをさっきの要件に合うように調整して、動かす。

YamakyuYamakyu

share memoryなるものが足りないと言い出した。
transaction中にテーブルロックをかける時のデータ量の上限の問題らしい。
仮テーブルを作る部分を外に出してついでにいらないテーブル少し消して、うごかす。

YamakyuYamakyu

注意すべき停留所リスト

  • 堀の内・堀ノ内(杉並区)
    京王がひらがな、都バスがカタカナ。都バスの北行きがなぜか二つに分かれてるのがポイント。
  • 新宿郵便局(新宿区・葛飾区)
    葛飾のは、にいじゅく。
    都バスの同名停留所は新宿一丁目、稲荷神社前、あと一つある。(適当DB操作調べ)(忘れた)
  • 渋谷駅・渋谷駅前(渋谷区)
    都バスには'前'をつけたがる性質がある。
  • 高円寺陸橋
    各方面のバス停が離れていて、京王と都が乗り入れるので、ここをどう処理するかがバス停まとめ道の難関。
  • 新宿西口周辺(新宿区)
    バス停が多く、分散していて、CH01(都庁循環)がこまめに止まるのでこの辺が鬼門。
  • 布田一丁目・電通大通り(調布市)
    調布駅北口方面がまさかの1標柱in2名前。お得。(ストリートビューより)
    詳細は文書で説明できる気がしないので省くが、甲州街道の八王子方に布田一丁目を設置できなかったことによる、調布市ミニバスと京王バスの停留所位置の違いが悪さをしている模様。ここが最難関。
  • 中杉通り(杉並区)
    京王のすぎ丸が停車し、都バスと京王の共同運行路線の渋66は止まらないので扱いが難しい。
YamakyuYamakyu

バス停をまとめられたので、次はバス停間を系統名、曜日でカウント。曜日を変換するやつは前に作った。ここでは土日祝平の4モードで区分。

YamakyuYamakyu

とりあえず比で二点間のパスをずらす。leastとgreatestでバス停をまとめると向きの問題が発生するようなので角度で場合分け。のちにrow_numberとcountで線の位置を中央に補正。y字の合流が鋭角の時はかさなってみえない問題が発生する。このアプローチはダメそう。

YamakyuYamakyu

バス停間パスを一つずつplpgsqlかなんかで入れて行ってその度にバッファ距離に入るものは修正していくアプローチになるかな。この場合は同じ系統のくせに分岐が発生する線をどう処理するかがポイント。

YamakyuYamakyu

そもそも名前で分けると深夜系統が悪さをするし、ほぼ同じ経路を通る路線もまとめたい。まず似たような系統まとめからかもしれない。

YamakyuYamakyu

一パターン毎にdwithinして重なった線毎にうまいことoffsetもしくは同一系統のようだったら重ねるしてかな。

作成者以外のコメントは許可されていません