ltc-code 0.2.25__tar.gz → 0.2.27__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 (70) hide show
  1. {ltc_code-0.2.25 → ltc_code-0.2.27}/PKG-INFO +1 -1
  2. {ltc_code-0.2.25 → ltc_code-0.2.27}/pyproject.toml +1 -1
  3. {ltc_code-0.2.25 → ltc_code-0.2.27}/pyproject.toml.orig +1 -1
  4. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/build_nsc_outcomes.py +195 -48
  5. {ltc_code-0.2.25 → ltc_code-0.2.27}/README.md +0 -0
  6. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/aspire.py +0 -0
  7. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/check_cmo_apps.do +0 -0
  8. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/christel_house.py +0 -0
  9. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/democracy_prep.py +0 -0
  10. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/green_dot.py +0 -0
  11. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/helpers.py +0 -0
  12. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/ilt.py +0 -0
  13. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/kipp_nj.py +0 -0
  14. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/main.py +0 -0
  15. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/mappings.py +0 -0
  16. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/rocketship.py +0 -0
  17. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260614_new_build_scripts_update/yes_prep.py +0 -0
  18. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/aspire.py +0 -0
  19. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/christel_house.py +0 -0
  20. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/democracy_prep.py +0 -0
  21. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/green_dot.py +0 -0
  22. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/ilt.py +0 -0
  23. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/kipp.py +0 -0
  24. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/kipp_nj.py +0 -0
  25. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/rocketship.py +0 -0
  26. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260630_census_disclosure/yes_prep.py +0 -0
  27. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260706_ceprscripts/BALANCE.do +0 -0
  28. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260706_ceprscripts/FS.do +0 -0
  29. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260706_ceprscripts/ITT.do +0 -0
  30. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260706_ceprscripts/TOT.do +0 -0
  31. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260706_ceprscripts/apps_helpers.py +0 -0
  32. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260706_ceprscripts/harmony.py +0 -0
  33. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260706_ceprscripts/main.py +0 -0
  34. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260712_uncommon_scripts/main.py +0 -0
  35. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260712_uncommon_scripts/mappings.py +0 -0
  36. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/20260712_uncommon_scripts/uncommon.py +0 -0
  37. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/__init__.py +0 -0
  38. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/aspire.py +0 -0
  39. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/check_cmo_apps.do +0 -0
  40. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/christel_house.py +0 -0
  41. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/green_dot.py +0 -0
  42. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/helpers.py +0 -0
  43. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/june13.py +0 -0
  44. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/june2.py +0 -0
  45. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/june30.py +0 -0
  46. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/june5.py +0 -0
  47. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/june7.py +0 -0
  48. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/kipp_nj.py +0 -0
  49. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/kipp_tx.py +0 -0
  50. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/main.py +0 -0
  51. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/make_summary_stats_table.py +0 -0
  52. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/mappings.py +0 -0
  53. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/may27.py +0 -0
  54. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/__init__.py +0 -0
  55. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/dhs_stem/dhs_stem_cip_additions_2024.csv +0 -0
  56. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/dhs_stem/extract_dhs_stem_cips.R +0 -0
  57. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/dhs_stem/stemList2024.pdf +0 -0
  58. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/input/.gitkeep +0 -0
  59. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/naics.py +0 -0
  60. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/output/.gitkeep +0 -0
  61. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/raw/ipeds_data.dta +0 -0
  62. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/nsc/run_nsc_outcomes.py +0 -0
  63. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/polars_dates.py +0 -0
  64. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/rocketship.py +0 -0
  65. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/schema_mapping.py +0 -0
  66. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/school_name_xwalk/__init__.py +0 -0
  67. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/school_name_xwalk/all_schools_with_ccd.csv +0 -0
  68. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/school_name_xwalk/merge_school_ccd.py +0 -0
  69. {ltc_code-0.2.25 → ltc_code-0.2.27}/src/ltc_code/signal_var_calcs.py +0 -0
  70. {ltc_code-0.2.25 → ltc_code-0.2.27}/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.25
