ltc-code 0.2.17__tar.gz → 0.2.18__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.17 → ltc_code-0.2.18}/PKG-INFO +1 -1
  2. {ltc_code-0.2.17 → ltc_code-0.2.18}/pyproject.toml +1 -1
  3. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/build_nsc_outcomes.py +95 -13
  4. ltc_code-0.2.18/src/ltc_code/nsc/raw/ipeds_data.dta +0 -0
  5. {ltc_code-0.2.17 → ltc_code-0.2.18}/README.md +0 -0
  6. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/aspire.py +0 -0
  7. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/check_cmo_apps.do +0 -0
  8. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/christel_house.py +0 -0
  9. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/democracy_prep.py +0 -0
  10. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/green_dot.py +0 -0
  11. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/helpers.py +0 -0
  12. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/ilt.py +0 -0
  13. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/kipp_nj.py +0 -0
  14. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/main.py +0 -0
  15. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/mappings.py +0 -0
  16. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/rocketship.py +0 -0
  17. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260614_new_build_scripts_update/yes_prep.py +0 -0
  18. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/aspire.py +0 -0
  19. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/christel_house.py +0 -0
  20. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/democracy_prep.py +0 -0
  21. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/green_dot.py +0 -0
  22. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/ilt.py +0 -0
  23. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/kipp.py +0 -0
  24. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/kipp_nj.py +0 -0
  25. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/rocketship.py +0 -0
  26. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260630_census_disclosure/yes_prep.py +0 -0
  27. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260706_ceprscripts/BALANCE.do +0 -0
  28. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260706_ceprscripts/FS.do +0 -0
  29. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260706_ceprscripts/ITT.do +0 -0
  30. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260706_ceprscripts/TOT.do +0 -0
  31. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260706_ceprscripts/apps_helpers.py +0 -0
  32. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260706_ceprscripts/harmony.py +0 -0
  33. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260706_ceprscripts/main.py +0 -0
  34. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260712_uncommon_scripts/main.py +0 -0
  35. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260712_uncommon_scripts/mappings.py +0 -0
  36. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/20260712_uncommon_scripts/uncommon.py +0 -0
  37. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/__init__.py +0 -0
  38. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/aspire.py +0 -0
  39. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/check_cmo_apps.do +0 -0
  40. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/christel_house.py +0 -0
  41. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/green_dot.py +0 -0
  42. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/helpers.py +0 -0
  43. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/june13.py +0 -0
  44. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/june2.py +0 -0
  45. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/june30.py +0 -0
  46. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/june5.py +0 -0
  47. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/june7.py +0 -0
  48. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/kipp_nj.py +0 -0
  49. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/kipp_tx.py +0 -0
  50. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/main.py +0 -0
  51. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/mappings.py +0 -0
  52. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/may27.py +0 -0
  53. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/__init__.py +0 -0
  54. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/input/.gitkeep +0 -0
  55. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/naics.csv +0 -0
  56. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/naics.py +0 -0
  57. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/naics_raw.xlsx +0 -0
  58. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/output/.gitkeep +0 -0
  59. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/raw/CREDENTIAL_LEVEL_LOOKUP_TABLE.xlsx +0 -0
  60. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/raw/IPEDS_IC_2013.csv +0 -0
  61. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/raw/IPEDS_IC_manual.xlsx +0 -0
  62. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/raw/chetty/mrc_table11.dta +0 -0
  63. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/raw/chetty/mrc_table2.dta +0 -0
  64. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/raw/college_crosswalk.xls +0 -0
  65. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/raw/directory.dta +0 -0
  66. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/nsc/run_nsc_outcomes.py +0 -0
  67. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/polars_dates.py +0 -0
  68. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/rocketship.py +0 -0
  69. {ltc_code-0.2.17 → ltc_code-0.2.18}/src/ltc_code/schema_mapping.py +0 -0
  70. {ltc_code-0.2.17 → ltc_code-0.2.18}/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.17
3
+ Version: 0.2.18
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.17"
3
+ version = "0.2.18"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -54,13 +54,17 @@ CHETTY_MRC_TABLE11 = next(
54
54
  )
