ltc-code 0.2.33__tar.gz → 0.2.34__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. {ltc_code-0.2.33 → ltc_code-0.2.34}/PKG-INFO +1 -1
  2. {ltc_code-0.2.33 → ltc_code-0.2.34}/pyproject.toml +3 -3
  3. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/directory.dta +1 -1
  4. ltc_code-0.2.34/src/ltc_code/tract_covars/README.md +85 -0
  5. ltc_code-0.2.34/src/ltc_code/tract_covars/build_tract_covars.py +171 -0
  6. ltc_code-0.2.34/src/ltc_code/tract_covars/inputs/nhgis0006_ds250_20205_tract_codebook.txt +263 -0
  7. ltc_code-0.2.34/src/ltc_code/tract_covars/inputs/nhgis0006_ds273_20245_tract_codebook.txt +261 -0
  8. {ltc_code-0.2.33 → ltc_code-0.2.34}/README.md +0 -0
  9. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/aspire.py +0 -0
  10. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/check_cmo_apps.do +0 -0
  11. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/christel_house.py +0 -0
  12. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/democracy_prep.py +0 -0
  13. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/green_dot.py +0 -0
  14. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/helpers.py +0 -0
  15. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/ilt.py +0 -0
  16. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/kipp_nj.py +0 -0
  17. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/main.py +0 -0
  18. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/mappings.py +0 -0
  19. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/rocketship.py +0 -0
  20. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260614_new_build_scripts_update/yes_prep.py +0 -0
  21. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/aspire.py +0 -0
  22. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/christel_house.py +0 -0
  23. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/democracy_prep.py +0 -0
  24. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/green_dot.py +0 -0
  25. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/ilt.py +0 -0
  26. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/kipp.py +0 -0
  27. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/kipp_nj.py +0 -0
  28. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/rocketship.py +0 -0
  29. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260630_census_disclosure/yes_prep.py +0 -0
  30. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260706_ceprscripts/BALANCE.do +0 -0
  31. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260706_ceprscripts/FS.do +0 -0
  32. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260706_ceprscripts/ITT.do +0 -0
  33. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260706_ceprscripts/TOT.do +0 -0
  34. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260706_ceprscripts/apps_helpers.py +0 -0
  35. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260706_ceprscripts/harmony.py +0 -0
  36. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260706_ceprscripts/main.py +0 -0
  37. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260712_uncommon_scripts/main.py +0 -0
  38. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260712_uncommon_scripts/mappings.py +0 -0
  39. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/20260712_uncommon_scripts/uncommon.py +0 -0
  40. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/__init__.py +0 -0
  41. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/aspire.py +0 -0
  42. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/check_cmo_apps.do +0 -0
  43. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/christel_house.py +0 -0
  44. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/green_dot.py +0 -0
  45. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/helpers.py +0 -0
  46. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/june13.py +0 -0
  47. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/june2.py +0 -0
  48. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/june30.py +0 -0
  49. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/june5.py +0 -0
  50. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/june7.py +0 -0
  51. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/kipp_nj.py +0 -0
  52. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/kipp_tx.py +0 -0
  53. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/main.py +0 -0
  54. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/make_summary_stats_table.py +0 -0
  55. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/mappings.py +0 -0
  56. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/may27.py +0 -0
  57. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/EXTENSIONS.md +0 -0
  58. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/NEW_CROSSWALK.md +0 -0
  59. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/OUTCOMES.md +0 -0
  60. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/__init__.py +0 -0
  61. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/build_nsc_outcomes.py +0 -0
  62. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/build_nsc_outcomes_new.py +0 -0
  63. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/dhs_stem/dhs_stem_cip_additions_2024.csv +0 -0
  64. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/dhs_stem/extract_dhs_stem_cips.R +0 -0
  65. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/dhs_stem/stemList2024.pdf +0 -0
  66. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/naics.csv +0 -0
  67. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/naics.py +0 -0
  68. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/naics_raw.xlsx +0 -0
  69. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/CREDENTIAL_LEVEL_LOOKUP_TABLE.xlsx +0 -0
  70. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/IPEDS_IC_2013.csv +0 -0
  71. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/IPEDS_IC_manual.xlsx +0 -0
  72. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/NSC_SCHOOL_CODE_TO_IPEDS_UNIT_ID_XWALK_APR-2023.xlsx +0 -0
  73. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/chetty/mrc_table11.dta +0 -0
  74. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/chetty/mrc_table2.dta +0 -0
  75. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/college_crosswalk.xls +0 -0
  76. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/raw/ipeds_data.dta +0 -0
  77. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/nsc/run_nsc_outcomes.py +0 -0
  78. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/README.md +0 -0
  79. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/build_latex.py +0 -0
  80. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/config.py +0 -0
  81. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/data/charter_exhibits/disclosed_results.xlsx +0 -0
  82. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/data/charter_exhibits/pending_results.xlsx +0 -0
  83. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/exhibit_selection.py +0 -0
  84. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_figures.py +0 -0
  85. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_requirements.txt +0 -0
  86. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/age25_earnings_table.tex +0 -0
  87. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/balance_table.tex +0 -0
  88. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/cmo_details_table.tex +0 -0
  89. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/college_data_comparison_table.tex +0 -0
  90. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/college_enrollment_completion_table.tex +0 -0
  91. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/college_heterogeneity_table.tex +0 -0
  92. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/college_mediation_additional_specs_table.tex +0 -0
  93. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/college_test_score_mediation_table.tex +0 -0
  94. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/earnings_heterogeneity_table.tex +0 -0
  95. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/earnings_robustness_table.tex +0 -0
  96. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/earnings_threshold_table.tex +0 -0
  97. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/intervention_calculation_table.tex +0 -0
  98. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/lottery_sample_selection_table.tex +0 -0
  99. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/other_mediators_table.tex +0 -0
  100. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/outputs/tables/output/summary_statistics_table.tex +0 -0
  101. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/paper/appendix.tex +0 -0
  102. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/paper/preamble.tex +0 -0
  103. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/latex_source/paper/tables-and-figures.tex +0 -0
  104. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/run_latex.sh +0 -0
  105. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/split_inputs.py +0 -0
  106. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/table_bindings.json +0 -0
  107. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/verify_exclusions.py +0 -0
  108. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/paper/verify_latex.py +0 -0
  109. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/plot_bars.py +0 -0
  110. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/plot_colleges.py +0 -0
  111. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/polars_dates.py +0 -0
  112. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/rocketship.py +0 -0
  113. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/schema_mapping.py +0 -0
  114. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/school_name_xwalk/__init__.py +0 -0
  115. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/school_name_xwalk/all_schools_with_ccd.csv +0 -0
  116. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/school_name_xwalk/merge_school_ccd.py +0 -0
  117. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/signal_var_calcs.py +0 -0
  118. {ltc_code-0.2.33 → ltc_code-0.2.34}/src/ltc_code/yes_prep.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ltc-code
