Skip to content

Locked methodology — worked through on engineering

One statement of every definition and scope, so none of it gets re-litigated. Engineering is used throughout as the worked example; the same rules apply to the other five buckets, with the one documented exception noted at the end.

Filter Value Why
release_id 'calendar_2025' One release. Pooling CY2022–25 is done separately as a robustness check in POOLED.md — it confirms these figures and tightens the interval 26%, but is not the headline because a pooled figure is no longer current.
visit 'V1' pas is collected only at the first visit; on multi-visit releases it is 100% populated at V1 and 0% at every revisit.
gedu_lvl = '12' “Literate: graduate”. Completed a UG, has not completed a PG'13' is “postgraduate and above” and is excluded. Someone currently doing a master’s is still '12' (unfinished) and appears as pas='91', which is why “studying” reads as PG study.
tedu_lvl = '03' “Technical degree in engineering/technology”. Degrees only — the engineering diplomas ('08', '13') are a separate bucket and are 9–10 percentage points worse on formal employment.
weight_annual not null All shares are weighted.

gedu_lvl and tedu_lvl are different fields whose code values overlap. tedu_lvl='13' is a graduate-level engineering diploma; gedu_lvl='13' is a postgraduate degree. They are unrelated.

2. Two age windows, for two different questions

Section titled “2. Two age windows, for two different questions”

This is the part that kept getting confused. They are not interchangeable.

Three years from engineering’s normal completion age of 22. Use this for anything about what a fresh graduate faces, and for any comparison against a college’s placement figure, because a placement is reported at graduation.

Emitted by the pipeline as age_band = '0a. recent graduates', with the window set per bucket — engineering 22–24, medical 24–26 (MBBS is 5.5 years including internship), everything else 21–23. It is not computed by hand anywhere.

n = 1,042 · 1.16m people

Formal Informal wage Self-employed Working for money Studying Neither
34.6% 2.9% 2.5% 40.0% 16.7% 43.3%

Formal and earning ≥₹6 lakh: 4.42% — and this one is checked against tenure below rather than taken on trust.

Every engineering graduate in the working-age range, most of them years into a career. Use this for the size and state of the existing workforce, and as the pool for the tenure analysis.

n = 4,734 · 5.42m people

Formal Informal wage Self-employed Working for money Studying Neither
52.6% 4.3% 7.8% 64.7% 6.0% 29.3%

Formal and earning ≥₹6 lakh: 17.56%

4.42% and 17.56% answer different questions. The first is what a graduating engineer walks into; the second is where engineers who have been working for years have got to. The gap between them is wage growth, not disagreement. Any comparison against a college’s reported placement salary uses A, because that is the moment a college measures.

2b. The ≥₹6 lakh figure is checked against tenure, not assumed

Section titled “2b. The ≥₹6 lakh figure is checked against tenure, not assumed”

The 22–24 window admits people at any tenure, and a 24-year-old can already have three years behind them. Since wages nearly double over that span, the age window could be importing wage growth into a number meant to describe people at the start. Engineering is the one bucket with the sample to test this, so it is tested. check_entry_wage.py, clustered bootstrap over first-stage units, 2,000 draws.

The like-for-like comparison — share of those in formal work clearing ₹6 lakh:

Cut n FSUs Estimate 95% CI
Aged 22–24, any tenure (what the headline uses) 304 279 12.76% [6.58, 20.08]
Aged 22–24, under 1 year in (strict) 101 94 10.34% [2.54, 21.24]
Any age, under 1 year in (ignores graduation timing) 182 168 11.19% [4.50, 18.78]

Resample first-stage units — the village or urban block PLFS sampled — with replacement: draw 916 units from the 916, take every person in each drawn unit, recompute the weighted share, repeat 2,000 times, read off the 2.5th and 97.5th percentiles.

Clustering is the right method but not the reason these intervals are wide. The schema warns that treating people as independent understates the standard error by 3.5–4.7×, but that applies to whole-population estimates where each unit yields about twelve households. A thin subgroup is different: engineering graduates aged 22–24 are 1,042 people across 916 units, 1.14 per unit, with only 110 units holding more than one. Measured design effect: 1.14×.

Method Interval on the 4.42% Width
Cluster bootstrap (used) [2.19%, 7.19%] 5.01pp
Person bootstrap [2.42%, 6.73%] 4.31pp
Wald from the raw count, unweighted [3.17%, 5.67%] 2.50pp

The count sets the scale and the survey weights nearly double it. n=34 in the numerator gives the Wald width of 2.50pp; weighting takes it to 4.31pp because a handful of high-weight respondents genuinely move the estimate; clustering adds the last 0.7pp. The cluster bootstrap is kept because it is correct whether or not clustering bites and costs nothing — not because it is doing the work.

Verdict: the age window is not measurably contaminated. The gap to the strict entry-tenure reading is 2.4pp and the intervals overlap heavily. It reads slightly high, so using it is generous toward whatever it is compared against — the conservative direction for our purposes.

The strict cut is not adopted because it costs more than it buys: n falls from 304 to 101 and the interval widens from 13.5pp to 18.7pp. A noisier estimate of the same number is not an improvement.

Carried through to the cohort figure, the three wage rates give:

Wage rate applied to the 34.6% formal rate ≥₹6L rate Cohort share
Same window, any tenure (headline) 12.76% 4.42%
Aged 22–24, entry tenure 10.34% 3.58%
Any age, entry tenure 11.19% 3.88%

So the headline 4.42% would become 3.58% on the strictest reading. Every alternative is lower, which means 4.42% is the generous end of the range — worth stating wherever it is compared against a college’s reported placement salary.

A note on reading these: only the first row can be computed directly as a share of the cohort. Rows 2 and 3 restrict the denominator to people in their first year of an activity, so their raw share answers a different question. The comparable quantity is the composite above — the cohort’s formal employment rate times the entry-tenure wage rate.

3. Wage progression is measured by tenure, not age

Section titled “3. Wage progression is measured by tenure, not age”

dur_pas — duration in the present principal activity status — collapsed to three steps. Pooled across ages 21–34, because tenure cells inside 22–24 alone are too thin.

Tenure beats age here because it measures time in work directly: it makes no assumption about when anyone graduated, and it separates a fresh entrant aged 30 from a thirty-year-old with eight years behind them.

Step n p25 Median p75 p90 ≥₹50k
Under 1 year 182 ₹20,000 ₹25,000 ₹35,000 ₹50,000 11.2%
1 to 3 years 762 ₹25,000 ₹34,500 ₹45,000 ₹65,000 24.4%
Over 3 years 1,216 ₹30,000 ₹42,000 ₹60,000 ₹80,000 42.9%

Median rises 1.68× and the share clearing ₹50,000 goes 11.2% → 42.9%.

The code boundaries are inferred, not documented. dur_pas has no codebook in our schema, so it was validated against people who cannot have long tenure: among graduates in salaried work, code 3 peaks at age 22–23, code 4 at 25, code 5 from 27. That fits the standard NSS ladder — under 6 months, 6–12 months, 1–2 years, 2–3 years, 3+ years.

Two caveats. It measures time in the activity status, not with an employer, so changing jobs while staying salaried keeps the clock running. And it is blank for anyone not working, so it says nothing about the unemployed.

4. The activity split — five categories, exhaustive

Section titled “4. The activity split — five categories, exhaustive”

Every person in the base falls in exactly one. They sum to 100.

Category Rule
Formal pas='31' AND ern_reg>0 AND (job_pas IN ('2','3','4') OR ssec_pas NOT IN ('8','9'))
Informal wage pas='31' AND ern_reg>0, without contract or benefits; plus casual wage labour pas IN ('41','42','51')
Self-employed pas IN ('11','12') AND ern_self>0
Studying pas='91' — already holds a degree, so this is postgraduate study
Neither everything else: 81 unemployed, 92/93 domestic duties, 21 unpaid family work, 94/95/97

What “formal” means, verified against the table’s own labels

Section titled “What “formal” means, verified against the table’s own labels”
  • pas='31' — “Worked as regular salaried/wage employee”, the usual principal activity over the last 365 days, not a snapshot of last week.
  • ern_reg>0 — actually paid. 97% of salaried workers clear this; the rest are dropped.
  • job_pas1 no written contract; 2/3/4 written contract of ≤1yr / 1–3yr / >3yr.
  • ssec_pas17 are combinations of PF/pension, gratuity and health/maternity benefits; 8 “not eligible for any”; 9 “not known”.
Route All graduates Engineering only
Share Median Share Median
Both contract and benefits 53.8% ₹28,500 75.9% ₹39,600
Benefits only, no contract 13.7% ₹22,000 15.1% ₹35,000
Contract only, no benefits 5.9% ₹14,000 2.5% ₹21,000
Neither → classed informal 26.7% ₹12,000 6.6% ₹16,000

A known weakness — and it bites at the bottom of the education distribution, not here. Across all graduates the contract-only group earns ₹14,000 median, barely above the ₹12,000 of those classed informal: a written contract alone is a weak marker. For engineering graduates the sign reverses — contract-only earns ₹21,000 against the informal ₹16,000, and is 2.5% of the salaried rather than 5.9%.

An earlier version of this table published the all-graduates figures under an “engineering graduates” heading, which made the weakness look far worse here than it is. It is real, and it is concentrated at low education levels: see ../ASSUMPTIONS.md C1, where contract-only admissions are 24% of the bottom rung’s formal count and earn less than the informal group.

Requiring social security would cut engineering’s formal rate from 52.58% to 51.20%. The current rule is kept because it matches how PLFS-based formality is conventionally measured, and the alternative is one line of code away.

  • Unpaid family helpers (pas='21') are never “working for money”. PLFS records them as employed; they earn nothing. They sit in “neither”.
  • Casual wage labour is counted as working without an earnings test, because ern_reg and ern_self cover only regular and self-employed work — of 1,286 casual labourers in the wider base, just 12 carry an earnings value. Requiring one would drop them for a reason about the survey’s field coverage, not their work. They are under 3% of engineering either way.

5. The ₹6 lakh line is a parameter, not an assumption

Section titled “5. The ₹6 lakh line is a parameter, not an assumption”

₹6 lakh a year is ₹50,000 a month — the 90th percentile for an employed engineering graduate at 22–24. output/wage_threshold_grid.csv carries the exact weighted share above every threshold from ₹15,000 to ₹150,000 a month, for every bucket and step, so it can be moved without a rerun.

A log-normal was fitted for interpolation and then checked before use. It fails at low thresholds by up to 14.9pp, because reported wages heap on round numbers and that is exactly where those thresholds sit. So share_above() reads the empirical grid and refuses to answer below ₹15,000. The fit is used only above ₹40,000 and only to extrapolate past the top of the grid.

The fit also sits below the true share at ₹50,000 in 29 of 42 cells — the real right tail is fatter than log-normal, which matters for any analysis that models Indian salaries that way.

Three buckets use age bands rather than tenure, because their smallest tenure cell falls under the 50 minimum: medical (43), other technical (47) and other diploma (45). The rule is applied mechanically in build.py and the lens chosen is printed for every bucket, never left implicit.

The “recent graduate” window is per-bucket, set three years from each course’s normal completion age: engineering 22–24, doctors 24–26 (MBBS is 5.5 years including internship), three-year degrees 21–23.