55
55
  # Chetty OPE-to-super-OPE crosswalk used to attach tier to NSC schools.
56
56
 
57
-
58
- APPS = INPUT_NSC / "apps.csv"
59
- # Application/student records; source for unique sid_cepr/cohort_lottery.
57
+ IPEDS_DATA = RAW_NSC / "ipeds_data.dta"
58
+ # Institution-level IPEDS outcomes, including completion within 150% of normal time.
60
59
 
61
60
  INPUT_NSC = PACKAGE_NSC / "input"
62
61
  # Folder for secured Census inputs that are not packaged with the code.
63
62
 
63
+ OUTPUT_NSC = PACKAGE_NSC / "output"
64
+ # Folder for final student-level NSC outcomes.
65
+
66
+ APPS = INPUT_NSC / "apps.csv"
67
+ # Application/student records; source for unique sid_cepr/cohort_lottery.
64
68
 
65
69
  INPUT_NSC_OLD = INPUT_NSC / "nsc_records_old.dta"
66
70
  # Older NSC pull, kept so the build can append old and new NSC versions.
@@ -68,7 +72,7 @@ INPUT_NSC_OLD = INPUT_NSC / "nsc_records_old.dta"
68
72
  INPUT_NSC_NEW = INPUT_NSC / "nsc_records_new.csv"
69
73
  # Newer NSC pull with studyid-based rows.
70
74
 
71
- OUTCOMES_OUTPUT = OUTPUT_NSC / "nsc_outcomes.parquet"
75
+ OUTCOMES = OUTPUT_NSC / "nsc_outcomes.parquet"
72
76
  # Final student-level NSC outcomes file.
73
77
 
74
78
 
@@ -223,24 +227,32 @@ directory = (
223
227
  .select("unitid", "college_name_directory", "years_directory", "ownership_directory")
224
228
  )
225
229
 
