「朝の通勤時間帯はオフィス街、夜は飲み屋街」── 街は時間で表情を変えます。人流データ(個人を特定しない匿名集計データ)を の地図に重ねて、時間帯ごとに動くヒートマップを作ると、商圏分析・出店判断・防災計画・観光戦略の議論が一気に具体化します。
① 人流データの入手元を知る(NTT・KDDI・Yahoo!・Agoop・V-) ② folium + HeatMapWithTime で時間帯モーション付き地図を作る ③ 商圏分析・出店判断への適用 ④ プライバシー配慮(k-匿名性・公的データ利用)
Step 1: 人流データの入手元
「人流」は誰でもタダで取れるデータではありません。個人情報保護の観点から、事業者が匿名集計したものを買う / 公的サービスから取るのが基本。代表的なソース:
| ソース | 提供形態 | 特徴 | 費用 |
|---|---|---|---|
| V-RESAS(内閣府) | Web ダッシュボード + | コロナ禍以降の人流変化、市区町村単位、無料 | 無料 |
| docomo モバイル空間統計 | ・コンサル契約 | 5G基地局ベース、500m メッシュ、年齢/性別属性付き | 有償(規模により百万〜) |
| KDDI Location Analyzer | ダッシュボード | リアルタイム性高、店舗商圏分析 | 月額課金 |
| Yahoo!(LINE Yahoo)DS.INSIGHT | API・SaaS | 検索ログ+位置情報、観光・商業に強い | 有償 |
| Agoop(Yahoo傘下) | API・SaaS | アプリ収集データ、屋外行動分析 | 有償 |
| Google Mobility Reports | 国別/地域別 CSV | コロナ期に話題、現在は提供終了 | (過去のみ) |
民間人流SaaS は商圏分析の精度が高いですが、最初の探索は V-RESAS で十分。CSV で時間帯別・地点別データが取れるので、folium での可視化練習・社内提案資料の作成にすぐ使えます。
Step 2: HeatMapWithTime で時間帯モーション
folium(Python の地図描画ライブラリ)には HeatMapWithTime プラグインがあり、「時間 t における点群(緯度・経度・重み)」のリストを渡すと、HTML 上でアニメーション再生できる地図ができます。
!pip install -q folium pandas numpy
import foliumfrom folium.plugins import HeatMapWithTimeimport numpy as npimport pandas as pdfrom datetime import datetime# 渋谷駅周辺で「3つの賑わいエリア」を時間帯で切り替える# - オフィス街:朝〜昼に活発(駅北東)# - ランチ街:昼に活発(駅南)# - 飲食街:夜に活発(駅南西、円山町方面)import numpy as np
np.random.seed(0)center_lat, center_lon = 35.6580, 139.7016 # 渋谷駅hours = list(range(24))
# 3 つのエリアの中心座標zones = { "office": {"lat": 35.6605, "lon": 139.7050, "radius": 0.0025}, "lunch": {"lat": 35.6565, "lon": 139.7030, "radius": 0.0020}, "night": {"lat": 35.6575, "lon": 139.6985, "radius": 0.0030},}
# 各時間帯における各エリアの「強度」(0〜1)def zone_intensity(h): # オフィス街:7-19時に活発、ピーク10時 office = max(0, 1 - abs(h - 10) / 6) if 6 <= h <= 19 else 0.05 # ランチ街:11-14時のみ lunch = max(0, 1 - abs(h - 12.5) / 2) if 11 <= h <= 14 else 0.1 # 夜:18-26時(深夜0-2時 = 24-26)に活発、ピーク22時 night_h = h if h >= 18 else h + 24 # 翌日0-2時を 24-26 として扱う night = max(0, 1 - abs(night_h - 22) / 4) if 18 <= night_h <= 26 else 0.05 return {"office": office, "lunch": lunch, "night": night}
# 点群を生成(各時間で位置と強度が変わる)n_per_zone = 120 # 各エリアに撒く点data_by_hour = []for h in hours: intensity = zone_intensity(h) points = [] for zname, z in zones.items(): # このエリアの強度が低ければスキップ w = intensity[zname] if w < 0.05: continue # 強度に比例した点数(少なくとも 5 点) n_pts = int(max(5, n_per_zone * w)) for _ in range(n_pts): r = np.random.exponential(z["radius"]) theta = np.random.uniform(0, 2 * np.pi) lat = z["lat"] + r * np.cos(theta) lon = z["lon"] + r * np.sin(theta) # weight は固定値(大きさで密度を表現するので weight は均一) points.append([lat, lon, 1.0]) data_by_hour.append(points)
# 確認:時間帯ごとの点数for h in [3, 8, 12, 18, 22]: print(f"{h:02d}:00 → {len(data_by_hour[h])} 点")import foliumfrom folium.plugins import HeatMapWithTime
# 地図ベース(OpenStreetMap)m = folium.Map( location=[center_lat, center_lon], zoom_start=15, tiles="OpenStreetMap",)
time_labels = [f"{h:02d}:00" for h in hours]
HeatMapWithTime( data=data_by_hour, index=time_labels, radius=30, auto_play=True, speed_step=0.5, # 再生スピード max_opacity=0.8, min_speed=0.1, max_speed=10, use_local_extrema=False, # 全フレーム共通スケール(時間で「動き」が見える) gradient={0.2: "#1f77b4", 0.4: "#2ca02c", 0.6: "#ffd92f", 0.8: "#ff7f0e", 1.0: "#d62728"},).add_to(m)
# 3 つの賑わい中心マーカー(参考用)for zname, z in zones.items(): folium.CircleMarker( location=[z["lat"], z["lon"]], radius=8, color="black", fill=True, fill_color="white", fill_opacity=0.9, popup={"office": "オフィス街", "lunch": "ランチ街", "night": "飲食街"}[zname], ).add_to(m)
# 凡例 HTML をマップに重ねるlegend_html = """<div style="position: fixed; bottom: 30px; right: 30px; z-index: 9999; background: white; padding: 12px 16px; border: 1px solid #ccc; border-radius: 8px; font-family: system-ui, sans-serif; font-size: 13px; box-shadow: 0 2px 8px rgba(0,0,0,0.15);"> <div style="font-weight: 700; margin-bottom: 6px;">人流密度(色)</div> <div style="display: flex; align-items: center; gap: 4px; margin-bottom: 4px;"> <span style="width:14px; height:14px; background:#1f77b4; display:inline-block;"></span> 低 <span style="width:14px; height:14px; background:#2ca02c; display:inline-block;"></span> <span style="width:14px; height:14px; background:#ffd92f; display:inline-block;"></span> <span style="width:14px; height:14px; background:#ff7f0e; display:inline-block;"></span> <span style="width:14px; height:14px; background:#d62728; display:inline-block;"></span> 高 </div> <div style="border-top: 1px solid #eee; margin-top: 6px; padding-top: 6px; font-size: 12px;"> <div>● オフィス街(北東):朝〜昼が活発</div> <div>● ランチ街(南):12時前後だけ</div> <div>● 飲食街(南西):夜〜深夜が活発</div> </div> <div style="margin-top: 6px; font-size: 11px; color: #888;"> 左下のスライダーで時刻を移動 / ▶︎ で自動再生 </div></div>"""m.get_root().html.add_child(folium.Element(legend_html))
m.save("shibuya_pulse.html")m # Colabならインライン表示朝7-10時 はオフィス街(地図右上)が真っ赤、12-13時 はランチ街(中央南)に移動、21-23時 は飲食街(左下)に大きく移る、という具合にヒートの中心が地理的に動くのが見えれば成功です。`auto_play=True` で開きますが、動かない場合は左下の ▶︎ ボタンを押してください(ブラウザの自動再生制限により、初回はクリック必要なことがあります)。
ブラウザに 24 時間分のヒートマップが現れ、自動再生されます。朝7時にオフィス街が赤く光り、12時にランチ街、21時に飲食街が点灯する様子が、地図上で時間とともに動きます。経営会議で見せると「街の鼓動が見える」と評価されます。
Step 3: 実データへの差し替え(V-RESAS の例)
上記はサンプルデータですが、実データは V-RESAS のCSVをそのまま読み込んで同じ形に整形すれば置き換え可能。
# V-RESAS の人流CSVを想定(メッシュID・時間帯・人口)# 実際のCSVは https://v-resas.go.jp/ からダウンロードdf = pd.read_csv("v-resas_shibuya_24h.csv")# 想定カラム: mesh_id, latitude, longitude, hour, count
data_by_hour = []for h in range(24): sub = df[df["hour"] == h] points = sub[["latitude", "longitude", "count"]].values.tolist() data_by_hour.append(points)
# ↑ そのまま HeatMapWithTime に渡せる形Step 4: ビジネス応用
- 出店候補地の比較:候補地A・Bの周辺500m圏で、「ターゲット時間帯」(例:飲食店なら18〜22時)の人流ボリュームを比較。データドリブンな立地評価。
- 既存店のシフト最適化:時間帯別の人流から、ピーク時刻を特定 → スタッフ配置を最適化。
- 広告配信タイミング:駅前のデジタルサイネージで「今この街にいる人」のボリュームに合わせて広告差し替え。
- 観光導線の設計:観光地の「混雑時間帯」を可視化、分散誘導施策の議論材料に。
- 防災計画:地震・火災シナリオで「最も人が多い時間帯」を特定 → 避難ルート設計の根拠。
- 駅の改良工事:ラッシュ時の人流データで、改札増設・通路拡張の優先順位付け。
- 営業時間の見直し:本当に夜遅くまで開けて意味あるか、を時間帯人流で検証。
Step 5: 静止画版(プレゼン資料・印刷用)
アニメは Web/ では映えますが、PowerPoint や にはコマ撮りで貼ると楽。matplotlib で各時間帯の静止画ヒートマップを生成して、4×6 のグリッドにまとめる手法。
import matplotlib.pyplot as pltimport numpy as np
# 6コマで「朝・昼・夕・夜・深夜・明け方」を代表させるkey_hours = [3, 8, 12, 18, 21, 23]fig, axes = plt.subplots(2, 3, figsize=(15, 10))
for ax, h in zip(axes.flatten(), key_hours): points = np.array(data_by_hour[h]) lat, lon, w = points[:, 0], points[:, 1], points[:, 2] sc = ax.scatter(lon, lat, c=w, s=100, cmap="hot_r", alpha=0.7) ax.set_title(f"{h:02d}:00", fontsize=14, fontweight="bold") ax.set_xlim(center_lon - 0.012, center_lon + 0.012) ax.set_ylim(center_lat - 0.012, center_lat + 0.012) ax.set_aspect("equal") ax.set_xticks([]); ax.set_yticks([])
fig.colorbar(sc, ax=axes, label="人流密度", shrink=0.6)fig.suptitle("渋谷駅周辺の1日(時間帯別人流)", fontsize=16, fontweight="bold")plt.savefig("shibuya_24h_grid.png", dpi=150, bbox_inches="tight")plt.show()プライバシーへの配慮(必須)
- k-匿名性: 1メッシュあたり最低 k 人(多くの場合 k=10)以上を集計、それ以下のセルは数値を伏せる
- 個人特定の禁止: 動線追跡を個人レベルでしない(同一人物が朝オフィス街→夜飲食街、を繋ぐような分析は控える)
- データ提供元の規約:商用 SaaS のデータは「自社内分析のみ可」「2次配布禁止」の制約があるケース多し。公開資料に出すときは要事前確認
- 従業員の動線監視に使わない:労働法上の問題が生じる
データ取得の留意点(2026年5月時点)
- OpenStreetMap タイル:folium デフォルトの OSM タイル URL は無料だが、商用大規模では Mapbox / Stadia Maps への切替推奨
- ODbL ライセンス: OSM 由来データを再公開する際は「© OpenStreetMap contributors」明記が必須
- V-RESAS 利用規約: 出典明記で再利用OK、商用OK
- 民間人流データの規約は事業者ごと:必ず個別契約で確認
- データ取得後の保管:ローカル / 自社 にスナップショット、再取得不能に備える
ふくふくの進め方
「街の鼓動を見たい」── V-RESAS の無料データから始めて、必要に応じて民間 SaaS へ拡張するのが定石。① V-RESAS で社内向け Proof of Concept(1週間)→ ② 関係者の合意形成 → ③ 民間データ調達・本番ダッシュボード(1〜2ヶ月)。1〜2ヶ月で「ターゲット街区の時間帯人流ダッシュボード」が動く構成です。お困りなら無料相談へ。
次回予告
EP.14 では、八潮市の陥没事故を題材に、下水道管路の経年データをどこまで公開情報で可視化できるかを実装しながら検証します。「データはある?」という疑問から始める、地下インフラ可視化の現実的アプローチ。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。