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?

救急隊最適配置についてのST_ClusterKmeans,ST_Centroidを利用して、考察する。

Posted at

投稿の動機について

 愛知県内小規模消防本部に勤務する50代半ばの公務員です。
コロナ以降、当本部においても、救急需要が増加しています。
消防本部の管轄を超えて、救急隊の配置を最適化することについて考察しました。
この場を借りて発表したいと思います。

引用データと、その取扱い、使用するソフトについて

 個人の資格で発表しますのでデータはすべて、オープンデータを利用しました。
救急隊が配置されていない、消防署出張所もありますが、
救急隊の配置を動的に変化する 施策を行っている本部もあること、
消防隊が現場に出動する活動もあるため、救急隊が配置されていない
署所もpointとして取り上げます。

平成27年愛知県消防署出張所配置状況

H27aichi_fd.png

算出方法の方針

1. 1kmメッシュ毎に人口予測を行い、救急搬送者予測数を計算する
2. 予測された救急搬送者を、範囲内にポイントとしてランダムプロットする
3. ポイントを内包、管轄する消防本部内の最近傍署所との直線距離を計算する
4. ポイントと最近傍の署所の直線距離を管区を考慮せずに計算する
5. ST_Clusterkmeansを使用し、クラスタリングを行う
6. クラスター毎に重心を計算する ST_Centroid
7. クラスター毎に、重心と内包するポイントを結ぶ直線距離を計算する
8. 既存署所数から数を減らしながら、最適な署所数を判断する
9. クラスター間の偏りを補正する
10. クラスターに対しサブクラスタリングをさらに行う
11. サブクラスターの重心からポイントまでの距離を計算し合計する
12. 3、4,11の距離を比較し、署所配置の見直しの効果を測定する

1 メッシュごとの人口予測と、救急搬送者数

 「将来人口・世帯予測ツールV3」を使用し100mメッシュ毎の人口を予測する。
令和12年の人口予測を基に、データを作成します。管轄する市町村はこの時期に
救急需要のピークを迎えるため選択しました。
管轄する市町村は過疎地域であり、0.5未満は誤差として処理されるため、1kmメッシュ毎にまとめました。

「将来人口・世帯予測ツール」マニュアルの出力仕様を確認。

救急搬送者予測数 = 令和12年5歳階級別人口予測 × 5歳階級別救急搬送率

1kmメッシュ毎に令和12年救急搬送者予測ができました。

2救急搬送者予測をポイントとしてランダムプロットする

救急出場は、道路沿いと想定できるが、市道まで網羅するオープンデータが見つかりませんでした。
精度の低下が予測されるますが、消防本部管轄域と1㎞メッシュポリゴンの交差域にランダムポイントを発生させました。
ST_GeneratePointsは「ポイントを発生させるGeom」、「発生させるポイント数」
の2つの引数を持ちます。

randompoints
SELECT id,
        (ST_Dump(ST_GeneratePoints
                (e.geom,
				(select point_counter from kmesh_qqpoint_counter WHERE id = e.id )))).geom
    FROM kmesh_qqpoint_counter as e;

309,306件の救急出場予測ポイントを得ました。ユニークなidを割り振りなおします。

3 管轄する消防本部内の最も近い署所とポイントの距離を計算しまとめる。

現在、救急出場予測ポイントは管轄消防本部の情報を持ちません。
ST_Withinを使用し、予測ポイントを含む消防本部idを選び抽出します。

ST_Within(point,polygon)
select q.sid,
       r.city_code,
	   r.p17_005,
	   q.geom
	   from qqhansou_point_geom as q
       left join aichi_fd_polygon as r
on ST_Within(q.geom, r.geom) order by city_code;

協定を結ぶ消防本部を除き、管轄を超えて救急出場は行いません。
管轄する消防本部内で最も近い署所を選び、直線距離を計算します。

ST_DistanceSphere
 select 
  distinct on (u.sid)
  u.sid,
  m.id as dept_id,
  u.geom,
  m.name,
  (ST_DistanceSphere(u.geom, m.geom)/1000) AS dist
  from qqhansou_point_geom As u left join aichi_fire_dep As m
  on u.city_code = m.city_code