226
- # Chetty/Barron tiers are keyed at super-OPE level. Table 11 maps school OPE IDs
227
- # to those super-OPE groups, which is the same route Sarah's Stata code uses.
228
- chetty_tiers = (
230
+ # Chetty college outcomes are keyed at super-OPE level. Table 11 maps school
231
+ # OPE IDs to those groups, which is the same route Sarah's Stata code uses.
232
+ chetty_college_outcomes = (
229
233
  scan_readstat(
230
234
  CHETTY_MRC_TABLE2,
231
235
  missing_string_as_null=True,
232
236
  value_labels_as_strings=False,
233
237
  )
234
- .select("super_opeid", "tier", "tier_name")
238
+ .select("super_opeid", "tier", "tier_name", "k_mean")
235
239
  .collect()
236
240
  .with_columns(
237
241
  pl.col("super_opeid").cast(pl.Int64, strict=False),
238
242
  pl.col("tier").cast(pl.Int8, strict=False),
243
+ pl.col("k_mean").cast(pl.Float64, strict=False),
239
244
  )
240
245
  .drop_nulls("super_opeid")
241
246
  .unique("super_opeid", keep="first")
242
247
  )
243
248
 
249
+ k_mean_neverattend = chetty_college_outcomes.filter(
250
+ pl.col("super_opeid") == -99
251
+ ).item(0, "k_mean")
252
+ k_mean_insuffdata = chetty_college_outcomes.filter(
253
+ pl.col("super_opeid") == -1
254
+ ).item(0, "k_mean")
255
+
244
256
  chetty_ope_crosswalk = (
245
257
  scan_readstat(
246
258
  CHETTY_MRC_TABLE11,
@@ -257,10 +269,30 @@ chetty_ope_crosswalk = (
257
269
  )
258
270
 
259
271
  chetty_by_opeid = (
260
- chetty_ope_crosswalk.join(chetty_tiers, on="super_opeid", how="left")
272
+ chetty_ope_crosswalk.join(
273
+ chetty_college_outcomes, on="super_opeid", how="left"
274
+ )
261
275
  .unique("opeid", keep="first")
262
276
  )
263
277
 
278
+ # IPEDS reports completion within 150% of normal time: six years at four-year
279
+ # colleges and three years at two-year colleges.
280
+ ipeds_completion = (
281
+ scan_readstat(
282
+ IPEDS_DATA,
283
+ missing_string_as_null=True,
284
+ value_labels_as_strings=False,
285
+ )
286
+ .select("unitid", "completion_rate_150pct_ip")
287
+ .collect()
288
+ .with_columns(
289
+ pl.col("unitid").cast(pl.Int64, strict=False),
290
+ pl.col("completion_rate_150pct_ip").cast(pl.Float64, strict=False),
291
+ )
292
+ .drop_nulls("unitid")
293
+ .unique("unitid", keep="first")
294
+ )
295
+
264
296
 
265
297
  ###########################################################
266
298
  # Build college reference data
@@ -272,6 +304,7 @@ college_ref = (
272
304
  .join(ipeds_manual, on="unitid", how="left")
273
305
  .join(directory, on="unitid", how="left")
274
306
  .join(chetty_by_opeid, on="opeid", how="left", validate="m:1")
307
+ .join(ipeds_completion, on="unitid", how="left", validate="m:1")
275
308
  .with_columns(
276
309
  pl.coalesce(
277
310
  [
@@ -318,6 +351,8 @@ college_ref = (
318
351
  "college_2yr",
319
352
  "tier",
320
353
  "college_elite",
354
+ "k_mean",
355
+ "completion_rate_150pct_ip",
321
356
  )
322
357
  )
323
358
 
@@ -731,6 +766,8 @@ enroll_weeks = (
731
766
  "college_2yr",
732
767
  "tier",
733
768
  "college_elite",
769
+ "k_mean",
770
+ "completion_rate_150pct_ip",
734
771
  "term_start_date",
735
772
  "term_end_date",
736
773
  "week_start",
@@ -788,6 +825,8 @@ enroll = (
788
825
  pl.col("college_2yr").max(),
789
826
  pl.col("tier").drop_nulls().min(),
790
827
  pl.col("college_elite").max(),
828
+ pl.col("k_mean").drop_nulls().first(),
829
+ pl.col("completion_rate_150pct_ip").drop_nulls().first(),
791
830
  pl.col("_term_att").max(),
792
831
  pl.col("week").min().alias("week_start"),
793
832
  pl.col("week").max().alias("week_end"),
@@ -892,9 +931,20 @@ first_institution = (
892
931
  .first()
893
932
  .alias("college_name_firstinst"),
894
933
  pl.col("college_years").first().alias("college_years_firstinst"),
934
+ pl.col("k_mean").first().alias("k_mean_firstinst"),
935
+ pl.col("completion_rate_150pct_ip")
936
+ .first()
937
+ .alias("completion_rate_150pct_firstinst"),
895
938
  )
896
939
  )
897
940
 
941
+ first_institution = first_institution.with_columns(
942
+ pl.col("k_mean_firstinst")
943
+ .fill_null(k_mean_insuffdata)
944
+ .alias("k_mean"),
945
+ pl.col("completion_rate_150pct_firstinst").alias("predicted_completion"),
946
+ )
947
+
898
948
  enrollment_outcomes = enrollment_outcomes.join(
899
949
  first_institution, on="sid_cepr", how="left"
900
950
  )
@@ -1291,6 +1341,16 @@ degree_outcomes = degree_outcomes.join(degree_details, on="sid_cepr", how="left"
1291
1341
  nsc_outcomes = (
1292
1342
  apps.join(enrollment_outcomes, on="sid_cepr", how="left", validate="m:1")
1293
1343
  .join(degree_outcomes, on="sid_cepr", how="left", validate="m:1")
1344
+ .with_columns(
1345
+ pl.when(pl.col("ID_FSC_firstinst").is_null())
1346
+ .then(k_mean_neverattend)
1347
+ .otherwise(pl.col("k_mean"))
1348
+ .alias("k_mean"),
1349
+ pl.when(pl.col("ID_FSC_firstinst").is_null())
1350
+ .then(0.0)
1351
+ .otherwise(pl.col("predicted_completion"))
1352
+ .alias("predicted_completion"),
1353
+ )
1294
1354
  )
1295
1355
 
1296
1356
  for year in range(1, N_YEARS_OUT + 1):
@@ -1365,15 +1425,13 @@ outcome_columns = [
1365
1425
  "att_any_byY2", "att_any_byY3", "att_any_byY4",
1366
1426
  "att_4yr_byY2", "att_4yr_byY3", "att_4yr_byY4",
1367
1427
  "att_2yr_byY2", "att_2yr_byY3", "att_2yr_byY4",
1368
- "att_elite_byY2", "att_elite_byY3", "att_elite_byY4", "att_elite_byY5", "att_elite_byY6"
1428
+ "att_elite_byY2", "att_elite_byY3", "att_elite_byY4", "att_elite_byY5", "att_elite_byY6",
1369
1429
 
1370
1430
  "cmp_any_byY4", "cmp_any_byY5", "cmp_any_byY6", "cmp_any_byY7", "cmp_any_byY8",
1371
1431
  "cmp_AA_byY4", "cmp_AA_byY5", "cmp_AA_byY6", "cmp_AA_byY7", "cmp_AA_byY8",
1372
1432
  "cmp_BA_byY4", "cmp_BA_byY5", "cmp_BA_byY6", "cmp_BA_byY7", "cmp_BA_byY8",
1373
1433
 
1374
- "cmp_elite_byY4", "cmp_elite_byY5", "cmp_elite_byY6", "cmp_elite_byY7", "cmp_elite_byY8"
1375
-
1376
-
1434
+ "cmp_elite_byY4", "cmp_elite_byY5", "cmp_elite_byY6", "cmp_elite_byY7", "cmp_elite_byY8",
1377
1435
  ]
1378
1436
 
1379
1437
  nsc_outcomes = nsc_outcomes.with_columns(
@@ -1384,6 +1442,11 @@ nsc_outcomes = nsc_outcomes.with_columns(
1384
1442
 
1385
1443
  keep_columns = [
1386
1444
  "sid_cepr",
1445
+ "k_mean",
1446
+ "predicted_completion",
1447
+ "ID_FSC_firstinst",
1448
+ "college_name_firstinst",
1449
+ "completion_rate_150pct_firstinst",
1387
1450
  ] + outcome_columns
1388
1451
 
1389
1452
  nsc_outcomes_final = nsc_outcomes.select(keep_columns)
@@ -1397,6 +1460,25 @@ nsc_outcomes.with_columns(
1397
1460
  print(f"Wrote {OUTCOMES}")
1398
1461
  print(f"Wrote {OUTCOMES.with_suffix('.csv')}")
1399
1462
  print(f"Rows: {nsc_outcomes.height}, columns: {len(nsc_outcomes.columns)}")
1463
+
1464
+ first_college_coverage = (
1465
+ nsc_outcomes.filter(pl.col("ID_FSC_firstinst").is_not_null())
1466
+ .group_by("ID_FSC_firstinst")
1467
+ .agg(
1468
+ pl.col("k_mean_firstinst").is_not_null().any().alias("has_k_mean"),
1469
+ pl.col("completion_rate_150pct_firstinst")
1470
+ .is_not_null()
1471
+ .any()
1472
+ .alias("has_completion_rate"),
1473
+ )
1474
+ )
1475
+ print(
1476
+ "First-college coverage: "
1477
+ f"{first_college_coverage['has_k_mean'].sum()}/"
1478
+ f"{first_college_coverage.height} with college-specific k_mean; "
1479
+ f"{first_college_coverage['has_completion_rate'].sum()}/"
1480
+ f"{first_college_coverage.height} with IPEDS completion rate"
1481
+ )
1400
1482
  print(
1401
1483
  nsc_outcomes.select("sid_cepr", "cohort_lottery", "recovered_nsc_outcome").sort(
1402
1484
  "sid_cepr"
File without changes