[
  {
    "model_version": "typology-2026.09.26-bbb4441b",
    "task": "typology",
    "title": "District typology and structural peers",
    "method": "Standardised features -> PCA (85% variance) -> k-means (n_init=50, seed 42); k chosen by silhouette over [6, 7, 8, 9]; peers are the 5 nearest districts in PCA space; names assigned by published rules on centroid z-scores.",
    "features": [
      {
        "name": "median income",
        "indicator": "income_median",
        "transform": "log",
        "period_cap": null
      },
      {
        "name": "GDP per capita",
        "indicator": "gdp_per_capita",
        "transform": "log",
        "period_cap": null
      },
      {
        "name": "agriculture share",
        "indicator": "share_agriculture",
        "transform": "sqrt",
        "period_cap": 2020
      },
      {
        "name": "mining share",
        "indicator": "share_mining",
        "transform": "sqrt",
        "period_cap": 2020
      },
      {
        "name": "manufacturing share",
        "indicator": "share_manufacturing",
        "transform": "sqrt",
        "period_cap": 2020
      },
      {
        "name": "services share",
        "indicator": "share_services",
        "transform": "sqrt",
        "period_cap": 2020
      },
      {
        "name": "population density",
        "indicator": "pop_density",
        "transform": "log",
        "period_cap": 2020
      },
      {
        "name": "share aged 65+",
        "indicator": "share_age_65plus",
        "transform": null,
        "period_cap": 2020
      },
      {
        "name": "labour force participation",
        "indicator": "lfpr",
        "transform": null,
        "period_cap": null
      },
      {
        "name": "piped-water access",
        "indicator": "access_piped_water",
        "transform": null,
        "period_cap": null
      }
    ],
    "training_units": 159,
    "k": 6,
    "silhouette": {
      "6": 0.213856,
      "7": 0.194549,
      "8": 0.187882,
      "9": 0.186314
    },
    "pca_components": 6,
    "explained_variance": [
      0.391289,
      0.174441,
      0.107462,
      0.094478,
      0.070456,
      0.059841
    ],
    "clusters": [
      {
        "cluster_id": 0,
        "name": "Ageing agricultural heartland",
        "size": 21,
        "sabah_members": [],
        "profile": {
          "median income": -0.713394,
          "GDP per capita": -0.117175,
          "agriculture share": 0.881353,
          "mining share": -0.537129,
          "manufacturing share": -0.65813,
          "services share": -0.564572,
          "population density": -0.561115,
          "share aged 65+": 1.312118,
          "labour force participation": -0.605566,
          "piped-water access": 0.197218
        }
      },
      {
        "cluster_id": 1,
        "name": "Metropolitan & industrial core",
        "size": 29,
        "sabah_members": [
          "sbh-kota-kinabalu"
        ],
        "profile": {
          "median income": 1.451224,
          "GDP per capita": 1.20727,
          "agriculture share": -1.392289,
          "mining share": 0.035859,
          "manufacturing share": 1.318384,
          "services share": 0.067468,
          "population density": 1.252051,
          "share aged 65+": -0.354927,
          "labour force participation": 0.933904,
          "piped-water access": 0.593512
        }
      },
      {
        "cluster_id": 2,
        "name": "Remote interior, low service access",
        "size": 19,
        "sabah_members": [
          "sbh-kota-marudu",
          "sbh-ranau",
          "sbh-tambunan",
          "sbh-tenom",
          "sbh-tongod"
        ],
        "profile": {
          "median income": -1.020465,
          "GDP per capita": -1.421191,
          "agriculture share": 0.192128,
          "mining share": -0.637821,
          "manufacturing share": -0.883018,
          "services share": 0.532217,
          "population density": -0.730171,
          "share aged 65+": 0.341324,
          "labour force participation": -0.639361,
          "piped-water access": -1.736801
        }
      },
      {
        "cluster_id": 3,
        "name": "Rural economy with mining & quarrying",
        "size": 16,
        "sabah_members": [
          "sbh-kota-belud",
          "sbh-kudat",
          "sbh-pitas",
          "sbh-tuaran"
        ],
        "profile": {
          "median income": -0.668515,
          "GDP per capita": -0.584107,
          "agriculture share": 0.295067,
          "mining share": 2.030892,
          "manufacturing share": -0.439661,
          "services share": 0.257322,
          "population density": -0.169118,
          "share aged 65+": 0.150289,
          "labour force participation": -0.929715,
          "piped-water access": -0.116365
        }
      },
      {
        "cluster_id": 4,
        "name": "Services-led towns & suburbs",
        "size": 51,
        "sabah_members": [
          "sbh-beaufort",
          "sbh-keningau",
          "sbh-kuala-penyu",
          "sbh-papar",
          "sbh-penampang",
          "sbh-putatan",
          "sbh-sandakan",
          "sbh-sipitang",
          "sbh-tawau"
        ],
        "profile": {
          "median income": 0.212918,
          "GDP per capita": -0.053428,
          "agriculture share": -0.251547,
          "mining share": -0.113473,
          "manufacturing share": 0.145286,
          "services share": 0.514371,
          "population density": 0.292552,
          "share aged 65+": -0.070484,
          "labour force participation": 0.210224,
          "piped-water access": 0.494022
        }
      },
      {
        "cluster_id": 5,
        "name": "Plantation & agrarian frontier",
        "size": 23,
        "sabah_members": [
          "sbh-beluran",
          "sbh-kalabakan",
          "sbh-kinabatangan",
          "sbh-kunak",
          "sbh-lahad-datu",
          "sbh-nabawan",
          "sbh-semporna",
          "sbh-telupid"
        ],
        "profile": {
          "median income": -0.342522,
          "GDP per capita": 0.283609,
          "agriculture share": 1.144582,
          "mining share": -0.189077,
          "manufacturing share": -0.348263,
          "services share": -1.328815,
          "population density": -0.994221,
          "share aged 65+": -0.980726,
          "labour force participation": 0.084155,
          "piped-water access": -0.508153
        }
      }
    ],
    "imputation": {
      "labour force participation": 3
    },
    "limitations": [
      "Features mix survey rounds (HIES 2024, LFS 2024) with GDP structure from 2020, the latest district GDP DOSM publishes.",
      "W.P. Putrajaya is excluded because DOSM reports its GDP inside Kuala Lumpur.",
      "Districts gazetted after 2020 (e.g. Membakut) have no GDP series and are not typed.",
      "Missing labour-force values for single-district states are median-imputed.",
      "Cluster names are descriptive labels generated by fixed rules, not judgements."
    ]
  },
  {
    "model_version": "shiftshare-2026.09.26-9f37dafd",
    "task": "shift_share",
    "title": "Shift-share decomposition of district GDP growth",
    "method": "Classic three-way decomposition (benchmark growth, industry mix, competitive) of the change in real district GDP across 5 sectors, against Sabah and Malaysia benchmarks. Identity: the three effects sum exactly to the change.",
    "windows": [
      {
        "t0": 2015,
        "t1": 2019,
        "label": "Pre-pandemic (2015–2019)"
      },
      {
        "t0": 2015,
        "t1": 2020,
        "label": "Full series (2015–2020)"
      }
    ],
    "limitations": [
      "District GDP is published only to 2020, so the decomposition cannot see 2021 onward.",
      "2020 is a pandemic year; the pre-pandemic window is shown by default.",
      "Offshore oil & gas ('Supra') is excluded from both district and benchmark totals.",
      "Import duties are not allocated to sectors, so sector sums can differ slightly from total GDP.",
      "Sector values below RM5 mil are suppressed by DOSM and treated as zero."
    ]
  },
  {
    "model_version": "scorecard-2026.09.26-7683be8f",
    "task": "scorecard",
    "title": "District scorecard rules",
    "method": "6.1 Scorecard: \"what's working, what's not\". Deliberately rule-based.\n\nFor each indicator with a direction (higher- or lower-is-better):\n  gap   = district vs the median of its structural peers (same period), direction-adjusted\n  trend = direction-adjusted change per year between the last two observations;\n          RM-denominated indicators are compared against Sabah's own growth over the\n          same window (keeping pace with the state), rates use percentage points per year\n  STRENGTH: above peers by more than the level threshold AND trend not worsening\n  CONCERN:  below peers by more than the level threshold AND trend not improving\n  otherwise the reading is \"mixed\" (e.g. below peers but improving) or \"in line\".\nThresholds below are published verbatim on the methodology page.",
    "thresholds": {
      "level": "Above/below peers: ±5% for RM values; ±1 percentage point for rates; ±0.01 for Gini.",
      "trend": "Improving/worsening: RM values growing 0.5 pp/yr faster/slower than Sabah as a whole over the same window; rates changing by more than 0.25 pp/yr; Gini by more than 0.002/yr."
    },
    "indicators": [
      "income_median",
      "poverty_absolute",
      "gini",
      "gdp_per_capita",
      "unemployment_rate",
      "lfpr",
      "access_piped_water",
      "access_electricity",
      "access_sanitation",
      "income_growth",
      "gdp_growth"
    ],
    "limitations": [
      "Peers come from the typology model; a different peer set can change a verdict.",
      "Nominal RM values are compared with Sabah's own nominal growth rather than deflated.",
      "Trends across a boundary change (e.g. Beaufort 2024 after Membakut) are not scored.",
      "GDP per capita exists only for 2020 and so has no trend."
    ]
  },
  {
    "model_version": "drivers-2026.09.26-1642cf15",
    "task": "drivers",
    "title": "Driver analysis (associated factors)",
    "method": "6.4 Driver analysis: which structural factors are *associated with* a district\nsitting above or below what its structure would predict.\n\nA ridge regression and a small gradient-boosted model (LightGBM) predict log median income and the\nabsolute poverty rate from structural features across all national districts\nand survey rounds (2019, 2022, 2024). Cross-validation is grouped by district so a\ndistrict is never scored by a model that saw it. LightGBM is used only if its CV\nerror is at least 5% below ridge's; attributions are SHAP values (LightGBM) or exact\nlinear contributions (ridge). Always labelled associations, never causes.",
    "features": {
      "sqrt_share_agriculture": "Agriculture share of GDP",
      "sqrt_share_mining": "Mining share of GDP",
      "sqrt_share_manufacturing": "Manufacturing share of GDP",
      "sqrt_share_services": "Services share of GDP",
      "log_gdp_per_capita": "GDP per capita (2020)",
      "log_pop_density": "Population density",
      "share_age_65plus": "Share aged 65+",
      "lfpr": "Labour force participation",
      "unemployment_rate": "Unemployment rate",
      "access_piped_water": "Piped-water access",
      "access_electricity": "Electricity access",
      "round": "Survey round"
    },
    "params": {
      "n_estimators": 400,
      "learning_rate": 0.03,
      "num_leaves": 7,
      "min_child_samples": 12,
      "subsample": 0.8,
      "subsample_freq": 1,
      "colsample_bytree": 0.8,
      "reg_lambda": 1.0,
      "random_state": 42,
      "verbose": -1
    },
    "targets": {
      "income_median": {
        "metrics": {
          "lightgbm": {
            "r2": 0.692947,
            "mae": 668.203851,
            "mae_sabah": 507.346304
          },
          "ridge": {
            "r2": 0.70863,
            "mae": 648.684746,
            "mae_sabah": 476.083695
          },
          "n": 472,
          "districts": 159,
          "champion": "ridge"
        },
        "importance": [
          {
            "feature": "Agriculture share of GDP",
            "mean_abs_shap": 0.072752
          },
          {
            "feature": "Survey round",
            "mean_abs_shap": 0.061492
          },
          {
            "feature": "GDP per capita (2020)",
            "mean_abs_shap": 0.059884
          },
          {
            "feature": "Population density",
            "mean_abs_shap": 0.05847
          },
          {
            "feature": "Labour force participation",
            "mean_abs_shap": 0.055238
          },
          {
            "feature": "Share aged 65+",
            "mean_abs_shap": 0.040236
          },
          {
            "feature": "Piped-water access",
            "mean_abs_shap": 0.029254
          },
          {
            "feature": "Unemployment rate",
            "mean_abs_shap": 0.026094
          },
          {
            "feature": "Manufacturing share of GDP",
            "mean_abs_shap": 0.010085
          },
          {
            "feature": "Mining share of GDP",
            "mean_abs_shap": 0.001955
          },
          {
            "feature": "Services share of GDP",
            "mean_abs_shap": 0.001747
          },
          {
            "feature": "Electricity access",
            "mean_abs_shap": 0.001057
          }
        ]
      },
      "poverty_absolute": {
        "metrics": {
          "lightgbm": {
            "r2": 0.627413,
            "mae": 4.128484,
            "mae_sabah": 7.546548
          },
          "ridge": {
            "r2": 0.526383,
            "mae": 4.663342,
            "mae_sabah": 8.026292
          },
          "n": 472,
          "districts": 159,
          "champion": "lightgbm"
        },
        "importance": [
          {
            "feature": "Unemployment rate",
            "mean_abs_shap": 3.510264
          },
          {
            "feature": "Piped-water access",
            "mean_abs_shap": 2.526609
          },
          {
            "feature": "GDP per capita (2020)",
            "mean_abs_shap": 1.84818
          },
          {
            "feature": "Services share of GDP",
            "mean_abs_shap": 1.021684
          },
          {
            "feature": "Labour force participation",
            "mean_abs_shap": 0.99563
          },
          {
            "feature": "Agriculture share of GDP",
            "mean_abs_shap": 0.661306
          },
          {
            "feature": "Manufacturing share of GDP",
            "mean_abs_shap": 0.621745
          },
          {
            "feature": "Electricity access",
            "mean_abs_shap": 0.570529
          },
          {
            "feature": "Share aged 65+",
            "mean_abs_shap": 0.548219
          },
          {
            "feature": "Population density",
            "mean_abs_shap": 0.483293
          },
          {
            "feature": "Mining share of GDP",
            "mean_abs_shap": 0.33499
          },
          {
            "feature": "Survey round",
            "mean_abs_shap": 0.299647
          }
        ]
      }
    },
    "limitations": [
      "Associations, not causes: SHAP explains the model, not the economy.",
      "About 480 district-rounds; with so few points the model is deliberately shallow.",
      "Sector structure and GDP per capita are fixed at 2020 for the 2022 and 2024 rounds.",
      "The non-citizen share is deliberately excluded as a feature.",
      "A district is flagged above/below expectations only when the gap exceeds the cross-validated mean absolute error."
    ]
  },
  {
    "model_version": "forecast-2026.09.26-74e1d087",
    "task": "forecast",
    "title": "Nowcasts and projections",
    "method": "6.5–6.6 Nowcasts and 1–3 year projections with calibrated uncertainty.\n\nDistrict GDP (published to 2020)\n  top-down   Sabah's published sector GDP (to 2025) drives each district's sectors:\n             Y_di(t) = Y_di(b) * S_i(t)/S_i(b) * exp(lambda * c_di * (t-b))\n  bottom-up  c_di is the district's own sector drift relative to the state over the\n             base window; lambda (shrinkage) is chosen on backtests in other states\n  reconcile  per state and sector, districts are scaled proportionally so they move\n             with the published state sector total (WLS/MinT with level-proportional\n             weights); offshore 'Supra' is carried separately so it never distorts districts\n  intervals  split-conformal: empirical log errors from backtests (base 2017, horizons\n             1–3) in *other* states (leave-one-state-out); widened by sqrt(h) beyond 3\n  projection 2026–2028 extends state sector growth at its non-pandemic median, with\n             transparent scenario shifts; state-path uncertainty is added in quadrature\n  lights     challenger: tilt each district's share of its state's nowcast by its night-light\n             growth relative to the state's, Y_d *= (L_d(t)/L_d(b))^beta, renormalised so\n             state totals are unchanged. beta is chosen on other states' backtests and the\n             challenger is adopted only if it cuts median error by at least 5%\n\nMedian household income (survey rounds 2019, 2022, 2024)\n  log y(T+h) = log y(T) + h * (g_state + rho * r_d)\n  g_state = the state's long-run median-income trend; r_d = district's excess growth\n  over its state in the last window; rho chosen by backtest (2019->2022 inputs, predict\n  2024). Conformal intervals from other states' errors.",
    "gdp": {
      "lambda": 0.25,
      "lambda_grid": {
        "0.0": 0.019794,
        "0.25": 0.017753,
        "0.5": 0.01878,
        "0.75": 0.020483,
        "1.0": 0.023636
      },
      "median_ape_by_horizon": {
        "chosen_model_national": {
          "1": 1.482488,
          "2": 1.834042,
          "3": 2.851676
        },
        "chosen_model_sabah_loso": {
          "1": 1.540825,
          "2": 1.91521,
          "3": 4.760496
        },
        "industry_mix_only_national": {
          "1": 1.672729,
          "2": 2.007335,
          "3": 3.031839
        },
        "naive_constant_share_national": {
          "1": 2.034214,
          "2": 2.74714,
          "3": 3.932056
        }
      },
      "median_ape_by_division_h3": {
        "West Coast": 8.311874,
        "Interior": 4.280517,
        "Kudat": 4.825944,
        "Sandakan": 4.092144,
        "Tawau": 6.691204
      },
      "conformal_log_quantiles": {
        "1": [
          -0.032943,
          0.039034
        ],
        "2": [
          -0.055071,
          0.078282
        ],
        "3": [
          -0.047316,
          0.135276
        ]
      },
      "calibration_group": [
        "Sabah",
        "Sarawak",
        "W.P. Labuan"
      ],
      "sabah_coverage_out_of_sample": {
        "1": 0.740741,
        "2": 0.62963,
        "3": 0.703704
      },
      "state_path_log_sd": 0.026124,
      "scenarios": {
        "baseline": {
          "label": "Baseline",
          "shift": {},
          "description": "State sector growth continues at its non-pandemic median."
        },
        "palm_oil_downturn": {
          "label": "Palm-oil downturn",
          "shift": {
            "agriculture": -0.04
          },
          "description": "Agriculture grows 4 pp/yr slower (e.g. a sustained CPO price slump or replanting drag)."
        },
        "oil_gas_downturn": {
          "label": "Oil & gas downturn",
          "shift": {
            "mining": -0.06,
            "services": -0.005
          },
          "description": "Mining 6 pp/yr slower with a 0.5 pp/yr services spillover. Offshore output ('Supra') bears most of it."
        },
        "services_upswing": {
          "label": "Tourism & services upswing",
          "shift": {
            "services": 0.015
          },
          "description": "Services grow 1.5 pp/yr faster (tourism, logistics)."
        }
      },
      "lights_challenger": {
        "available": true,
        "years": [
          2012,
          2025
        ],
        "beta_grid": {
          "0.0": 0.017753,
          "0.1": 0.020102,
          "0.2": 0.024041,
          "0.3": 0.029072,
          "0.5": 0.037891,
          "0.75": 0.049554,
          "1.0": 0.062901
        },
        "best_beta": 0.0,
        "challenger_beta": 0.1,
        "adopted": false,
        "margin_required": 0.05,
        "median_ape_by_horizon": {
          "champion_other_states": {
            "1": 1.433727,
            "2": 1.815409,
            "3": 2.681169
          },
          "challenger_other_states": {
            "1": 1.543384,
            "2": 2.044648,
            "3": 2.667969
          },
          "champion_sabah": {
            "1": 1.540825,
            "2": 1.91521,
            "3": 4.760496
          },
          "challenger_sabah": {
            "1": 1.48013,
            "2": 3.897743,
            "3": 5.229808
          }
        }
      }
    },
    "income": {
      "rho": 0.0,
      "rho_grid": {
        "0.0": 0.052947,
        "0.25": 0.056329,
        "0.5": 0.058418,
        "0.75": 0.069418,
        "1.0": 0.072749
      },
      "median_ape": {
        "h2_national": 5.553838,
        "h2_sabah_loso": 6.905928,
        "h2_state_trend_only_national": 5.553838,
        "h3_state_trend_only_national": 15.69585,
        "h3_sabah_loso": 12.755427
      },
      "conformal_log_quantiles": {
        "1": [
          -0.083289,
          0.085548
        ],
        "2": [
          -0.117789,
          0.120984
        ],
        "3": [
          -0.144261,
          0.148174
        ]
      },
      "calibration_group": [
        "Sabah",
        "Sarawak",
        "W.P. Labuan"
      ],
      "sabah_coverage_out_of_sample_h2": 0.666667
    },
    "interval": "80% (p10–p90), split-conformal from leave-one-state-out backtests",
    "limitations": [
      "District GDP after 2020 is not published; 2021–2025 values are nowcasts built from published state sector growth, not observations.",
      "Backtests cover horizons 1–3; wider horizons scale the interval by sqrt(h).",
      "Intervals are calibrated on East Malaysian districts (Mondrian conformal). With 27 Sabah districts, out-of-sample coverage estimates are noisy (about ±8 points).",
      "Income projections are nominal and rest on one backtest round; treat as indicative.",
      "Scenarios are transparent sensitivities, not predictions of commodity prices.",
      "The night-lights challenger did not beat the sector-only nowcast by the required 5% in other states' backtests (any positive weight on lights raised the error), so it is reported but not used. Night lights stay in the atlas as a descriptive indicator."
    ]
  }
]