3
+ Version: 0.2.27
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.25"
3
+ version = "0.2.27"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ltc-code"
3
- version = "0.2.25"
3
+ version = "0.2.27"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -461,7 +461,7 @@ ipeds_completion = (
461
461
 
462
462
  # Use the graduation cohort itself as the student weight. Summing completers and
463
463
  # cohorts is equivalent to weighting each college's rate by its IPEDS denominator.
464
- ipeds_sector_rates = (
464
+ ipeds_completion_with_sector = (
465
465
  ipeds_completion
466
466
  .join(
467
467
  ipeds_2013.select("unitid", "years_ipeds_2013"),
@@ -501,6 +501,10 @@ ipeds_sector_rates = (
501
501
  .otherwise(pl.lit("2yr_or_less"))
502
502
  .alias("college_sector")
503
503
  )
504
+ )
505
+
506
+ ipeds_sector_rates = (
507
+ ipeds_completion_with_sector
504
508
  .group_by("college_sector")
505
509
  .agg(
506
510
  pl.col("completers_150pct_ip").sum().alias("completers"),
@@ -1120,6 +1124,7 @@ enroll = (
1120
1124
  pl.col("cohort_18").first(),
1121
1125
  pl.col("college").first(),
1122
1126
  pl.col("college_name").first(),
1127
+ pl.col("unitid").drop_nulls().first(),
1123
1128
  pl.col("college_years").first(),
1124
1129
  pl.col("college_any").max(),
1125
1130
  pl.col("college_4yr").max(),
@@ -1331,6 +1336,7 @@ first_institution = (
1331
1336
  .first()
1332
1337
  .alias("college_name_firstinst"),
1333
1338
  pl.col("college_years").first().alias("college_years_firstinst"),
1339
+ pl.col("unitid").first().alias("unitid_firstinst"),
1334
1340
  pl.col("completion_rate_150pct_ip")
1335
1341
  .first()
1336
1342
  .alias("completion_rate_150pct_firstinst"),
@@ -1725,61 +1731,163 @@ for age in AGE_ATTENDANCE_RANGE:
1725
1731
  )
1726
1732
 
1727
1733
 
1728
- # Define expected completion over the cumulative by-Y4 attendance window. The
1729
- # detailed measure uses the first institution's rate (with the tier-median fill
1730
- # above), gives observed nonattenders zero, and leaves attendee rates missing
1731
- # when neither the institution rate nor its tier median is available.
1732
- nsc_outcomes = nsc_outcomes.with_columns(
1733
- pl.when(pl.col("att_any_byY4") == 0)
1734
- .then(None)
1735
- .otherwise(pl.col("completion_rate_150pct_ip"))
1736
- .alias("completion_rate_150pct_ip")
1734
+ # Calculate coarse averages, restricted to the first institutions among observed by-Y4 attendees
1735
+ sample_firstinst_unitids = (
1736
+ nsc_outcomes.filter(pl.col("att_any_byY4") == 1)
1737
+ .select(pl.col("unitid_firstinst").alias("unitid"))
1738
+ .drop_nulls()
1739
+ .unique()
1740
+ )
1741
+
1742
+ ipeds_sample_sector_rates = (
1743
+ ipeds_completion_with_sector.join(
1744
+ sample_firstinst_unitids,
1745
+ on="unitid",
1746
+ how="semi",
1747
+ )
1748
+ .group_by("college_sector")
1749
+ .agg(
1750
+ pl.len().alias("institutions"),
1751
+ pl.col("completers_150pct_ip").sum().alias("completers"),
1752
+ pl.col("cohort_adj_150pct_ip").sum().alias("cohort"),
1753
+ )
1754
+ .with_columns(
1755
+ (pl.col("completers") / pl.col("cohort")).alias("cmp_rate_coarse_sample")
1756
+ )
1737
1757
  )
1738
1758
 
1759
+ cmp_rate_4yr_coarse_sample = ipeds_sample_sector_rates.filter(
1760
+ pl.col("college_sector") == "4yr"
1761
+ ).item(0, "cmp_rate_coarse_sample")
1762
+ cmp_rate_2yr_coarse_sample = ipeds_sample_sector_rates.filter(
1763
+ pl.col("college_sector") == "2yr_or_less"
1764
+ ).item(0, "cmp_rate_coarse_sample")
1765
+ sample_4yr_institutions = ipeds_sample_sector_rates.filter(
1766
+ pl.col("college_sector") == "4yr"
1767
+ ).item(0, "institutions")
1768
+ sample_2yr_institutions = ipeds_sample_sector_rates.filter(
1769
+ pl.col("college_sector") == "2yr_or_less"
1770
+ ).item(0, "institutions")
1771
+
1772
+ for label, value in {
1773
+ "sample four-year completion rate": cmp_rate_4yr_coarse_sample,
1774
+ "sample two-year-or-less completion rate": cmp_rate_2yr_coarse_sample,
1775
+ }.items():
1776
+ if value is None or not 0 <= value <= 1:
1777
+ raise ValueError(f"Invalid IPEDS-cohort-weighted {label}: {value}")
1778
+
1779
+
1780
+ # Define completion-rate measures over the cumulative by-Y4 attendance window, just for attendees
1739
1781
  nsc_outcomes = nsc_outcomes.with_columns(
1740
- pl.col("completion_rate_150pct_ip").alias("cmp_rate"),
1741
- pl.when(pl.col("att_any_byY4").is_null())
1742
- .then(None)
1743
- .when(pl.col("att_4yr_byY4") == 1)
1744
- .then(cmp_rate_4yr_coarse)
1745
- .when(pl.col("att_2yr_byY4") == 1)
1746
- .then(cmp_rate_2yr_coarse)
1747
- .when(pl.col("att_any_byY4") == 0)
1748
- .then(0.0)
1782
+ # Detailed cmp rate
1783
+ pl.when(pl.col("att_any_byY4") == 1)
1784
+ .then(pl.col("completion_rate_150pct_ip"))
1785
+ .otherwise(None)
1786
+ .alias("cmp_rate"),
1787
+ # Coarse cmp rate
1788
+ pl.when(pl.col("att_any_byY4") == 1)
1789
+ .then(
1790
+ pl.when(pl.col("college_years_firstinst") == 4)
1791
+ .then(cmp_rate_4yr_coarse)
1792
+ .when(pl.col("college_years_firstinst").is_in([1, 2]))
1793
+ .then(cmp_rate_2yr_coarse)
1794
+ .otherwise(None)
1795
+ )
1749
1796
  .otherwise(None)
1750
1797
  .alias("cmp_rate_coarse"),
1798
+ # Coarse cmp rate, using in-sample avgs
1799
+ pl.when(pl.col("att_any_byY4") == 1)
1800
+ .then(
1801
+ pl.when(pl.col("college_years_firstinst") == 4)
1802
+ .then(cmp_rate_4yr_coarse_sample)
1803
+ .when(pl.col("college_years_firstinst").is_in([1, 2]))
1804
+ .then(cmp_rate_2yr_coarse_sample)
1805
+ .otherwise(None)
1806
+ )
1807
+ .otherwise(None)
1808
+ .alias("cmp_rate_coarse_sample"),
1809
+ # Coarsen only the 4yr cmp rates
1810
+ pl.when(pl.col("att_any_byY4") == 1)
1811
+ .then(
1812
+ pl.when(pl.col("college_years_firstinst") == 4)
1813
+ .then(cmp_rate_4yr_coarse)
1814
+ .when(pl.col("college_years_firstinst").is_in([1, 2]))
1815
+ .then(pl.col("completion_rate_150pct_ip"))
1816
+ .otherwise(None)
1817
+ )
1818
+ .otherwise(None)
1819
+ .alias("cmp_rate_coarsen_4yr"),
1820
+ # Coarsen only the 2yr cmp rates
1821
+ pl.when(pl.col("att_any_byY4") == 1)
1822
+ .then(
1823
+ pl.when(pl.col("college_years_firstinst") == 4)
1824
+ .then(pl.col("completion_rate_150pct_ip"))
1825
+ .when(pl.col("college_years_firstinst").is_in([1, 2]))
1826
+ .then(cmp_rate_2yr_coarse)
1827
+ .otherwise(None)
1828
+ )
1829
+ .otherwise(None)
1830
+ .alias("cmp_rate_coarsen_2yr"),
1751
1831
  )
1752
1832
 
1833
+ # Now set observed nonattenders to zero for each measure
1753
1834
  nsc_outcomes = nsc_outcomes.with_columns(
1754
- pl.when(pl.col("att_any_byY4").is_null())
1755
- .then(None)
1756
- .when(pl.col("att_any_byY4") == 0)
1835
+ # Detailed cmp rate
1836
+ pl.when(pl.col("att_any_byY4") == 0)
1757
1837
  .then(0.0)
1758
1838
  .otherwise(pl.col("cmp_rate"))
1759
1839
  .alias("adj_cmp_rate"),
1760
- pl.col("cmp_rate_coarse")
1761
- .alias("adj_cmp_rate_coarse"),
1762
- ).with_columns(
1763
- pl.when(pl.col("att_any_byY4").is_null())
1764
- .then(None)
1765
- .otherwise(pl.col("cmp_rate"))
1766
- .alias("cmp_rate"),
1767
- pl.when(pl.col("att_any_byY4").is_null())
1768
- .then(None)
1769
- .otherwise(pl.col("adj_cmp_rate"))
1770
- .alias("adj_cmp_rate"),
1771
- pl.when(pl.col("att_any_byY4").is_null())
1772
- .then(None)
1840
+ # Coarse cmp rate
1841
+ pl.when(pl.col("att_any_byY4") == 0)
1842
+ .then(0.0)
1773
1843
  .otherwise(pl.col("cmp_rate_coarse"))
1774
- .alias("cmp_rate_coarse"),
1775
- pl.when(pl.col("att_any_byY4").is_null())
1776
- .then(None)
1777
- .otherwise(pl.col("adj_cmp_rate_coarse"))
1778
1844
  .alias("adj_cmp_rate_coarse"),
1779
- pl.when(pl.col("att_any_byY4").is_null())
1780
- .then(None)
1845
+ # Coarse cmp rate, using in-sample avgs
1846
+ pl.when(pl.col("att_any_byY4") == 0)
1847
+ .then(0.0)
1848
+ .otherwise(pl.col("cmp_rate_coarse_sample"))
1849
+ .alias("adj_cmp_rate_coarse_sample"),
1850
+ # Coarsen only the 4yr cmp rates
1851
+ pl.when(pl.col("att_any_byY4") == 0)
1852
+ .then(0.0)
1853
+ .otherwise(pl.col("cmp_rate_coarsen_4yr"))
1854
+ .alias("adj_cmp_rate_coarsen_4yr"),
1855
+ # Coarsen only the 2yr cmp rates
1856
+ pl.when(pl.col("att_any_byY4") == 0)
1857
+ .then(0.0)
1858
+ .otherwise(pl.col("cmp_rate_coarsen_2yr"))
1859
+ .alias("adj_cmp_rate_coarsen_2yr"),
1860
+ )
1861
+
1862
+
1863
+ # Split the existing prediction by first institution, preserving missing rates.
1864
+ nsc_outcomes = nsc_outcomes.with_columns(
1865
+ pl.when(
1866
+ pl.col("adj_cmp_rate").is_not_null()
1867
+ & pl.col("college_years_firstinst").is_in([1, 2])
1868
+ )
1869
+ .then(0.0)
1870
+ .otherwise(pl.col("adj_cmp_rate"))
1871
+ .alias("adj_cmp_rate_4yr"),
1872
+ pl.when(
1873
+ pl.col("adj_cmp_rate").is_not_null()
1874
+ & (pl.col("college_years_firstinst") == 4)
1875
+ )
1876
+ .then(0.0)
1781
1877
  .otherwise(pl.col("adj_cmp_rate"))
1782
- .alias("predicted_completion"),
1878
+ .alias("adj_cmp_rate_2yr"),
1879
+ )
1880
+
1881
+ # Match the available 1098-T calendar years, including the missing 2015 year.
1882
+ tax_years = list(range(2011, 2015)) + list(range(2016, 2023))
1883
+ nsc_outcomes = nsc_outcomes.with_columns(
1884
+ [
1885
+ pl.when((pl.col("cohort_lottery") + age).is_in(tax_years))
1886
+ .then(pl.col(f"att_any_{age}"))
1887
+ .otherwise(None)
1888
+ .alias(f"att_any_1098_{age}")
1889
+ for age in AGE_ATTENDANCE_RANGE
1890
+ ]
1783
1891
  )
1784
1892
 
1785
1893
 
@@ -1799,6 +1907,7 @@ outcome_columns = [
1799
1907
  "att_elite_byY2", "att_elite_byY3", "att_elite_byY4", "att_elite_byY5", "att_elite_byY6",
1800
1908
 
1801
1909
  *[f"att_any_{age}" for age in AGE_ATTENDANCE_RANGE],
1910
+ *[f"att_any_1098_{age}" for age in AGE_ATTENDANCE_RANGE],
1802
1911
  *[f"att_4yr_{age}" for age in AGE_ATTENDANCE_RANGE],
1803
1912
  *[f"att_2yr_{age}" for age in AGE_ATTENDANCE_RANGE],
1804
1913
 
@@ -1855,9 +1964,12 @@ keep_columns = [
1855
1964
  "completion_rate_150pct_ip",
1856
1965
  "cmp_rate",
1857
1966
  "adj_cmp_rate",
1858
- "cmp_rate_coarse",
1967
+ "adj_cmp_rate_2yr",
1968
+ "adj_cmp_rate_4yr",
1859
1969
  "adj_cmp_rate_coarse",
1860
- "predicted_completion",
1970
+ "adj_cmp_rate_coarse_sample",
1971
+ "adj_cmp_rate_coarsen_4yr",
1972
+ "adj_cmp_rate_coarsen_2yr",
1861
1973
  "ID_FSC_firstinst",
1862
1974
  "college_name_firstinst",
1863
1975
  "tier_firstinst",
@@ -1902,6 +2014,13 @@ print(
1902
2014
  f"completion 2yr-or-less={cmp_rate_2yr_coarse:.4f}; "
1903
2015
  "observed by-Y4 nonattender completion=0"
1904
2016
  )
2017
+ print(
2018
+ "Sample-institution IPEDS-cohort-weighted coarse values: "
2019
+ f"completion 4yr={cmp_rate_4yr_coarse_sample:.4f} "
2020
+ f"({sample_4yr_institutions} institutions), "
2021
+ f"completion 2yr-or-less={cmp_rate_2yr_coarse_sample:.4f} "
2022
+ f"({sample_2yr_institutions} institutions)"
2023
+ )
1905
2024
 
1906
2025
  observable_y4 = nsc_outcomes.filter(pl.col("att_any_byY4").is_not_null())
1907
2026
  coarse_audit = observable_y4.select(
@@ -1914,6 +2033,15 @@ coarse_audit = observable_y4.select(
1914
2033
  .alias("adj_cmp_rate_coarse_missing"),
1915
2034
  pl.col("adj_cmp_rate_coarse").min().alias("adj_cmp_rate_coarse_min"),
1916
2035
  pl.col("adj_cmp_rate_coarse").max().alias("adj_cmp_rate_coarse_max"),
2036
+ pl.col("adj_cmp_rate_coarse_sample")
2037
+ .null_count()
2038
+ .alias("adj_cmp_rate_coarse_sample_missing"),
2039
+ pl.col("adj_cmp_rate_coarse_sample")
2040
+ .min()
2041
+ .alias("adj_cmp_rate_coarse_sample_min"),
2042
+ pl.col("adj_cmp_rate_coarse_sample")
2043
+ .max()
2044
+ .alias("adj_cmp_rate_coarse_sample_max"),
1917
2045
  )
1918
2046
 
1919
2047
  if coarse_audit.item(0, "k_mean_coarse_missing") > 0:
@@ -1922,6 +2050,10 @@ if coarse_audit.item(0, "adj_cmp_rate_coarse_missing") > 0:
1922
2050
  raise ValueError(
1923
2051
  "adj_cmp_rate_coarse is unexpectedly missing for observable students."
1924
2052
  )
2053
+ if coarse_audit.item(0, "adj_cmp_rate_coarse_sample_missing") > 0:
2054
+ raise ValueError(
2055
+ "adj_cmp_rate_coarse_sample is unexpectedly missing for observable students."
2056
+ )
1925
2057
 
1926
2058
  print("Coarse outcome audit:")
1927
2059
  print(coarse_audit)
@@ -1940,22 +2072,22 @@ attendee_completion_audit = (
1940
2072
  )
1941
2073
  .sum()
1942
2074
  .alias("filled_by_tier_median"),
1943
- pl.col("predicted_completion")
2075
+ pl.col("adj_cmp_rate")
1944
2076
  .is_null()
1945
2077
  .sum()
1946
2078
  .alias("missing_after_tier_median"),
1947
- pl.col("predicted_completion")
2079
+ pl.col("adj_cmp_rate")
1948
2080
  .is_null()
1949
2081
  .mean()
1950
2082
  .alias("missing_after_tier_median_share"),
1951
2083
  (
1952
- pl.col("predicted_completion").is_null()
2084
+ pl.col("adj_cmp_rate").is_null()
1953
2085
  & pl.col("tier_firstinst").is_null()
1954
2086
  )
1955
2087
  .sum()
1956
2088
  .alias("missing_after_tier_median_no_tier"),
1957
2089
  (
1958
- pl.col("predicted_completion").is_null()
2090
+ pl.col("adj_cmp_rate").is_null()
1959
2091
  & pl.col("tier_firstinst").is_not_null()
1960
2092
  )
1961
2093
  .sum()
@@ -1969,3 +2101,18 @@ print(
1969
2101
  "sid_cepr"
1970
2102
  )
1971
2103
  )
2104
+
2105
+ # Nonmissing year-eight completion identifies students with the full Y8 window.
2106
+ year8_attendance_audit = (
2107
+ nsc_outcomes.filter(pl.col("cmp_BA_byY8").is_not_null())
2108
+ .select(
2109
+ pl.len().alias("N_year8_sample"),
2110
+ (pl.col("att_any_byY4") == 0).sum().alias("N_no_attendance_byY4"),
2111
+ (
2112
+ (pl.col("att_any_byY4") == 0)
2113
+ & (pl.col("cmp_BA_byY8") == 1)
2114
+ ).sum().alias("N_no_attendance_byY4_BA_byY8"),
2115
+ )
2116
+ )
2117
+ print("Year-eight sample attendance and late BA completion check:")
2118
+ print(year8_attendance_audit)
File without changes