3
- Version: 0.2.33
3
+ Version: 0.2.34
4
4
  Summary: Add your description here
5
5
  Requires-Dist: fastexcel>=0.16,<0.20
6
6
  Requires-Dist: polars>=1.36.1,<1.42
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ltc-code"
3
- version = "0.2.33"
3
+ version = "0.2.34"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -19,5 +19,5 @@ requires = ["uv_build>=0.11.16,<0.12.0"]
19
19
  build-backend = "uv_build"
20
20
 
21
21
  [tool.uv.build-backend]
22
- source-exclude = ["**/.DS_Store", "**/__pycache__/**", "**/*.pyc", "src/ltc_code/nsc/input/**", "src/ltc_code/nsc/output/**"]
23
- wheel-exclude = ["**/.DS_Store", "**/__pycache__/**", "**/*.pyc", "ltc_code/nsc/input/**", "ltc_code/nsc/output/**"]
22
+ source-exclude = ["**/.DS_Store", "**/__pycache__/**", "**/*.pyc", "src/ltc_code/nsc/input/**", "src/ltc_code/nsc/output/**", "src/ltc_code/tract_covars/inputs/*.csv", "src/ltc_code/tract_covars/outputs/**"]
23
+ wheel-exclude = ["**/.DS_Store", "**/__pycache__/**", "**/*.pyc", "ltc_code/nsc/input/**", "ltc_code/nsc/output/**", "ltc_code/tract_covars/inputs/*.csv", "ltc_code/tract_covars/outputs/**"]
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
- name: ltc_code-0.2.33/src/ltc_code/nsc/raw/directory.dta
2
+ name: ltc_code-0.2.34/src/ltc_code/nsc/raw/directory.dta
3
3
  size: 256195841 bytes
4
4
  sha256: 9fe2a62ad77a3a17f5c8f45df08cc2e8daeb46425eeb062a576353aa2bd463d1
