You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of #192, Workstream A week-1 deliverable ("SIPP job-level reader in data/ — label-verified, family.py pattern"). Third reader after #193/#194 (ASEC NOEMP) and #198/#199 (January tenure supplement).
What
A label-verified reader for the SIPP 2014-redesign public-use files' job-level monthly records — the primary label panel that trains and scores job-spell transitions (plan architecture; SIPP holdouts referee gates E4/E5/E6/E8/E9) — plus a spell-collapse helper that emits a C1-preview frame (explicitly not the frozen C1: ADR 0003 is still Proposed on #195).
Verified facts (Census SIPP variable API + published input examples, read 2026-07-14)
The pu files are pipe-delimited CSVs, and Census's own 2023_sipp_python_input_example.py instructs reading with usecols (the full file does not fit in memory). One row per person-month (SSUID × PNUM × MONTHCODE), with up to seven job slots per row (EJB1_* … EJB7_*).
Per-job variables, all verified via api.census.gov/data/{2022,2023}/sipp/variables/*.json with identical labels in both years:
EJB{n}_JOBID — "Unique identifier for a job which is consistent across waves" (−999 missing) — the within-panel employer-attachment key the whole phase-1 hazard story rests on;
EJB{n}_BMONTH / EJB{n}_EMONTH — beginning/ending month of the job spell with employer n;
EJB{n}_CLWRK — class of worker (1 federal / 2 active-duty military / 3 state / 4 local / 5 private for-profit / 6 private nonprofit / 7–8 self-employed inc/uninc; −9 missing);
EJB{n}_JBORSE — employer vs self-employed vs other work arrangement;
TJB{n}_IND / TJB{n}_OCC — industry/occupation codes (string-typed in the API schema);
TJB{n}_MSUM — monthly earnings from job n (−999 missing).
Person-month variables: SSUID (string — a scrambled PSU/sequence/frame composite; the PERIDNUM/HRHHID dtype lesson applies), PNUM, MONTHCODE (reference month 1–12), SWAVE, WPFINWGT (final person weight, is-weight: true), TAGE, ESEX.
The API base exists for 2022+ only; 2021-and-earlier files (and the 2014 panel's wave files) are documented extensions requiring their own dictionary verification, same posture as the other two readers.
Scope
data/sipp_jobs.py:
read_sipp_job_months(year, ...) for 2022 and 2023: staged pu{year}.csv[.gz] under ~/PolicyEngine/sipp-data (env POPULACE_DYNAMICS_SIPP_DIR), pipe-delimiter enforced, usecols restricted to the verified set, SSUID as string. Emits one row per person-month-job (job slots with non-missing JOBID): person id (SSUID-PNUM), reference year/month, wave, job slot, job id, class of worker (raw + label), work arrangement, establishment-size code, industry, monthly earnings, within-month earnings_share and top-earner flag, age, sex, weight.
Read-time verification: required columns per detected job slot, domains for every coded variable (MONTHCODE 1–12, CLWRK/JBORSE/EMPSIZE code sets, BMONTH/EMONTH in {−9} ∪ 1–12, JOBID positive or −999, MSUM ≥ 0 or −999, weight ≥ 0), loud refusal otherwise; path/year consistency for pu{yy} filenames; unsupported years refused.
job_spells(job_months): collapse maximal consecutive-month runs per (person, job id) into spells — start/end (year, month), months observed, total/mean earnings, majority-of-months primary flag, spell-constant attribute checks (class of worker / industry / size within a spell surfaced, not silently averaged). Output shape mirrors C1 (person_id, spell bounds, industry, size-code, earnings_share, primary_job) and is labeled C1-preview: it also gives Workstream B a real generator for C1-conforming fixture files.
Tests: fixture-based (small pipe-delimited files), every refusal path, spell-collapse geometry (gaps split spells; concurrent jobs share earnings correctly), and a skipif-gated real-data pass.
Non-goals
No transition hazards, no imputation, no seam-bias handling (phase-1 work with its own pre-registration), no cross-wave linkage yet (JOBID makes it possible later; single-file first), no 2014-panel/2021-and-earlier support, no frozen C1 emission, no committed microdata.
Acceptance
Fixture round-trips: person with two concurrent jobs → correct shares and primary flags; a mid-year gap in the same JOBID → two spells; attribute change within a JOBID run → surfaced.
Every domain/universe violation raises with the year and column named.
Module docstring records the API-verification provenance per supported year.
Part of #192, Workstream A week-1 deliverable ("SIPP job-level reader in
data/— label-verified,family.pypattern"). Third reader after #193/#194 (ASEC NOEMP) and #198/#199 (January tenure supplement).What
A label-verified reader for the SIPP 2014-redesign public-use files' job-level monthly records — the primary label panel that trains and scores job-spell transitions (plan architecture; SIPP holdouts referee gates E4/E5/E6/E8/E9) — plus a spell-collapse helper that emits a C1-preview frame (explicitly not the frozen C1: ADR 0003 is still Proposed on #195).
Verified facts (Census SIPP variable API + published input examples, read 2026-07-14)
2023_sipp_python_input_example.pyinstructs reading withusecols(the full file does not fit in memory). One row per person-month (SSUID×PNUM×MONTHCODE), with up to seven job slots per row (EJB1_*…EJB7_*).api.census.gov/data/{2022,2023}/sipp/variables/*.jsonwith identical labels in both years:EJB{n}_JOBID— "Unique identifier for a job which is consistent across waves" (−999 missing) — the within-panel employer-attachment key the whole phase-1 hazard story rests on;EJB{n}_BMONTH/EJB{n}_EMONTH— beginning/ending month of the job spell with employer n;EJB{n}_CLWRK— class of worker (1 federal / 2 active-duty military / 3 state / 4 local / 5 private for-profit / 6 private nonprofit / 7–8 self-employed inc/uninc; −9 missing);EJB{n}_JBORSE— employer vs self-employed vs other work arrangement;EJB{n}_EMPSIZE— establishment size at the worker's location (the C2 proxy-chain input, per the Employer–firm relationships: US-first plan, evaluation gates, and two-workstream split #192 finding and ADR 0003; codes 1–8, verified on Employer-firm Workstream B: canonical banding (C2), ADR 0003, target pipeline (#192) #195);TJB{n}_IND/TJB{n}_OCC— industry/occupation codes (string-typed in the API schema);TJB{n}_MSUM— monthly earnings from job n (−999 missing).SSUID(string — a scrambled PSU/sequence/frame composite; the PERIDNUM/HRHHID dtype lesson applies),PNUM,MONTHCODE(reference month 1–12),SWAVE,WPFINWGT(final person weight,is-weight: true),TAGE,ESEX.Scope
data/sipp_jobs.py:read_sipp_job_months(year, ...)for 2022 and 2023: stagedpu{year}.csv[.gz]under~/PolicyEngine/sipp-data(envPOPULACE_DYNAMICS_SIPP_DIR), pipe-delimiter enforced,usecolsrestricted to the verified set,SSUIDas string. Emits one row per person-month-job (job slots with non-missingJOBID): person id (SSUID-PNUM), reference year/month, wave, job slot, job id, class of worker (raw + label), work arrangement, establishment-size code, industry, monthly earnings, within-monthearnings_shareand top-earner flag, age, sex, weight.pu{yy}filenames; unsupported years refused.job_spells(job_months): collapse maximal consecutive-month runs per (person, job id) into spells — start/end (year, month), months observed, total/mean earnings, majority-of-months primary flag, spell-constant attribute checks (class of worker / industry / size within a spell surfaced, not silently averaged). Output shape mirrors C1 (person_id, spell bounds, industry, size-code, earnings_share, primary_job) and is labeled C1-preview: it also gives Workstream B a real generator for C1-conforming fixture files.Non-goals
Acceptance