Data AnalyticsPortfolio projects with Indian public data

Pupil-teacher ratios across Indian states with UDISE+ data

PK
Pankit Kumar
Sr. Data Scientist at Parexel (a Goldman Sachs–backed company) · 27 September 2026 · 4 min read
In this article (7 sections)

Related programme: the Data Analytics with Generative AI course (3–4 months; ₹90,000 (inclusive of GST) · EMI from ₹4,000/month).

The short answer

UDISE+ is India's school census, and its state-level indicators let you ask a concrete question: where are schools short of teachers, and is the problem a high average ratio or a long tail of single-teacher and empty schools? This project pulls three related indicators from MoSPI's eSankhyiki portal: pupil-teacher ratio, schools with a single teacher and schools with zero enrolment. It combines them into a state profile. The deliverable is a compact comparison that avoids the most common mistake with this data, which is treating one average ratio as the whole story.

Getting the indicators

MoSPI's eSankhyiki UDISE page serves UDISE+ indicators in a simple long format. The columns we observed are Indicator, Year, State/UT, Value. For 2024-25, for example, it listed Delhi with 5,556 schools. The indicator list includes:

  • Pupil Teacher Ratio
  • Schools with Zero Enrolments
  • Schools with Single Teachers
  • Dropout Rate, Transition Rate, Retention Rate
  • GER, NER and the gender parity index of GER
  • School having ICT Labs, and computer and digital-initiative details
  • Drinking water and other facility indicators

Download each indicator you need for the same year. Check the Metadata tab for whether a ratio is given for all levels together or by level (primary, upper primary and so on), and check the downloaded file's header. The ministry's own UDISE+ portal has the underlying reports if you want more detail.

Why an employer would care

Education departments, CSR foundations, edtech companies and development consultancies all target resources by state and district. An analyst who can say "this state's average ratio looks fine, but it has a large share of single-teacher schools" gives a funder something actionable. The project also shows you can join several long-format indicator files into one tidy table without losing track of units.

Cleaning issues

  • Long format. Every indicator sits in the same Value column. Pivot on Indicator only after filtering to one Year.
  • Mixed units. A ratio, a count of schools and a percentage share in one column. Name the pivoted columns with their units.
  • Counts need denominators. "Schools with Single Teachers" is more meaningful as a share of all schools in the state. Find the total schools figure in the portal, confirm its year matches, and divide.
  • State naming. UTs can be merged or renamed across years. Normalise names before comparing years.
  • Totals. Keep the All India row for reference, but exclude it from rankings.

Code: pivot, normalise, profile

The table below is synthetic. State names are real, but every value is invented and is not UDISE+ data.

python
import pandas as pd

long = pd.DataFrame({
    "Indicator": ["Pupil Teacher Ratio"] * 5 + ["Schools with Single Teachers"] * 5
                 + ["Schools with Zero Enrolments"] * 5 + ["Total Schools"] * 5,
    "Year": ["2024-25"] * 20,
    "State/UT": ["Delhi", "Haryana", "Uttar Pradesh", "Kerala", "All India"] * 4,
    "Value": [30, 24, 33, 17, 25,          # ratio
              40, 2500, 20000, 150, 100000,  # schools
              10, 300, 2500, 50, 12000,    # schools
              5500, 23000, 250000, 16000, 1450000],
})

wide = long[long["Year"] == "2024-25"].pivot(index="State/UT", columns="Indicator", values="Value")
wide = wide.rename(columns={
    "Pupil Teacher Ratio": "ptr",
    "Schools with Single Teachers": "single_teacher_schools",
    "Schools with Zero Enrolments": "zero_enrolment_schools",
    "Total Schools": "total_schools",
})
wide["single_teacher_share_pct"] = 100 * wide["single_teacher_schools"] / wide["total_schools"]
wide["zero_enrolment_share_pct"] = 100 * wide["zero_enrolment_schools"] / wide["total_schools"]

states = wide.drop(index="All India")
india = wide.loc["All India"]
states["ptr_vs_india"] = states["ptr"] - india["ptr"]
states["single_vs_india_pp"] = states["single_teacher_share_pct"] - india["single_teacher_share_pct"]

def profile(r):
    high_ptr = r["ptr_vs_india"] > 0
    high_single = r["single_vs_india_pp"] > 0
    if high_ptr and high_single:
        return "crowded and thin"
    if high_ptr:
        return "crowded classrooms"
    if high_single:
        return "thinly staffed schools"
    return "better than national on both"

states["profile"] = states.apply(profile, axis=1)
print(states[["ptr", "single_teacher_share_pct", "zero_enrolment_share_pct", "profile"]].round(1).to_string())

The "Total Schools" indicator name in the synthetic data is a stand-in. Find the portal's actual indicator for total schools, confirm it covers the same school types, and use its exact name.

The profile labels turn two numbers into a category a non-technical reader understands. Keep the thresholds simple and explain them. Here they are "above or below the national figure".

Analysis plan

  1. 1Choose one year and download the indicators above, plus total schools.
  2. 2Build the wide table and the two shares.
  3. 3Plot states on a scatter: pupil-teacher ratio on one axis, single-teacher share on the other, with national reference lines dividing four quadrants.
  4. 4Repeat for an earlier year if the portal has it, and draw arrows showing each state's movement.
  5. 5Add the zero-enrolment share as a note, not a ranking. A school with no pupils may be merging or closing, and the data cannot say which.

Deliverables

ItemDetail
Quadrant scatterPTR against single-teacher share, NCR states labelled
Profile tableState, PTR, single-teacher share, zero-enrolment share, profile
Movement chartArrows from the earlier year to the latest, if both are available
READMEIndicator names used, year, denominators, thresholds, source, caveats

Among the caveats, note that a state average hides district variation. Also note that pupil-teacher ratio says nothing about teacher qualification or subject coverage.

Interview questions

  • Why is a single average pupil-teacher ratio not enough?
  • How did you check that your denominator covers the same schools as the numerator?
  • A state has a low ratio and many single-teacher schools. How is that possible?
  • What would you want at district level to target a teacher-recruitment programme?

A second UDISE+ project, on the school ICT lab gap, uses the same portal for a different question. Our Data Analyst programme teaches long-to-wide reshaping and denominator checks through projects like this one.

PK
Pankit Kumar
Lead Instructor, NeuraPath Academy

Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.

This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.

Explore Data Analytics with Generative AI
Counselling is free · no obligation

Not sure which programme fits?

Tell us your background and we will map it to the right entry point — including saying so when a cheaper programme is the better fit. A counsellor replies within one working day.