@@ -0,0 +1,85 @@
1
+ # Tract poverty covariates
2
+
3
+ From the repository root, run:
4
+
5
+ ```sh
6
+ uv run --no-project --with pandas python src/ltc_code/tract_covars/build_tract_covars.py
7
+ ```
8
+
9
+ The script reads the two NHGIS CSVs in `inputs` and writes
10
+ `outputs/tract_covars.csv` and `outputs/tract_covars.dta`. Paths are relative
11
+ to the script, so it can also be run from another working directory. Pandas
12
+ is required for Stata export; the command above supplies it. If pandas is
13
+ already installed, the script can be run directly with `python3`.
14
+ The Stata file uses Stata 14+ format, string geographic identifiers (preserving
15
+ leading zeros), double-precision measures, and Stata missing numeric values.
16
+
17
+ For an installed package, install pandas separately, then run
18
+ `python -m ltc_code.tract_covars.build_tract_covars --inputs-dir /path/to/inputs --output /path/to/outputs/tract_covars.csv`.
19
+ The NHGIS CSV inputs and generated outputs are not bundled in the package.
20
+
21
+ The output contains one row per tract in the 2020 input, including Puerto Rico,
22
+ with exactly these seven columns:
23
+
24
+ | Column | Definition |
25
+ | --- | --- |
26
+ | state20 | Two-digit 2020 state FIPS code |
27
+ | county20 | Three-digit 2020 county FIPS code within state |
28
+ | tract20 | Six-digit 2020 tract code within county |
29
+ | poor_share2020 | Poverty share from the 2016–2020 ACS |
30
+ | poor_sharese2020 | Approximate standard error of that share |
31
+ | poor_share2024 | Poverty share from the 2020–2024 ACS |
32
+ | poor_sharese2024 | Approximate standard error of that share |
33
+
34
+ Import the three geographic columns as strings to retain leading zeros.
35
+ Their concatenation is the 11-digit tract GEOID. Shares and standard errors
36
+ are in proportion units, not percentage points. Blank output fields are missing.
37
+
38
+ ## Calculation
39
+
40
+ Table B17001 gives the population for whom poverty status is determined
41
+ (`AM63E001` in 2020; `AU77E001` in 2024) and the subset below the poverty
42
+ level (`AM63E002`; `AU77E002`). Let these be `T` and `P`.
43
+ The corresponding `M001` and `M002` fields are 90% margins of error.
44
+
45
+ ```text
46
+ p = P / T
47
+ SE_T = MOE_T / 1.645
48
+ SE_P = MOE_P / 1.645
49
+ SE_p = sqrt(SE_P**2 - p**2 * SE_T**2) / T
50
+ ```
51
+
52
+ If the quantity under the square root is negative, use addition in place of
53
+ subtraction, following the Census ratio fallback. These are approximate SEs
54
+ derived from published margins of error, not replicate-weight SEs. A zero or
55
+ missing denominator makes both outputs missing; missing MOEs make only the
56
+ SE missing. Negative input sentinel values are treated as missing.
57
+
58
+ Source: [Census ACS worked examples, proportions and ratios, pp. 6–7](https://www2.census.gov/programs-surveys/acs/tech_docs/accuracy/2023_ACS_Accuracy_Document_Worked_Examples.pdf).
59
+
60
+ ## Geographic vintage and matching
61
+
62
+ Both the [2020 ACS](https://www.census.gov/programs-surveys/acs/geography-acs/geography-boundaries-by-year/2020.html)
63
+ and [2024 ACS](https://www.census.gov/programs-surveys/acs/geography-acs/geography-boundaries-by-year/2024.html)
64
+ use 2020 Census tract definitions, with some intervening geographic changes.
65
+ This output uses the **2020 input's tract list and county identifiers** as its
66
+ reference geography. It is a left join onto that list, not an inner join.
67
+
68
+ Connecticut replaced its counties with planning regions. Its land tract codes
69
+ remain unchanged and are unique statewide in both inputs. The script checks
70
+ these conditions and matches those tracts by state and tract, restoring the
71
+ 2020 county codes. The four 2020 Connecticut water tracts remain as rows with
72
+ missing poverty measures. The five 2024 water tracts are not appended or
73
+ matched across altered boundaries; their poverty denominators are all zero.
74
+ See [Census Planning Database documentation, Appendix D](https://www2.census.gov/adrm/PDB/2024/2024_PDB_Documentation.pdf)
75
+ for the Connecticut geography change and water-tract split.
76
+
77
+ All other tracts are matched on state, county, and tract. In the supplied files,
78
+ 14 Suffolk County, New York tracts occur only in 2020; their 2024 measures are
79
+ left missing. The script prints their GEOIDs. It does not impute these values
80
+ or claim that identical identifiers guarantee unchanged boundaries everywhere.
81
+ Unexpected unmatched 2024 land tracts or duplicate keys stop the build.
82
+
83
+ The supplied inputs produce 85,395 rows. Both outputs are excluded from Git by the
84
+ repository's existing data-file ignore rules and can be regenerated with the
85
+ command above. See the input codebooks for NHGIS attribution and terms.
@@ -0,0 +1,171 @@
1
+ """Build tract poverty shares and approximate standard errors from NHGIS ACS inputs.
2
+
3
+ Run with Python 3.9+ and pandas. See README.md
4
+ for the geography handling and Census Bureau sources for the SE formula.
5
+ """
6
+
7
+ import argparse
8
+ import csv
9
+ import math
10
+ from pathlib import Path
11
+ from typing import Dict, Optional, Tuple
12
+
13
+
14
+ HERE = Path(__file__).resolve().parent
15
+ INPUTS = {
16
+ 2020: ("nhgis0006_ds250_20205_tract.csv", "AM63"),
17
+ 2024: ("nhgis0006_ds273_20245_tract.csv", "AU77"),
18
+ }
19
+ FIELDS = [
20
+ "state20", "county20", "tract20",
21
+ "poor_share2020", "poor_sharese2020",
22
+ "poor_share2024", "poor_sharese2024",
23
+ ]
24
+ Key = Tuple[str, str, str]
25
+ Estimate = Tuple[Optional[float], Optional[float]]
26
+
27
+
28
+ def nonnegative(value: str) -> Optional[float]:
29
+ """Treat blank and negative ACS/NHGIS sentinel values as missing."""
30
+ if not value.strip():
31
+ return None
32
+ number = float(value)
33
+ return number if math.isfinite(number) and number >= 0 else None
34
+
35
+
36
+ def poverty_estimate(row: Dict[str, str], prefix: str) -> Estimate:
37
+ total = nonnegative(row[prefix + "E001"])
38
+ poor = nonnegative(row[prefix + "E002"])
39
+ if total is None or total == 0 or poor is None:
40
+ return None, None
41
+ if poor > total:
42
+ raise ValueError("Poverty count exceeds the poverty-status universe.")
43
+ share = poor / total
44
+ total_moe = nonnegative(row[prefix + "M001"])
45
+ poor_moe = nonnegative(row[prefix + "M002"])
46
+ if total_moe is None or poor_moe is None:
47
+ return share, None
48
+ # ACS MOEs use 90% confidence. The numerator is a subset of the denominator.
49
+ poor_var = (poor_moe / 1.645) ** 2
50
+ total_term = share ** 2 * (total_moe / 1.645) ** 2
51
+ variance = poor_var - total_term
52
+ if variance < 0:
53
+ # Census recommends the ratio formula if the proportion radicand is negative.
54
+ variance = poor_var + total_term
55
+ return share, math.sqrt(variance) / total
56
+
57
+
58
+ def read_year(inputs_dir: Path, year: int) -> Dict[Key, Estimate]:
59
+ filename, prefix = INPUTS[year]
60
+ estimates = {}
61
+ with (inputs_dir / filename).open(newline="", encoding="utf-8-sig") as source:
62
+ reader = csv.DictReader(source)
63
+ required = {"STATEA", "COUNTYA", "TRACTA"} | {
64
+ prefix + suffix for suffix in ("E001", "E002", "M001", "M002")
65
+ }
66
+ if not required.issubset(reader.fieldnames or []):
67
+ raise ValueError(f"Missing required columns in {filename}")
68
+ for row in reader:
69
+ key = tuple(row[name].strip() for name in ("STATEA", "COUNTYA", "TRACTA"))
70
+ if any(not code.isdigit() or len(code) != width
71
+ for code, width in zip(key, (2, 3, 6))):
72
+ raise ValueError(f"Invalid geographic identifier: {key}")
73
+ if key in estimates:
74
+ raise ValueError(f"Duplicate tract in {year}: {key}")
75
+ estimates[key] = poverty_estimate(row, prefix)
76
+ return estimates
77
+
78
+
79
+ def ct_land_keys(estimates: Dict[Key, Estimate]) -> Dict[str, Key]:
80
+ """CT land tract codes are unique statewide and unchanged across these inputs."""
81
+ lookup = {}
82
+ for key, values in estimates.items():
83
+ state, _, tract = key
84
+ if state != "09":
85
+ continue
86
+ if tract.startswith("99"):
87
+ # Water tracts changed boundaries; never assign them to a land tract.
88
+ if values != (None, None):
89
+ raise ValueError(f"Unexpected populated CT water tract: {key}")
90
+ continue
91
+ if tract in lookup:
92
+ raise ValueError(f"Ambiguous Connecticut land tract: {tract}")
93
+ lookup[tract] = key
94
+ return lookup
95
+
96
+
97
+ def build(inputs_dir: Path, output: Path) -> None:
98
+ import pandas as pd
99
+
100
+ old = read_year(inputs_dir, 2020)
101
+ new = read_year(inputs_dir, 2024)
102
+ ct_old, ct_new = ct_land_keys(old), ct_land_keys(new)
103
+ if ct_old.keys() != ct_new.keys():
104
+ raise ValueError("Connecticut land tract codes differ; a new crosswalk is needed.")
105
+
106
+ rows = []
107
+ used = set()
108
+ unmatched = []
109
+ for key in sorted(old):
110
+ state, county, tract = key
111
+ new_key = ct_new.get(tract) if state == "09" else key
112
+ values = new.get(new_key, (None, None))
113
+ if new_key in new:
114
+ used.add(new_key)
115
+ elif not (state == "09" and tract.startswith("99")):
116
+ unmatched.append("".join(key))
117
+ rows.append((state, county, tract, *old[key], *values))
118
+
119
+ # Only unpopulated Connecticut water tracts may be excluded from 2024.
120
+ unexpected = [key for key in new.keys() - used
121
+ if not (key[0] == "09" and key[2].startswith("99")
122
+ and new[key] == (None, None))]
123
+ if unexpected:
124
+ raise ValueError(f"2024 tracts need an explicit mapping to 2020: {unexpected}")
125
+
126
+ output.parent.mkdir(parents=True, exist_ok=True)
127
+ with output.open("w", newline="", encoding="utf-8") as target:
128
+ writer = csv.writer(target)
129
+ writer.writerow(FIELDS)
130
+ writer.writerows(rows)
131
+ stata_output = output.with_suffix(".dta")
132
+ frame = pd.DataFrame(rows, columns=FIELDS)
133
+ frame[FIELDS[3:]] = frame[FIELDS[3:]].astype("float64")
134
+ frame.to_stata(
135
+ stata_output,
136
+ write_index=False,
137
+ version=118,
138
+ data_label="ACS tract poverty shares, 2020 tract identifiers",
139
+ variable_labels={
140
+ "state20": "2020 state FIPS code",
141
+ "county20": "2020 county FIPS code within state",
142
+ "tract20": "2020 tract code within county",
143
+ "poor_share2020": "Poverty share, 2016-2020 ACS (0-1)",
144
+ "poor_sharese2020": "Approximate SE of poverty share, 2016-2020 ACS",
145
+ "poor_share2024": "Poverty share, 2020-2024 ACS (0-1)",
146
+ "poor_sharese2024": "Approximate SE of poverty share, 2020-2024 ACS",
147
+ },
148
+ )
149
+ print(f"Wrote {len(rows):,} unique 2020 tracts to {output}")
150
+ print(f"Wrote Stata dataset to {stata_output}")
151
+ print(f"Mapped {len(ct_old):,} Connecticut land tracts to 2020 county codes.")
152
+ print(f"Tracts without a 2024 match (excluding CT water): {len(unmatched)}")
153
+ if unmatched:
154
+ print(" " + ", ".join(unmatched))
155
+ for index, field in enumerate(FIELDS[3:], start=3):
156
+ print(f"{field}: {sum(row[index] is None for row in rows):,} missing")
157
+
158
+
159
+ def main() -> None:
160
+ parser = argparse.ArgumentParser(description=__doc__)
161
+ parser.add_argument("--inputs-dir", type=Path, default=HERE / "inputs")
162
+ parser.add_argument("--output", type=Path,
163
+ default=HERE / "outputs" / "tract_covars.csv")
164
+ args = parser.parse_args()
165
+ if args.output.suffix.lower() != ".csv":
166
+ parser.error("--output must be a .csv path; a sibling .dta is also written")
167
+ build(args.inputs_dir, args.output)
168
+
169
+
170
+ if __name__ == "__main__":
171
+ main()
@@ -0,0 +1,263 @@
1
+ --------------------------------------------------------------------------------
2
+ Codebook for NHGIS data file 'nhgis0006_ds250_20205_tract'
3
+ --------------------------------------------------------------------------------
4
+
5
+ Contents
6
+ - Data Summary
7
+ - Data Dictionary
8
+ - Citation and Use
9
+
10
+ Additional documentation on NHGIS data sources is available at:
11
+ https://www.nhgis.org/documentation/tabular-data
12
+
13
+ --------------------------------------------------------------------------------
14
+ Data Summary
15
+ --------------------------------------------------------------------------------
16
+
17
+ Year: 2016-2020
18
+ Geographic level: Census Tract (by State--County)
19
+ Extent: All areas
20
+ Dataset: 2020 American Community Survey: 5-Year Data [2016-2020, Tracts & Larger Areas]
21
+ NHGIS code: 2016_2020_ACS5b
22
+ NHGIS ID: ds250
23
+ Breakdown(s): Geographic Component:
24
+ Total area (00)
25
+ Data type(s): (E) Estimates
26
+ (M) Margins of error
27
+
28
+ Tables:
29
+
30
+ 1. Poverty Status in the Past 12 Months by Sex by Age
31
+ Universe: Population for whom poverty status is determined
32
+ Source code: B17001
33
+ NHGIS code: AM63
34
+
35
+ --------------------------------------------------------------------------------
36
+ Data Dictionary
37
+ --------------------------------------------------------------------------------
38
+
39
+ Context Fields
40
+ GISJOIN: GIS Join Match Code
41
+ YEAR: Data File Year
42
+ STUSAB: State Postal Abbreviation
43
+ REGIONA: Region Code
44
+ DIVISIONA: Division Code
45
+ STATE: State Name
46
+ STATEA: State Code
47
+ COUNTY: County Name
48
+ COUNTYA: County Code
49
+ COUSUBA: County Subdivision Code
50
+ PLACEA: Place Code
51
+ TRACTA: Census Tract Code
52
+ CONCITA: Consolidated City Code
53
+ AIANHHA: American Indian Area/Alaska Native Area/Hawaiian Home Land Code
54
+ RES_ONLYA: American Indian Area/Alaska Native Area (Reservation or Statistical Entity Only) Code
55
+ TRUSTA: American Indian Area (Off-Reservation Trust Land Only)/Hawaiian Home Land Code
56
+ AIHHTLI: American Indian Trust Land/ Hawaiian Home Land Indicator
57
+ AITSCEA: Tribal Subdivision/Remainder Code
58
+ ANRCA: Alaska Native Regional Corporation Code
59
+ CBSAA: Metropolitan Statistical Area/Micropolitan Statistical Area Code
60
+ CSAA: Combined Statistical Area Code
61
+ METDIVA: Metropolitan Division Code
62
+ NECTAA: New England City and Town Area Code
63
+ CNECTAA: Combined New England City and Town Area Code
64
+ NECTADIVA: New England City and Town Area Division Code
65
+ UAA: Urban Area Code
66
+ CDCURRA: Congressional District (2019-2021, 116th Congress) Code
67
+ SLDUA: State Legislative District (Upper Chamber) Code
68
+ SLDLA: State Legislative District (Lower Chamber) Code
69
+ ZCTA5A: 5-Digit ZIP Code Tabulation Area Code
70
+ SUBMCDA: Subminor Civil Division Code
71
+ SDELMA: School District (Elementary)/Remainder Code
72
+ SDSECA: School District (Secondary)/Remainder Code
73
+ SDUNIA: School District (Unified)/Remainder Code
74
+ PCI: Principal City Indicator
75
+ PUMAA: Public Use Microdata Area Code
76
+ GEOID: Census Geographic Identifier
77
+ BTTRA: Tribal Census Tract Code
78
+
79
+ Data Type (E):
80
+ Estimates
81
+
82
+ NAME_E: Area Name
83
+
84
+ Table 1: Poverty Status in the Past 12 Months by Sex by Age
85
+ Universe: Population for whom poverty status is determined
86
+ Source code: B17001
87
+ NHGIS code: AM63
88
+ AM63E001: Total
89
+ AM63E002: Income in the past 12 months below poverty level
90
+ AM63E003: Income in the past 12 months below poverty level: Male
91
+ AM63E004: Income in the past 12 months below poverty level: Male: Under 5 years
92
+ AM63E005: Income in the past 12 months below poverty level: Male: 5 years
93
+ AM63E006: Income in the past 12 months below poverty level: Male: 6 to 11 years
94
+ AM63E007: Income in the past 12 months below poverty level: Male: 12 to 14 years
95
+ AM63E008: Income in the past 12 months below poverty level: Male: 15 years
96
+ AM63E009: Income in the past 12 months below poverty level: Male: 16 and 17 years
97
+ AM63E010: Income in the past 12 months below poverty level: Male: 18 to 24 years
98
+ AM63E011: Income in the past 12 months below poverty level: Male: 25 to 34 years
99
+ AM63E012: Income in the past 12 months below poverty level: Male: 35 to 44 years
100
+ AM63E013: Income in the past 12 months below poverty level: Male: 45 to 54 years
101
+ AM63E014: Income in the past 12 months below poverty level: Male: 55 to 64 years
102
+ AM63E015: Income in the past 12 months below poverty level: Male: 65 to 74 years
103
+ AM63E016: Income in the past 12 months below poverty level: Male: 75 years and over
104
+ AM63E017: Income in the past 12 months below poverty level: Female
105
+ AM63E018: Income in the past 12 months below poverty level: Female: Under 5 years
106
+ AM63E019: Income in the past 12 months below poverty level: Female: 5 years
107
+ AM63E020: Income in the past 12 months below poverty level: Female: 6 to 11 years
108
+ AM63E021: Income in the past 12 months below poverty level: Female: 12 to 14 years
109
+ AM63E022: Income in the past 12 months below poverty level: Female: 15 years
110
+ AM63E023: Income in the past 12 months below poverty level: Female: 16 and 17 years
111
+ AM63E024: Income in the past 12 months below poverty level: Female: 18 to 24 years
112
+ AM63E025: Income in the past 12 months below poverty level: Female: 25 to 34 years
113
+ AM63E026: Income in the past 12 months below poverty level: Female: 35 to 44 years
114
+ AM63E027: Income in the past 12 months below poverty level: Female: 45 to 54 years
115
+ AM63E028: Income in the past 12 months below poverty level: Female: 55 to 64 years
116
+ AM63E029: Income in the past 12 months below poverty level: Female: 65 to 74 years
117
+ AM63E030: Income in the past 12 months below poverty level: Female: 75 years and over
118
+ AM63E031: Income in the past 12 months at or above poverty level
119
+ AM63E032: Income in the past 12 months at or above poverty level: Male
120
+ AM63E033: Income in the past 12 months at or above poverty level: Male: Under 5 years
121
+ AM63E034: Income in the past 12 months at or above poverty level: Male: 5 years
122
+ AM63E035: Income in the past 12 months at or above poverty level: Male: 6 to 11 years
123
+ AM63E036: Income in the past 12 months at or above poverty level: Male: 12 to 14 years
124
+ AM63E037: Income in the past 12 months at or above poverty level: Male: 15 years
125
+ AM63E038: Income in the past 12 months at or above poverty level: Male: 16 and 17 years
126
+ AM63E039: Income in the past 12 months at or above poverty level: Male: 18 to 24 years
127
+ AM63E040: Income in the past 12 months at or above poverty level: Male: 25 to 34 years
128
+ AM63E041: Income in the past 12 months at or above poverty level: Male: 35 to 44 years
129
+ AM63E042: Income in the past 12 months at or above poverty level: Male: 45 to 54 years
130
+ AM63E043: Income in the past 12 months at or above poverty level: Male: 55 to 64 years
131
+ AM63E044: Income in the past 12 months at or above poverty level: Male: 65 to 74 years
132
+ AM63E045: Income in the past 12 months at or above poverty level: Male: 75 years and over
133
+ AM63E046: Income in the past 12 months at or above poverty level: Female
134
+ AM63E047: Income in the past 12 months at or above poverty level: Female: Under 5 years
135
+ AM63E048: Income in the past 12 months at or above poverty level: Female: 5 years
136
+ AM63E049: Income in the past 12 months at or above poverty level: Female: 6 to 11 years
137
+ AM63E050: Income in the past 12 months at or above poverty level: Female: 12 to 14 years
138
+ AM63E051: Income in the past 12 months at or above poverty level: Female: 15 years
139
+ AM63E052: Income in the past 12 months at or above poverty level: Female: 16 and 17 years
140
+ AM63E053: Income in the past 12 months at or above poverty level: Female: 18 to 24 years
141
+ AM63E054: Income in the past 12 months at or above poverty level: Female: 25 to 34 years
142
+ AM63E055: Income in the past 12 months at or above poverty level: Female: 35 to 44 years
143
+ AM63E056: Income in the past 12 months at or above poverty level: Female: 45 to 54 years
144
+ AM63E057: Income in the past 12 months at or above poverty level: Female: 55 to 64 years
145
+ AM63E058: Income in the past 12 months at or above poverty level: Female: 65 to 74 years
146
+ AM63E059: Income in the past 12 months at or above poverty level: Female: 75 years and over
147
+
148
+ Data Type (M):
149
+ Margins of error
150
+
151
+ NAME_M: Area Name
152
+
153
+ Table 1: Poverty Status in the Past 12 Months by Sex by Age
154
+ Universe: Population for whom poverty status is determined
155
+ Source code: B17001
156
+ NHGIS code: AM63
157
+ AM63M001: Total
158
+ AM63M002: Income in the past 12 months below poverty level
159
+ AM63M003: Income in the past 12 months below poverty level: Male
160
+ AM63M004: Income in the past 12 months below poverty level: Male: Under 5 years
161
+ AM63M005: Income in the past 12 months below poverty level: Male: 5 years
162
+ AM63M006: Income in the past 12 months below poverty level: Male: 6 to 11 years
163
+ AM63M007: Income in the past 12 months below poverty level: Male: 12 to 14 years
164
+ AM63M008: Income in the past 12 months below poverty level: Male: 15 years
165
+ AM63M009: Income in the past 12 months below poverty level: Male: 16 and 17 years
166
+ AM63M010: Income in the past 12 months below poverty level: Male: 18 to 24 years
167
+ AM63M011: Income in the past 12 months below poverty level: Male: 25 to 34 years
168
+ AM63M012: Income in the past 12 months below poverty level: Male: 35 to 44 years
169
+ AM63M013: Income in the past 12 months below poverty level: Male: 45 to 54 years
170
+ AM63M014: Income in the past 12 months below poverty level: Male: 55 to 64 years
171
+ AM63M015: Income in the past 12 months below poverty level: Male: 65 to 74 years
172
+ AM63M016: Income in the past 12 months below poverty level: Male: 75 years and over
173
+ AM63M017: Income in the past 12 months below poverty level: Female
174
+ AM63M018: Income in the past 12 months below poverty level: Female: Under 5 years
175
+ AM63M019: Income in the past 12 months below poverty level: Female: 5 years
176
+ AM63M020: Income in the past 12 months below poverty level: Female: 6 to 11 years
177
+ AM63M021: Income in the past 12 months below poverty level: Female: 12 to 14 years
178
+ AM63M022: Income in the past 12 months below poverty level: Female: 15 years
179
+ AM63M023: Income in the past 12 months below poverty level: Female: 16 and 17 years
180
+ AM63M024: Income in the past 12 months below poverty level: Female: 18 to 24 years
181
+ AM63M025: Income in the past 12 months below poverty level: Female: 25 to 34 years
182
+ AM63M026: Income in the past 12 months below poverty level: Female: 35 to 44 years
183
+ AM63M027: Income in the past 12 months below poverty level: Female: 45 to 54 years
184
+ AM63M028: Income in the past 12 months below poverty level: Female: 55 to 64 years
185
+ AM63M029: Income in the past 12 months below poverty level: Female: 65 to 74 years
186
+ AM63M030: Income in the past 12 months below poverty level: Female: 75 years and over
187
+ AM63M031: Income in the past 12 months at or above poverty level
188
+ AM63M032: Income in the past 12 months at or above poverty level: Male
189
+ AM63M033: Income in the past 12 months at or above poverty level: Male: Under 5 years
190
+ AM63M034: Income in the past 12 months at or above poverty level: Male: 5 years
191
+ AM63M035: Income in the past 12 months at or above poverty level: Male: 6 to 11 years
192
+ AM63M036: Income in the past 12 months at or above poverty level: Male: 12 to 14 years
193
+ AM63M037: Income in the past 12 months at or above poverty level: Male: 15 years
194
+ AM63M038: Income in the past 12 months at or above poverty level: Male: 16 and 17 years
195
+ AM63M039: Income in the past 12 months at or above poverty level: Male: 18 to 24 years
196
+ AM63M040: Income in the past 12 months at or above poverty level: Male: 25 to 34 years
197
+ AM63M041: Income in the past 12 months at or above poverty level: Male: 35 to 44 years
198
+ AM63M042: Income in the past 12 months at or above poverty level: Male: 45 to 54 years
199
+ AM63M043: Income in the past 12 months at or above poverty level: Male: 55 to 64 years
200
+ AM63M044: Income in the past 12 months at or above poverty level: Male: 65 to 74 years
201
+ AM63M045: Income in the past 12 months at or above poverty level: Male: 75 years and over
202
+ AM63M046: Income in the past 12 months at or above poverty level: Female
203
+ AM63M047: Income in the past 12 months at or above poverty level: Female: Under 5 years
204
+ AM63M048: Income in the past 12 months at or above poverty level: Female: 5 years
205
+ AM63M049: Income in the past 12 months at or above poverty level: Female: 6 to 11 years
206
+ AM63M050: Income in the past 12 months at or above poverty level: Female: 12 to 14 years
207
+ AM63M051: Income in the past 12 months at or above poverty level: Female: 15 years
208
+ AM63M052: Income in the past 12 months at or above poverty level: Female: 16 and 17 years
209
+ AM63M053: Income in the past 12 months at or above poverty level: Female: 18 to 24 years
210
+ AM63M054: Income in the past 12 months at or above poverty level: Female: 25 to 34 years
211
+ AM63M055: Income in the past 12 months at or above poverty level: Female: 35 to 44 years
212
+ AM63M056: Income in the past 12 months at or above poverty level: Female: 45 to 54 years
213
+ AM63M057: Income in the past 12 months at or above poverty level: Female: 55 to 64 years
214
+ AM63M058: Income in the past 12 months at or above poverty level: Female: 65 to 74 years
215
+ AM63M059: Income in the past 12 months at or above poverty level: Female: 75 years and over
216
+
217
+ --------------------------------------------------------------------------------
218
+ Citation and Use of NHGIS Data
219
+ --------------------------------------------------------------------------------
220
+
221
+ All persons are granted a limited license to use this documentation and the
222
+ accompanying data, subject to the following conditions:
223
+
224
+ * REDISTRIBUTION: You will not redistribute the data without permission.
225
+
226
+ You may publish a subset of the data to meet journal requirements for accessing
227
+ data related to a particular publication. Contact us for permission for any other
228
+ redistribution; we will consider requests for free and commercial redistribution.
229
+
230
+ * CITATION: You will cite NHGIS appropriately.
231
+
232
+ * Publications and research reports employing NHGIS data (either tabular or GIS)
233
+ should include the following citation:
234
+
235
+ Jonathan Schroeder, David Van Riper, Steven Manson, Grace Cooper, Zachary Krause, Tracy Kugler, Tsu Zhu, and Steven Ruggles.
236
+ IPUMS National Historical Geographic Information System: Version 21.0
237
+ [dataset]. Minneapolis, MN: IPUMS. 2026.
238
+ http://doi.org/10.18128/D050.V21.0
239
+
240
+ * For policy briefs, online resources, or articles in the popular press, we
241
+ recommend that you cite the use of NHGIS data as follows:
242
+
243
+ IPUMS NHGIS, University of Minnesota, www.nhgis.org.
244
+
245
+ These terms of use are a legally binding agreement. You can use the data only in
246
+ accordance with these terms, and any other use is a violation of the agreement.
247
+ Violations may result in revocation of the agreement and prohibition from using other
248
+ IPUMS data. If IPUMS or our partners are harmed from your violation, you are
249
+ responsible for all damages, including reasonable attorney's fees and expenses.
250
+
251
+ In addition, we request that users send us a copy of any publications, research
252
+ reports, or educational material making use of the data or documentation.
253
+
254
+ Send electronic material to: nhgis@umn.edu
255
+
256
+ Printed matter should be sent to:
257
+
258
+ IPUMS NHGIS
259
+ Institute for Social Research and Data Innovation
260
+ University of Minnesota
261
+ 50 Willey Hall
262
+ 225 19th Ave S
263
+ Minneapolis, MN 55455