Skip to content

Seat Accessibility Model

What this answers: of India’s engineering and medical (MBBS + BDS) seat capacity, how much is actually reachable by what fraction of students — once you gate each seat twice: once on merit (exam percentile) and once on money (fee vs family budget). Those two gates are deliberately anti-correlated in the Indian seat design — cheap seats have high merit bars; low-merit seats are expensive — so “how many seats exist” is a wildly misleading measure of access. This model computes the honest one.

Built for a policy / advocacy paper. The findings write-up is in paper.md; this README is how it’s built and how to reproduce it.


1. Worked example — one college, end to end

Section titled “1. Worked example — one college, end to end”

Take a private-state MBBS college with 150 sanctioned seats. The model does this:

  1. Split into regulatory buckets (rules in sources/assumptions.yaml → medical_split):
    • pvt_govt_rate = 75 seats (50%) — the NMC-2022 rule: half of a private college’s seats are charged at the government rate, filled merit-first.
    • management = 53 seats (35%) — FRC-capped but ₹10–25 L/yr.
    • nri = 22 seats (15%) — ₹25–50 L/yr, effectively affluent-only.
    • Check enforced in code: 75 + 53 + 22 = 150 = total intake.
  2. Attach a fee band and a merit floor to each bucket (from the config): pvt_govt_rate → ~₹1.2 L/yr, merit floor ~90th percentile; management → ~₹18 L/yr, floor ~50th (near qualifying); nri → ~₹40 L/yr, floor ~qualifying.
  3. A student reaches a bucket only if BOTH gates pass: merit_percentile ≥ floor AND fundable_budget ≥ fee.
    • A 90th-percentile student from a ₹1 L/yr-budget family reaches the 75 cheap merit seats — but nothing else in this college.
    • A 55th-percentile student from a ₹20 L/yr family reaches the 53 management seats (merit is a non-issue) but is locked out of the cheap ones (filled by higher scorers).
    • A 75th-percentile student from a ₹1 L/yr family — the squeezed middle — reaches zero seats here: too low for the merit-gated cheap seats, too poor for the management seats.
  4. Aggregate this over every college and every student cell → the reachability heatmap and headline statistics.

That single college shows the whole point: 150 “available” seats, but for a high-merit poor student only 75 are reachable, and for the squeezed middle, none.


Output (on GCS) What it is
exploded_buckets.csv one row per (college × seat-bucket): seats, fee band, merit floor, domicile scope. The model’s core table.
reachability.csv R[merit_band × budget_tier] = reachable seats, at every calibration (fee low/high × cutoff tight/loose × affordability conservative/generous).
headline_stats.csv the paper’s punchline numbers per calibration.
fig_heatmap.png reachable seats across merit percentile × budget tier.
fig_access_curves.png cumulative reachable seats vs merit, one line per budget tier.
fig_lorenz.png inequality-of-access curve (cumulative capacity vs cumulative students).
fig_capacity_trend.png capacity by college founding year (medical) + AICTE govt-vs-private intake trend (engineering).
bucket_summary.csv, fee_spotcheck.csv per-bucket table; Gujarat real-fee validation.

3. Provenance map — where every number comes from

Section titled “3. Provenance map — where every number comes from”

Pulled from BigQuery (avantifellows.external_data_sources):

  • nmc_fact_mbbs_seats — MBBS capacity: 780 colleges, 118,190 seats, management_category, year_of_inception. The MBBS split + trend anchor.
  • nirf_fact_aggregate — engineering capacity: first_year_intake per NIRF-ranked institute (latest academic_year per institute). This is the “where we have NIRF” engineering scope (~260 ranked institutes, ~252 k seats).
  • aicte_fact_intake — engineering govt-vs-private intake trend, 2012–13→2022–23.
  • plfs_fact_households — MPCE / household consumption distribution, used to ground the affordability budget-tier shares (weighted, calendar_2025).

Custom / built here (not in BQ):

  • Bucket split — sanctioned intake exploded into regulatory buckets (govt AIQ/state, private govt-rate/management/NRI, deemed management/NRI) via the rules in sources/assumptions.yaml. Encodes NMC-2022 (50% govt-rate), the 15% AIQ, and the deemed “no-government-quota” reality.
  • Two-gate reachability — the merit×money computation in build_model.py.
  • Management → archetype normalisation — NMC has no “Deemed” label (Government/Trust/Society/Private only); engineering archetype derived from College DB college_type.

Non-BQ inputs — fetched from GCS (College DB origin):

  • bds_all_colleges_2025-26.csv — BDS capacity (330 colleges; BQ has no BDS seat matrix). From Avanti’s College DB medical-national-ranks pipeline.
  • college_master.csv — engineering management archetype via nirf_id join.
  • gujarat_acpc_2024.csv — real per-course fees, used only to spot-check the indicative fee bands (Gujarat).

All modelling choices live in ONE file: sources/assumptions.yaml — split shares, fee bands, merit floors, budget tiers, qualifier counts. To change an assumption you edit that file and rerun; you never touch the code.


Terminal window
# 1. fetch the non-BQ inputs into sources/ (BQ + these = full reproduction)
gcloud storage cp \
"gs://avantifellows-external-data/seat-accessibility/inputs/*" \
analysis/neet-inequity-paper/sources/
# 2. build the model (reads BQ + sources/, writes output/)
python3 build_model.py
# 3. render figures + tables
python3 analyze.py

Requires bq authenticated (see repo docs/BQ_SETUP.md) and pandas, matplotlib, pyyaml.


5. Where the estimate is soft (read before citing a number)

Section titled “5. Where the estimate is soft (read before citing a number)”
  • Fees are indicative, not sourced per-college. No systematic Indian fee dataset exists. Bands come from published FRC caps / university notifications and are swept low/high to bracket. Gujarat ACPC real fees validate the accessible end (merit medical ~₹96 k/yr, engg ~₹75 k/yr — both inside the bands); management/NRI fees can’t be validated this way.
  • Management “cutoffs” are demand-clearing prices, not fixed floors — swept tight/loose.
  • Affordability is a modelling assumption. PLFS gives the consumption distribution (≈0.1 % of households consume ≥₹10 L/yr); fundable outlay lifts that with loans + an aspirant-skew factor. Reported at two calibrations. This input drives the answer as much as the seat data — never quote a single point.
  • Deemed medical colleges are not separately identified. NMC labels them Trust/Society/Private, so they get the private-state 50%-govt-rate bucket. If genuine deemed universities (Manipal, DY Patil, …) don’t honour that rule, the true government-rate share is lower than 40.8 % (nearer ~30 %) and the affluent-only share higher. Directional caveat; a deemed-identification join is future work.
  • Engineering scope = NIRF-ranked only. Undercounts the private long tail (AICTE shows ~1 M private engg seats vs the ~250 k modelled here) — so the engineering affluent-only share is understated.
  • Domicile: state-quota buckets carry scope=state; a reachable_seats_national column isolates what an out-of-state student can reach. Full state-by-state fragmentation is not modelled in v1.
  • Trend caveat: medical trend attributes current seat counts to each college’s founding year (survivorship + current-size proxy), not historical seat counts.

De-identified model outputs (college-level aggregates, no PII) on GCS: