OSM 的隱藏陷阱 - 以 λ 指數修正志願者貢獻偏差
本篇要講述的是台中 OSM 建築數量 2018→2025 增長 392%,但其中有多少是真實建設、多少是社群回填?本篇提出一個可量化、可控制修正強度的 λ 偏差修正指數。
關於 OSM 其中的 392% 增長,有多少是真的?
先來檢視台中市 OSM 數據 8 年的原始增長:
| 年份 | OSM 建築物總數 | 設施 | 商店 | 休閒 |
|---|---|---|---|---|
| 2018 | 16,926 | 1,015 | 408 | 30 |
| 2019 | 12,771 | 1,207 | 466 | 32 |
| 2020 | 16,117 | 1,520 | 541 | 39 |
| 2021 | 18,500 | 1,599 | 534 | 47 |
| 2022 | 24,973 | 1,640 | 590 | 49 |
| 2023 | 41,055 | 1,762 | 647 | 57 |
| 2024 | 47,584 | 1,874 | 690 | 59 |
| 2025 | 50,208 | 1,921 | 705 | 61 |
採樣點 500m 範圍內的「平均建築數量」更為顯著——從 2018 年的 27.6 棟,增至 2025 年的 136 棟,+392.1%。
然而其中一個問題隨之而來:
台中市真的在 8 年內新建了比原有存量多 4 倍的建築嗎?
答案顯然是否定的。即便是開發最密集的城市,10 年內新建建築數量也不會超過存量的 1 倍。這 392% 中,相當大一部分其實是「OSM 社群志願者持續將既有建築數位化補登」,也就是「歷史回填」(backfilling)效應。
量化「回填效應」:社群貢獻量代理變數
若某城市/地區 2018 年 OSM 共有 1 萬個元素(建築 + 設施 + 商店 + 休閒),2025 年增至 5 萬,則社群整體活躍度約提升 5 倍。建築數的增長,有相當一部分是被這個系統性的「社群更積極補登」推高的。
我們在這邊定義「社群貢獻量代理」C_t:
C_t = building_count_t + amenity_count_t + shop_count_t + leisure_count_t
將所有有測量目標的標籤加總,作為志願者整體投入程度的代理變數。
# osm/bias_correction.py:37-54
def compute_total_elements(osm_metrics: pd.DataFrame) -> pd.Series:
"""
計算每年 OSM 快照的「總元素數」(社群貢獻量代理)。
"""
count_cols = [c for c in ["building_count", "amenity_count", "shop_count", "leisure_count"]
if c in osm_metrics.columns]
osm_metrics = osm_metrics.copy()
osm_metrics["total_elements"] = osm_metrics[count_cols].sum(axis=1)
return osm_metrics.groupby("year")["total_elements"].mean()
台中 8 年的 C_t(每採樣點 500m 範圍內均值):
| 年份 | C_t(總元素均值) |
|---|---|
| 2018 | 34.6 |
| 2021 | 57.1 |
| 2025 | 146.4 |
C_2025 / C_2018 ≈ 4.23,社群貢獻量翻了逾四倍。
核心假設:社群貢獻量增長與真實建設增長是兩個獨立過程。OSM 元素總數的增長是兩者的疊加,而研究者真正關心的是後者。
修正公式
定義第 t 年的「校正後增長指數」:
G_t_corr = (N_t / N_0) × (C_0 / C_t)^λ
各符號意義:
| 符號 | 意義 |
|---|---|
N_t / N_0 |
原始增長率(基準年 N_0 = 2018) |
(C_0 / C_t) |
社群貢獻量的衰減倒數(C 愈大,此因子愈小) |
λ ∈ [0, 1] |
修正強度,由分析者根據研究假設設定 |
三種直覺解讀:
- λ = 0:完全不修正 →
G_corr = N_t / N_0,還原為原始指數 - λ = 1:完全比例修正 → 假設所有 OSM 元素增長均來自社群活躍度提升
- λ = 0.5:中度修正,假設增長中一半來自真實建設、一半來自社群回填
# osm/bias_correction.py:80-97
v0 = series[base_year]
c0 = community_proxy[base_year]
for year in sorted(series.index):
v_t = series[year]
c_t = community_proxy.get(year, c0)
raw_growth = v_t / v0 if v0 > 0 else np.nan
# 社群貢獻量修正因子 = (C_t0 / C_t)^λ
if c0 > 0 and c_t > 0:
correction_factor = (c0 / c_t) ** lambda_
else:
correction_factor = 1.0
corrected_growth = raw_growth * correction_factor
將 λ 套用至台中數據
採用建議值 λ = 0.5:
| 年份 | 建築均值 | 原始增長 | 修正因子 (C_0/C_t)^0.5 | 修正後增長 |
|---|---|---|---|---|
| 2018 | 27.6 | +0.0% | 1.000 | +0.0% |
| 2019 | 31.0 | +12.3% | 0.943 | +5.9% |
| 2020 | 34.8 | +26.1% | 0.901 | +13.6% |
| 2021 | 47.6 | +72.5% | 0.778 | +34.2% |
| 2022 | 67.8 | +145.7% | 0.692 | +70.0% |
| 2023 | 109.8 | +297.8% | 0.582 | +131.1% |
| 2024 | 131.3 | +375.7% | 0.508 | +141.7% |
| 2025 | 136.0 | +392.8% | 0.486 | +139.6% |
392% 被壓縮至 140%,較接近一個快速發展城市的合理增速範圍。
原始指數顯示「8 年新增了 4 倍建築」,修正後顯示「8 年新增了 1.4 倍建築」——後者更符合人類認知與官方建照數據的數量級。
λ 靈敏度分析
λ 是無法從資料本身唯一決定的參數,因此靈敏度分析是不可省略的步驟——它能讓讀者清楚看見「不同假設下結論差距有多大」。
# osm/bias_correction.py:111-143
def sensitivity_analysis(series, community_proxy, base_year=BASE_YEAR, lambdas=None):
if lambdas is None:
lambdas = [0.0, 0.25, 0.5, 0.75, 1.0]
final_year = sorted(series.index)[-1]
rows = []
for lam in lambdas:
df = bias_correction_index(series, community_proxy, base_year, lam)
final_row = df[df["year"] == final_year].iloc[0]
rows.append({
"lambda": lam,
"corrected_growth_index": final_row["corrected_growth_index"],
"implied_real_growth_pct": (final_row["corrected_growth_index"] - 1) * 100,
})
return pd.DataFrame(rows)
台中 2018→2025 建築增長在不同 λ 值下的結果:
| λ | 修正後增長 |
|---|---|
| 0.00 | +393%(不修正) |
| 0.25 | +234% |
| 0.50 | +140%(中度修正,建議值) |
| 0.75 | +73% |
| 1.00 | +17%(完全比例修正) |
只是在這情況下,下列兩種解讀都不合適:
- λ = 0 派:「OSM 增長即真實建設增長」→ 4 倍增長,呈現泡沫式擴張
- λ = 1 派:「OSM 增長完全等比例反映社群活躍度」→ 17%,幾乎零成長
兩者皆不準確,真實情況必然介於兩者之間,靈敏度分析的價值在於:明確告知讀者「結論在 λ ∈ [0.25, 0.75] 範圍內均成立」,比直接拋出單一數字嚴謹得多。
後面我們將繼續講解關於 λ 值得其他參考計算方式,這本篇中著墨在城市 OSM 數據跟志願者貢獻偏之間的關係計算。