月曜日, 6月 23, 2025
月曜日, 6月 23, 2025
- Advertisment -
ホームニューステックニュースサポーター地理分布可視化を支える技術 — Looker Studio奮闘記 #PostgreSQL

サポーター地理分布可視化を支える技術 — Looker Studio奮闘記 #PostgreSQL



サポーター地理分布可視化を支える技術 — Looker Studio奮闘記 #PostgreSQL

image.png

チームみらいではサポーターが日本全国にどのように分布しているかをLooker Studioを用いたダッシュボードで公開しています。これがどのような仕組みで動いているのかを解説します。この記事はチームみらいの技術的な取り組みを共有するものであり、投票を呼びかけるものではありません。

全国の分布を見たかった

このシステムはもともと神奈川県選挙区総支部長の河合みちおさんと、チームみらいアクションボードをバリバリ開発しているKenta Muraiさんとが作っていたものらしいです。僕はLooker Studioという言葉を聞いたこともなかったのですが、ものができてから眺めていじって「これは面白いな!」となりました。

その中で一つ不満点があったのはこれ:

image.png

面白い可視化なのだけど妻が北海道出身の僕としては北海道なども含めて全体を見たいなと思いました。じゃあズームアウトすればいいじゃないかと思いましたか?やってみましょう

image.png

枠線が太すぎる!!!

これを何とかしたい気持ちがLooker Studioを色々試してみる原動力になりました。

当初の仕組み

Muraiさんから色々話を聞いていて興味深かったのはデータの集め方と持ち方でした。詳細に住所の情報をとると個人情報としての扱いが必要になって運営の負担が増えるし、そもそも何かを郵送するわけではないので詳細な住所が必要ではない。しかし東京なのか福岡なのかなどの「大体どこにいるのか」の情報は欲しい。このニーズを満たす方法として「郵便番号だけとる」という解決が行われていました。

初期の構成は下記のようになっていました

  • Google Formでボランティア登録
  • レスポンスデータがGoogle Spreadsheetに入る
  • Google App Scriptでzipcloudの郵便番号検索APIを叩いて郵便番号を粗い住所に変える
  • Looker StudioがそのSpreadsheetを読んで可視化する

image.png

悪戦苦闘

SpreadsheetをLooker Studioで読んだ時に、デフォルトでは住所は文字列型のままなので、それを地域の型に変換してやらないとマップ集計ができないことにまずつまずきました。
image.png

また、領域塗り分けの線は上記の図ですでに最小の1pxであり、これ以上細くはできないこともわかりました。枠線を消すことはできます。でもそれだと色が薄い地域は見づらくなってしまいます。

色々試行錯誤して、最終的に「領域の境界や人数による色の濃淡変化は今回やりたい可視化には必要ではない」と判断し、冒頭のバブルチャートを採用しました。

再掲:
image.png

余談ですが、最初はチームみらいの色のミントグリーンにしていて、色覚特性によっては見づらいという報告があり、デフォルトの色に戻しました。色は素人考えでカスタマイズすべきではないですね。

仕様の変更!!

チームみらいアクションボードの話は実は伏線でした!!

チームみらいアクションボードは、サポーターの人たちがいろいろな活動をRPGのレベル上げみたいゲーム感覚で楽しめるシステムです。現時点で1200+人が登録しています。私は「日本初の規模で政治をゲーミフィケーションする社会実験が行われている状態」と思っています。この最初の社会実験に参加できる機会はあと1ヶ月くらいです: 登録はこちら

IMG_5786.jpeg

こうなると次に何が起こるでしょうか?シニアエンジニアなら勘付いた人も多いことでしょう。

「Google Formで登録した人とチームみらいアクションボードで登録した人のどちらをサポーターとして集計すべきなのでしょうか?」

結論「どちらの入口を好むかは人によって異なるので両方維持したい」ということになり、集計値としては「重複を排除して合算したい」ということになりました。つまりこういうこと:

image.png

うん?これ、結構めんどくさい要求仕様ですよね?(苦笑)

Supabaseに入ってるデータを取り出して重複除去するところまで全部Google App Scriptで扱うのは少し荷が重そう。そう考えてデータ分析パイプラインをまるっとGitHub Actionsに移行しました。

新しい構成:

  • (A): Supabaseのauth.usersと郵便番号を含むテーブルをJOINして最小限の出力を返すPostgres関数を作り、Remote Procedure Callsで呼ぶ
  • (B): Google CloudのサービスアカウントGoogle Spreadsheet APIを呼ぶ(対象Spreadsheetが公開のものではないため、自動化用のアカウントを作り、閲覧権限者に追加する必要がある)
  • (C): マージ処理「共通のメールアドレスのものが両方にある場合、created_atが新しいものだけを残す」をする
  • (D): マージ結果を新しいSpreadsheetに書く

図解するとこうです:
image.png

やった!合算したグラフが出せました!めでたしめでたし!
image.png

できたと思ってからが長い

めでたしめでたし!で終わったら世の中幸せなんですけど、そうはいかないのが世の真実。切り替えの数日前には作業は完了したが、いざ本番切り替えしてアナウンスすると途端に問題に気づくもんです……。

大阪市に穴が空いている問題

image.png