order by u.sid, 
u.geom <-> m.geom;

救急出場予測ポイントと既存の消防署所をcity_codeでleft_joinします。
<->演算子を使用し、直線距離が短いものから並び替えます。
distinct on で最短のものを選びます。

4 ポイントと最近傍の署所の直線距離を管区を考慮せずに計算する

ST_DistanceSphere2
select 
  distinct on (u.sid)
  u.sid,
  m.id as dept_id,
  u.geom,
  m.name, 
  (ST_DistanceSphere(u.geom, m.geom)/1000) AS dist
  from qqhansou_point_geom As u left join aichi_fire_dep As m
  on ST_DWithin(u.geom, m.geom, 0.3)
order by u.sid, 
u.geom <-> m.geom
;

先のコードとほぼ同じですが、left join の条件を
ST_DWithin(u.geom, m.geom, 0.3)に変更してあります。
数値0.3は随時変更してください。
大きすぎると計算時間がかかり、小さすぎるとjoin出来ずNullとなります。

5 ST_Clusterkmeansを使用し、クラスタリングを行う。

ST_ClusterKMeans
SELECT 
	   sid 
      ,ST_ClusterKMeans(geom, 40 )OVER() AS cluster_id 
      , geom
    FROM qqhansou_point_geom;

6. クラスター毎に重心を計算する ST_Cntroid

ST_Centroid
SELECT
	  cluster_id
     ,ST_Centroid(ST_Union(geom)) as cgeom
	 from cluster_KMeans
	 group by cluster_id;

7. クラスター毎に、重心と内包するポイントを結ぶ直線距離を計算する

ST_DistanceSphere
 select 
     distinct on (u.sid)
     u.cluster_id, m.cgeom, 
     (ST_DistanceSphere(u.geom, m.cgeom)/1000) AS dist  
  from cluster_kMeans As u left join  center_cluster as m
     on ST_DWithin(u.geom, m.cgeom, 1.0)
     order by u.sid,
     u.geom <-> m.cgeom;

8. 既存署所数から数を減らしながら、最適な署所数を判断する。

5,6,7のコードをwithでまとめます。

ST_Centroid_Clusterlocal
WITH cluster_KMeans AS(
    SELECT
	   sid 
      ,ST_ClusterKMeans(geom, 40 )OVER() AS cluster_id 
      , geom
    FROM qqhansou_point_geom
	),
     center_cluster As( 
    SELECT
	  cluster_id
     ,ST_Centroid(ST_Union(geom)) as cgeom
	 from cluster_KMeans
	 group by cluster_id
	 ),
	 cluster_dist As(
  select 
     distinct on (u.sid)
     u.cluster_id, m.cgeom, 
     (ST_DistanceSphere(u.geom, m.cgeom)/1000) AS dist
  
  from cluster_kMeans As u left join  center_cluster as m
     on ST_DWithin(u.geom, m.cgeom, 1.0)
     order by u.sid, u.geom <-> m.cgeom
     ),
	 cluster_count As(
  select
    cluster_id,
    sum(dist) as sum_dist,
    count(cluster_id) as counter
    from cluster_dist
    group by cluster_id
    )
select
   c.cluster_id,
   c.sum_dist,
   c.counter,
   m.cgeom 
 from cluster_count as c left join center_cluster as m
 on c.cluster_id = m.cluster_id   
;

クラスター数の決定には、エルボー法、シルエット法があります。
厳密な計算は難しいので、グラフを作成し、推定します。
クラスター数は40,50周辺で減少が緩やかになります。
今回はクラスター数を40と決めます。

kluster数グラフ.png

9. クラスター間の偏りを補正する

クラスター数を40に定め、8のコードを実行します。
kmeans法はクラスター間の重心距離の総計が最短になるようにクラスタリングを行います。
救急件数272件から18468件、直線総距離1726㎞から47397㎞と大きな偏りがあります。
補正方法として、クラスターをさらにサブクラスターに別けます。

救急隊の基準出場件数を決めます。

愛知県消防年報(令和6年版)データを基にします。
一年間の出場件数419,704を救急隊数248で割り1,692これを基準にします。
cluster40_couter_std.png

表について説明します。

  • cluster_idは40個に分けられたクラスターの番号です
  • sum_distはクラスターの中心からポイントまでの直線距離の合計です
  • counterはクラスター毎のポイント数すなわち救急予想出場件数です
  • cgeomはクラスターの中心の位置情報を表します
  • counter_stdはcounterを基準で割り切り上げたものです

本来はsum_distを基準にクラスターを分割したいのですが、この情報にあたるものがオープンデータに無いため、counter_stdで代用します。サブクラスター数は合計で202となります。

10. 分割されたクラスターをクラスター毎にcounter_std数で分割します。

ST_clusterKmeans_partitionby_cluster_id
SELECT 
	  sid
	, cluster_id  
	, ST_ClusterKMeans(geom,counter_std)over(PARTITION BY cluster_id) AS cluster_sub_id 
	, sum_dist
	, counter
	, dist_std
	, count_std
	, geom
FROM qqhansou_geom_cluster_std;

これは5のコードと似ています。相違点は、
ST_ClusterKMeansの分割数がcounter_stdになっているところ
over()のカッコ内PARTITION by cluster_idとなっているところです。

cluster_idとcluster_sub_idを組み合わせてjoin_idとします。
文字列に変換して||で結合しました。より良い方法がありましたら、教えてください。

11. サブクラスターの重心からポイントまでの距離を計算し合計する。

8のコードに準じて、重心を計算し、join_id毎に現場到着までの直線距離を計算します。

ST_Centroid_subcluster
WITH center_subcluster As( 
    SELECT
	  join_id
     
     ,ST_Centroid(ST_Union(geom)) as cgeom
	 from subcluster202_qqhansou2
	 group by join_id
	 ),
	 subcluster_dist As(
  select 
     distinct on (u.sid)
     u.join_id, m.cgeom, 
     (ST_DistanceSphere(u.geom, m.cgeom)/1000) AS sub_dist
  
  from subcluster202_qqhansou2 As u left join  center_subcluster as m
     on ST_DWithin(u.geom, m.cgeom, 0.2)
     order by u.sid, u.geom <-> m.cgeom
     ),
	 subcluster_count As(
  select
    join_id,
    sum(sub_dist) as sum_sub_dist,
    count(join_id) as sub_counter
    from subcluster_dist
    group by join_id
    )
select
   c.join_id,
   cast(c.sum_sub_dist as numeric(5,1)),
   c.sub_counter,
   m.cgeom 
 from subcluster_count as c left join center_subcluster as m
 on c.join_id = m.join_id
   
;
FROM qqhansou_geom_cluster_std;

ここでjoin_idにより202に分割されたクラスターをボロノイド図で示します。
ポイント近くの数字は、予測される出場件数となります。

voronoi202.png

12. 直線距離総計を比較し、効果を検証する

3 消防本部管轄内最近傍署所と救急出場予測点までの直線距離総計 455046.2km
4 管轄所を越え、最近傍署所から救急出場予測点までの直線距離総計 431481.2㎞
11 クラスターで分割した重心から救急出場予測点までの直線距離総計 400162.4㎞

3,4は既存の消防署所213箇所からの直線距離総計です。
11は202か所からの直線距離です。10パーセント以上の削減効果があります。

検討課題

・より精度の高い検証を行うためには、愛知県下、消防本部の現場到着までの距離情報の提供を受ける必要があります。現場到着までの直線距離は、現場到着までの時間の近似値とみなせます。これを平準化できます。
・ 救急出場発生の可能性が高い、自動車が通行可能な道路のデータを得て、バッファーを発生し予測される救急出場場所をバッファー内に発生させることがきれば、より精度の高い予測となります。

結語

・オープンデータのみを使用したため、精度は不十分かもしれませんが、市区町村の境界を考慮しない署所の配置は、十分な効果があり、検討に値するものです。

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?