この可視化の何がおかしいかわかりますか?中央のデータがない穴の領域には大阪市があります。大阪市のサポーター数が0人とは考えにくい。分析パイプラインをGoogle App ScriptsからGitHub Actions上のPythonへと切り替えたタイミングで何か挙動が変わってしまっているようです。

本当は移行前に「プロセスの変更によって従来のデータが変化しないか」を、データを通し直して検証してみるべきでしたね。迂闊でした。まあAIエージェントのDevinに「大阪市周りで何が変わったのか調べて」と言ったらあっさり特定してくれたので結果オーライとしましょう。

Devinの報告

大阪市関連「地方行政区画(第2レベル)」データ比較結果
両スプレッドシートのデータを比較した結果、明確な相違が確認されました。
主要な相違点:

  • 現在のスプレッドシート: 大阪市・堺市の区レベル詳細データ(例: 大阪府大阪市中央区、大阪府堺市北区)
  • 従来のスプレッドシート: 市レベルのデータ(例: 大阪府大阪市、大阪府堺市)

これは、現在のコードが郵便番号からより詳細な行政区画情報を取得しているためと考えられます。現在のデータはより精密で正確です。

現在のデータはより精密で正確です……。この報告をChatGPT o3に渡すと正解に辿り着きます。

ChatGPTの回答

Looker Studio には「地方行政区画(第 2 レベル)」=市区町村レベルのジオタイプが用意されていますが、政令指定都市の“区”まではポリゴンが用意されていません(例外は東京 23 区のみ)。

そう、Looker Studioの市区町村可視化は「東京都23区では区まで含める必要があるが、他の市では区を含めてはいけない」という仕様なんですね。ここを修正して無事大阪市も表示されるようになりました。

image.png
やっぱり大阪市が一番色濃いですね。

郵便番号APIが止まる問題

アクションボード由来で数百人分の新しいデータが増えました。これを処理するとエラーがたくさん… たぶん郵便番号検索APIが一定時間ウィンドウ内で100件までに制限されてそうだなと思いました。数百件を一度に処理すると、高頻度アクセスすぎて止められてしまうのでしょう。

この件は、そもそもGitHub ActionsでやるならAPIに頼らなくてもいいなと考えなおしました。郵政公社から元データCSVをダウンロードして自前で郵便番号を住所に変換する実装に変更しました。

高頻度実行したらデータが壊れている報告が……

image.png

データが壊れていると言う報告があり、確認すると壊れていない、と言う問題。でもスクリーンショットという動かぬ証拠があるので、実際に壊れているタイミングがあったことがわかります。

すでにあるデータを削除してからSpreadsheetに書き込むところに10秒程度かかっていて、運悪くそのタイミングでLooker Studioが読みにくるとデータがないので壊れた表示になる、という現象でした。Looker Studioはデフォルトで15分間隔でデータを読みます。当初は元データの側を1日1回更新にしていましたが、せっかく盛り上がってきたからとGitHub Actionsの側も15分間隔にしたので、運悪く壊れる確率が96倍に?

Spreadsheetに書かれているものを全部消してから、マージした新しいデータを全部書く仕組みだったのをやめました。かわりに、Spreadsheetに現在書かれているデータを読んで、手元のデータとの差分だけを追記するようにしました。これで更新にかかる時間を大幅削減!データが消えている時間もないので安心!

翌日「データが増えている」という報告が……

image.png

ぎゃー、エンジニアからは「処理がバグっているのでは」という指摘が飛んでくる反面、コミュニティマネージャーは数値が正しいと思い込んで大喜び!待って!SNSアナウンスしないで!状況確認します!

Devinの報告
image.png

なるほど、マイクロ秒の桁数かー。o3による解説:

PostgreSQL では内部的に「秒+ 0–6 桁の小数部」で時刻を保持しますが、文字列にする段階で末尾の 0 を自動的に削るため、たとえば
2025-06-21 15:56:20.123450+09 → 2025-06-21 15:56:20.12345+09
のように「5 桁マイクロ秒」が現れます。

つまり10%の確率で末尾が0になり、Supabaseが内部で使っているPostgreSQLはその時に0を削って5桁で返してくる。Python側がそれをケアしないでいるとパースエラーになり、Devinくんが親切に「エラーが起きても無視して続行」のコードにしていたのでそのまま実行完了して、1割重複したデータが書かれてしまう、という現象でした。

まとめ

出来事がたくさんありすぎて後半はかなり駆け足になりましたが、チームみらいによるLooker Studioを用いたダッシュボードでの地理情報可視化の裏側の仕組みを公開しました。

これらのコードは都知事選の時と同じように落ち着いてからOSS公開しようかなと思っています。実はパイプライン切り替えから実は1週間しか経っておらずコードが高速に変化していっているのが理由の一つ。扱うデータの性質的に今の段階では複数人でオープンソース開発する感じではないよなと言うのがもう一つの理由です。

一方でアクションボードの方はオープンソース開発が日々ガンガン行われています。登録はこちらリポジトリはこちら。日々平均20~30件のコミットがあるhotなプロジェクトです。興味があれば眺めてみてください。このグラフもチームみらいのLooker Studioダッシュボードで見ることができます。
image.png





Source link

Views: 0

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -