ltc-code 0.2.1__tar.gz → 0.2.12__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.
- {ltc_code-0.2.1 → ltc_code-0.2.12}/PKG-INFO +1 -1
- {ltc_code-0.2.1 → ltc_code-0.2.12}/pyproject.toml +1 -1
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/.DS_Store +0 -0
- ltc_code-0.2.12/src/ltc_code/20260712_uncommon_scripts/main.py +638 -0
- ltc_code-0.2.12/src/ltc_code/20260712_uncommon_scripts/mappings.py +1400 -0
- ltc_code-0.2.12/src/ltc_code/20260712_uncommon_scripts/uncommon.py +2029 -0
- ltc_code-0.2.12/src/ltc_code/nsc/__init__.py +0 -0
- ltc_code-0.2.12/src/ltc_code/nsc/naics.csv +25 -0
- ltc_code-0.2.12/src/ltc_code/nsc/naics.py +126 -0
- ltc_code-0.2.12/src/ltc_code/nsc/naics_raw.xlsx +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/README.md +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/aspire.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/check_cmo_apps.do +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/christel_house.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/democracy_prep.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/green_dot.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/helpers.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/ilt.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/kipp_nj.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/main.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/mappings.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/rocketship.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260614_new_build_scripts_update/yes_prep.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/aspire.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/christel_house.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/democracy_prep.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/green_dot.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/ilt.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/kipp.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/kipp_nj.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/rocketship.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260630_census_disclosure/yes_prep.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/BALANCE.do +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/FS.do +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/ITT.do +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/TOT.do +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/apps_helpers.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/harmony.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/20260706_ceprscripts/main.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/__init__.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/aspire.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/check_cmo_apps.do +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/christel_house.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/green_dot.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/helpers.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/june13.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/june2.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/june30.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/june5.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/june7.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/kipp_nj.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/kipp_tx.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/main.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/mappings.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/may27.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/polars_dates.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/rocketship.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/schema_mapping.py +0 -0
- {ltc_code-0.2.1 → ltc_code-0.2.12}/src/ltc_code/yes_prep.py +0 -0
|
Binary file
|
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
# --- Import Necessary Libraries ---
|
|
2
|
+
import polars as pl
|
|
3
|
+
import sys
|
|
4
|
+
sys.path.append(r"P:\Long_Term_Charter\programs\new_build")
|
|
5
|
+
from paths import *
|
|
6
|
+
from helpers import *
|
|
7
|
+
from mappings import *
|
|
8
|
+
from polars_readstat import scan_readstat
|
|
9
|
+
import glob
|
|
10
|
+
import unicodedata
|
|
11
|
+
import re
|
|
12
|
+
from typing import Union, List, Dict, Optional
|
|
13
|
+
import time
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
# For parallel processing
|
|
18
|
+
import subprocess
|
|
19
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
20
|
+
|
|
21
|
+
# set row/col config
|
|
22
|
+
pl.Config.set_tbl_cols(13)
|
|
23
|
+
pl.Config.set_tbl_rows(15)
|
|
24
|
+
|
|
25
|
+
# --- Create log file and start timer ---
|
|
26
|
+
logging.basicConfig(
|
|
27
|
+
filename=LOGS / "main.log",
|
|
28
|
+
filemode="w", # "w"=overwrite existing log
|
|
29
|
+
level=logging.INFO,
|
|
30
|
+
format="%(asctime)s - %(message)s",
|
|
31
|
+
force=True, #reconfigure logging even if already set up
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
###########################################################################################################################################################################
|
|
37
|
+
# --- Define necessary paths and scripts ---
|
|
38
|
+
###########################################################################################################################################################################
|
|
39
|
+
|
|
40
|
+
BASE = Path("P:/Long_Term_Charter/programs/new_build")
|
|
41
|
+
|
|
42
|
+
# Paths to code files
|
|
43
|
+
CMO_ID_DIR = BASE / "cmo_ids"
|
|
44
|
+
CMO_APPS_DIR = BASE / "cmo_apps"
|
|
45
|
+
|
|
46
|
+
# Paths to input data
|
|
47
|
+
OLD_SAMPLE = OLD_BUILD / "old_cmo_apps.parquet"
|
|
48
|
+
KIPP_ENR = CMO_APPS / "kipp_enroll" / "kipp.dta"
|
|
49
|
+
|
|
50
|
+
# Paths to output data
|
|
51
|
+
OUTPUT_LONG_DTA = CMO_APPS / "stata" / "cmo_apps.dta"
|
|
52
|
+
OUTPUT_LONG_PARQUET = CMO_APPS / "stata" / "cmo_apps.parquet"
|
|
53
|
+
OUTPUT_WIDE_SID_DTA = CMO_APPS/ "stata_collapsed" / "cmo_apps_sid.dta"
|
|
54
|
+
OUTPUT_WIDE_SID_PARQUET = CMO_APPS / "stata_collapsed" / "cmo_apps_sid.parquet"
|
|
55
|
+
OUTPUT_NSC_DTA = CMO_ANALYSIS / "cmo_analysis.dta"
|
|
56
|
+
OUTPUT_NSC_PARQUET = CMO_ANALYSIS / "cmo_analysis.parquet"
|
|
57
|
+
OUTPUT_WIDE_NSC_DTA = CMO_ANALYSIS / "cmo_analysis_wide.dta"
|
|
58
|
+
OUTPUT_WIDE_NSC_PARQUET = CMO_ANALYSIS / "cmo_analysis_wide.parquet"
|
|
59
|
+
OUTPUT_SCHOOLS_CSV = CLEAN / "new_build" / "schools" / "all_schools.csv"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Define which apps scripts we want to run
|
|
64
|
+
apps_scripts = [
|
|
65
|
+
#CMO_APPS_DIR / "aspire.py",
|
|
66
|
+
#CMO_APPS_DIR / "christel_house.py",
|
|
67
|
+
#CMO_APPS_DIR / "democracy_prep.py",
|
|
68
|
+
#CMO_APPS_DIR / "green_dot.py",
|
|
69
|
+
#CMO_APPS_DIR / "harmony.py",
|
|
70
|
+
#CMO_APPS_DIR / "ilt.py",
|
|
71
|
+
#CMO_APPS_DIR / "kipp.py",
|
|
72
|
+
#CMO_APPS_DIR / "kipp_nj.py",
|
|
73
|
+
#CMO_APPS_DIR / "kipp_tx.py",
|
|
74
|
+
#CMO_APPS_DIR / "rocketship.py",
|
|
75
|
+
CMO_APPS_DIR / "uncommon.py",
|
|
76
|
+
#CMO_APPS_DIR / "yes_prep.py",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
###########################################################################################################################################################################
|
|
80
|
+
# --- Run scripts ---
|
|
81
|
+
###########################################################################################################################################################################
|
|
82
|
+
|
|
83
|
+
# Runs cmo scripts
|
|
84
|
+
def run_cmo(script):
|
|
85
|
+
# Start timer
|
|
86
|
+
start = time.perf_counter()
|
|
87
|
+
logging.info(f"Starting {script.name}")
|
|
88
|
+
subprocess.run(
|
|
89
|
+
[sys.executable, str(script.relative_to(BASE))],
|
|
90
|
+
cwd=BASE,
|
|
91
|
+
check=True,
|
|
92
|
+
)
|
|
93
|
+
# log elapsed time
|
|
94
|
+
elapsed_min = (time.perf_counter() - start) / 60
|
|
95
|
+
logging.info(f"Finished {script.name}: took {elapsed_min:.2f} minutes")
|
|
96
|
+
|
|
97
|
+
# Appends all the cmos together
|
|
98
|
+
def append_cmos(*cmos):
|
|
99
|
+
# Scan in old build data
|
|
100
|
+
old_data = pl.scan_parquet(OLD_SAMPLE)
|
|
101
|
+
# Read in KIPP enrollment data
|
|
102
|
+
enr = (
|
|
103
|
+
scan_readstat(KIPP_ENR)
|
|
104
|
+
.select("enrollment_years", "enroll",
|
|
105
|
+
"sid_cepr_kipp_nj", "sid_cepr_kipp_ma",
|
|
106
|
+
"sid_cepr_kipp_nc", "sid_cepr_kipp_tx", )
|
|
107
|
+
.rename({
|
|
108
|
+
"enroll": "enroll_kipp",
|
|
109
|
+
})
|
|
110
|
+
)
|
|
111
|
+
dfs = []
|
|
112
|
+
for cmo in cmos:
|
|
113
|
+
"""
|
|
114
|
+
if cmo == "harmony":
|
|
115
|
+
df = (
|
|
116
|
+
old_data
|
|
117
|
+
.filter(pl.col("cmo_name") == "HARMONY PUBLIC SCHOOLS")
|
|
118
|
+
.filter(pl.col("sid_cepr") != "")
|
|
119
|
+
.filter(pl.col("sample") == 1)
|
|
120
|
+
.filter(
|
|
121
|
+
(pl.col("application_date").is_not_null())
|
|
122
|
+
&
|
|
123
|
+
(pl.col("application_date") != "")
|
|
124
|
+
)
|
|
125
|
+
# Create better late applicant variable specifically for harmony
|
|
126
|
+
.with_columns(
|
|
127
|
+
pl.when(
|
|
128
|
+
(
|
|
129
|
+
pl.col("application_date")
|
|
130
|
+
.str.strptime(pl.Date, "%m/%d/%Y", strict=False)
|
|
131
|
+
.dt.year() == pl.col("school_year").cast(pl.Int64, strict=False)
|
|
132
|
+
)
|
|
133
|
+
&
|
|
134
|
+
(
|
|
135
|
+
(
|
|
136
|
+
pl.col("application_date")
|
|
137
|
+
.str.strptime(pl.Date, "%m/%d/%Y", strict=False)
|
|
138
|
+
.dt.month() > 2
|
|
139
|
+
)
|
|
140
|
+
|
|
|
141
|
+
(
|
|
142
|
+
(
|
|
143
|
+
pl.col("application_date")
|
|
144
|
+
.str.strptime(pl.Date, "%m/%d/%Y", strict=False)
|
|
145
|
+
.dt.month() == 2
|
|
146
|
+
)
|
|
147
|
+
&
|
|
148
|
+
(
|
|
149
|
+
pl.col("application_date")
|
|
150
|
+
.str.strptime(pl.Date, "%m/%d/%Y", strict=False)
|
|
151
|
+
.dt.day() >= 11
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
.then(1)
|
|
157
|
+
.otherwise(0)
|
|
158
|
+
.alias("late_applicant_clean")
|
|
159
|
+
)
|
|
160
|
+
.filter(pl.col("late_applicant_clean") == 0)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
elif cmo == "uncommon":
|
|
164
|
+
df = (
|
|
165
|
+
old_data
|
|
166
|
+
.filter(pl.col("cmo_name") == "UNCOMMON SCHOOLS")
|
|
167
|
+
.filter(pl.col("sid_cepr") != "")
|
|
168
|
+
.filter(pl.col("sample") == 1)
|
|
169
|
+
# Drop troublesome cohorts
|
|
170
|
+
.filter(
|
|
171
|
+
~(
|
|
172
|
+
(pl.col("cohort_lottery").is_not_null())
|
|
173
|
+
& (pl.col("cohort_lottery").is_in([1990, 1991]))
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
.filter(
|
|
177
|
+
(pl.col("late_applicant") == "0")
|
|
178
|
+
|
|
|
179
|
+
(pl.col("late_applicant") == "")
|
|
180
|
+
|
|
|
181
|
+
(pl.col("late_applicant").is_null())
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
"""
|
|
185
|
+
if cmo == "kipp_tx":
|
|
186
|
+
enr_filt = (
|
|
187
|
+
enr
|
|
188
|
+
.select("sid_cepr_kipp_tx", "enrollment_years", "enroll_kipp")
|
|
189
|
+
.unique("sid_cepr_kipp_tx")
|
|
190
|
+
.rename({
|
|
191
|
+
"sid_cepr_kipp_tx": "sid_cepr",
|
|
192
|
+
})
|
|
193
|
+
)
|
|
194
|
+
df = (
|
|
195
|
+
old_data
|
|
196
|
+
.filter(pl.col("cmo_name") == "KIPP TX")
|
|
197
|
+
.filter(pl.col("sid_cepr") != "")
|
|
198
|
+
.filter(pl.col("sample") == 1)
|
|
199
|
+
# Restrict to the randomized years
|
|
200
|
+
.filter(pl.col("school_year").cast(pl.Int32, strict=False).is_between(2012, 2016))
|
|
201
|
+
# Create better filter out late apps
|
|
202
|
+
.with_columns(
|
|
203
|
+
pl.when(
|
|
204
|
+
(
|
|
205
|
+
pl.col("application_date")
|
|
206
|
+
.str.strptime(pl.Date, "%m/%d/%Y", strict=False)
|
|
207
|
+
.dt.year() == pl.col("school_year").cast(pl.Int64, strict=False)
|
|
208
|
+
)
|
|
209
|
+
&
|
|
210
|
+
(
|
|
211
|
+
pl.col("application_date")
|
|
212
|
+
.str.strptime(pl.Date, "%m/%d/%Y", strict=False)
|
|
213
|
+
.dt.month() > 1
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
.then(1)
|
|
217
|
+
.otherwise(0)
|
|
218
|
+
.alias("late_applicant_clean")
|
|
219
|
+
)
|
|
220
|
+
.filter(pl.col("late_applicant_clean") == 0)
|
|
221
|
+
# Join on KIPP foundation enrollment
|
|
222
|
+
.join(enr_filt, how = "left", on = "sid_cepr", validate = "m:1")
|
|
223
|
+
.rename({
|
|
224
|
+
"enroll": "enroll_old"
|
|
225
|
+
})
|
|
226
|
+
.with_columns(
|
|
227
|
+
pl.when(pl.col("cohort_lottery").cast(pl.Int32) <= 2003)
|
|
228
|
+
.then(pl.col("enroll_kipp").fill_null(0))
|
|
229
|
+
.alias("enroll_kipp")
|
|
230
|
+
)
|
|
231
|
+
.with_columns(
|
|
232
|
+
pl.col("enrollment_years").fill_null(0).alias("enrollment_years"),
|
|
233
|
+
|
|
234
|
+
pl.when(pl.col("cohort_lottery").cast(pl.Int32) <= 2003)
|
|
235
|
+
.then(pl.col("enroll_kipp"))
|
|
236
|
+
.otherwise(pl.col("enroll_old"))
|
|
237
|
+
.alias("enroll")
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
elif cmo == "ascend":
|
|
241
|
+
df = (
|
|
242
|
+
old_data
|
|
243
|
+
.filter(pl.col("cmo_name") == "ASCEND PUBLIC CHARTER SCHOOLS")
|
|
244
|
+
.filter(pl.col("sid_cepr") != "")
|
|
245
|
+
.filter(pl.col("sample") == 1)
|
|
246
|
+
)
|
|
247
|
+
elif cmo == "kipp_nc":
|
|
248
|
+
enr_filt = (
|
|
249
|
+
enr
|
|
250
|
+
.select("sid_cepr_kipp_nc", "enrollment_years", "enroll_kipp")
|
|
251
|
+
.unique("sid_cepr_kipp_nc")
|
|
252
|
+
.rename({
|
|
253
|
+
"sid_cepr_kipp_nc": "sid_cepr",
|
|
254
|
+
})
|
|
255
|
+
)
|
|
256
|
+
df = (
|
|
257
|
+
old_data
|
|
258
|
+
.filter(pl.col("cmo_name") == "KIPP NC")
|
|
259
|
+
.filter(pl.col("sid_cepr") != "")
|
|
260
|
+
.filter(pl.col("sample") == 1)
|
|
261
|
+
# Join on KIPP foundation enrollment
|
|
262
|
+
.join(enr_filt, how = "left", on = "sid_cepr", validate = "m:1")
|
|
263
|
+
.rename({
|
|
264
|
+
"enroll": "enroll_old"
|
|
265
|
+
})
|
|
266
|
+
.with_columns(
|
|
267
|
+
pl.when(pl.col("cohort_lottery").cast(pl.Int32) <= 2003)
|
|
268
|
+
.then(pl.col("enroll_kipp").fill_null(0))
|
|
269
|
+
.alias("enroll_kipp")
|
|
270
|
+
)
|
|
271
|
+
.with_columns(
|
|
272
|
+
pl.col("enrollment_years").fill_null(0).alias("enrollment_years"),
|
|
273
|
+
|
|
274
|
+
pl.when(pl.col("cohort_lottery").cast(pl.Int32) <= 2003)
|
|
275
|
+
.then(pl.col("enroll_kipp"))
|
|
276
|
+
.otherwise(pl.col("enroll_old"))
|
|
277
|
+
.alias("enroll")
|
|
278
|
+
)
|
|
279
|
+
)
|
|
280
|
+
elif cmo == "kipp_ma":
|
|
281
|
+
enr_filt = (
|
|
282
|
+
enr
|
|
283
|
+
.select("sid_cepr_kipp_ma", "enrollment_years", "enroll_kipp")
|
|
284
|
+
.unique("sid_cepr_kipp_ma")
|
|
285
|
+
.rename({
|
|
286
|
+
"sid_cepr_kipp_ma": "sid_cepr",
|
|
287
|
+
})
|
|
288
|
+
)
|
|
289
|
+
df = (
|
|
290
|
+
old_data
|
|
291
|
+
.filter(pl.col("cmo_name") == "KIPP MA")
|
|
292
|
+
.filter(pl.col("sid_cepr") != "")
|
|
293
|
+
.filter(pl.col("sample") == 1)
|
|
294
|
+
.filter(
|
|
295
|
+
(pl.col("late_applicant") == "0")
|
|
296
|
+
|
|
|
297
|
+
(pl.col("late_applicant") == "")
|
|
298
|
+
|
|
|
299
|
+
(pl.col("late_applicant").is_null())
|
|
300
|
+
)
|
|
301
|
+
# Join on KIPP foundation enrollment
|
|
302
|
+
.join(enr_filt, how = "left", on = "sid_cepr", validate = "m:1")
|
|
303
|
+
.rename({
|
|
304
|
+
"enroll": "enroll_old"
|
|
305
|
+
})
|
|
306
|
+
.with_columns(
|
|
307
|
+
pl.when(pl.col("cohort_lottery").cast(pl.Int32) <= 2003)
|
|
308
|
+
.then(pl.col("enroll_kipp").fill_null(0))
|
|
309
|
+
.alias("enroll_kipp")
|
|
310
|
+
)
|
|
311
|
+
.with_columns(
|
|
312
|
+
pl.col("enrollment_years").fill_null(0).alias("enrollment_years"),
|
|
313
|
+
|
|
314
|
+
pl.when(pl.col("cohort_lottery").cast(pl.Int32) <= 2003)
|
|
315
|
+
.then(pl.col("enroll_kipp"))
|
|
316
|
+
.otherwise(pl.col("enroll_old"))
|
|
317
|
+
.alias("enroll")
|
|
318
|
+
)
|
|
319
|
+
)
|
|
320
|
+
elif cmo == "great_oaks":
|
|
321
|
+
df = (
|
|
322
|
+
old_data
|
|
323
|
+
.filter(pl.col("cmo_name") == "GREAT OAKS")
|
|
324
|
+
.filter(pl.col("sid_cepr") != "")
|
|
325
|
+
.filter(pl.col("sample") == 1)
|
|
326
|
+
)
|
|
327
|
+
elif cmo == "ilt":
|
|
328
|
+
df = (
|
|
329
|
+
old_data
|
|
330
|
+
.filter(pl.col("cmo_name") == "INTERNATIONAL LEADERSHIP OF TEXAS")
|
|
331
|
+
.filter(pl.col("sid_cepr") != "")
|
|
332
|
+
.filter(pl.col("sample") == 1)
|
|
333
|
+
.filter(
|
|
334
|
+
(pl.col("late_applicant") == "0")
|
|
335
|
+
|
|
|
336
|
+
(pl.col("late_applicant") == "")
|
|
337
|
+
|
|
|
338
|
+
(pl.col("late_applicant").is_null())
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
else:
|
|
342
|
+
df = (
|
|
343
|
+
scan_readstat(CMO_APPS / "stata" / "{0}.dta".format(cmo))
|
|
344
|
+
)
|
|
345
|
+
dfs.append(df)
|
|
346
|
+
# Create stata version
|
|
347
|
+
out_stata = (
|
|
348
|
+
pl.concat(dfs, how = "diagonal_relaxed")
|
|
349
|
+
# Do some cleaning
|
|
350
|
+
.with_columns(
|
|
351
|
+
pl.col("entry_grade_clean").replace(grade_map).alias("entry_grade_clean"),
|
|
352
|
+
pl.col("late_app").fill_null(0),
|
|
353
|
+
pl.col("application_cancel").fill_null(0),
|
|
354
|
+
# Replace the null risk id names with the risk ids for CMOs we didn't change
|
|
355
|
+
pl.coalesce(["risk_id_name", "risk_id"]).alias("risk_id_name"),
|
|
356
|
+
# Fix initial_offer and waitlist_offer
|
|
357
|
+
pl.coalesce(["initial_offer", "offer_initial"]).alias("initial_offer"),
|
|
358
|
+
pl.coalesce(["waitlist_offer", "offer_waitlist"]).alias("waitlist_offer"),
|
|
359
|
+
)
|
|
360
|
+
.drop("cohort_lottery")
|
|
361
|
+
.with_columns([
|
|
362
|
+
pl.col("dob_clean")
|
|
363
|
+
.str
|
|
364
|
+
.strip_chars()
|
|
365
|
+
.str
|
|
366
|
+
.slice(-4)
|
|
367
|
+
.cast(pl.String)
|
|
368
|
+
.alias("cohort_lottery")
|
|
369
|
+
])
|
|
370
|
+
.with_columns( # fix sid cepr (weirdly sometmes a cmo will have a decimanl in the string leading to errors later on) must convert 1010.0 -> 1010
|
|
371
|
+
pl.col("sid_cepr").cast(pl.Float64, strict=False).cast(pl.Int64).cast(pl.String).str.strip_chars().alias("sid_cepr"),
|
|
372
|
+
)
|
|
373
|
+
# Drop non randomized apps
|
|
374
|
+
.filter(
|
|
375
|
+
(pl.col("late_app") != 1)
|
|
376
|
+
&
|
|
377
|
+
(pl.col("application_cancel") != 1)
|
|
378
|
+
)
|
|
379
|
+
).collect()
|
|
380
|
+
# Create parquet version
|
|
381
|
+
out_parquet = (
|
|
382
|
+
out_stata
|
|
383
|
+
.filter(
|
|
384
|
+
(pl.col("late_app") != 1)
|
|
385
|
+
&
|
|
386
|
+
(pl.col("application_cancel") != 1)
|
|
387
|
+
)
|
|
388
|
+
)
|
|
389
|
+
# Write datasets
|
|
390
|
+
out_stata.to_pandas().to_stata(OUTPUT_LONG_DTA, write_index=False, version = 118)
|
|
391
|
+
out_parquet.write_parquet(OUTPUT_LONG_PARQUET)
|
|
392
|
+
return out_stata, out_parquet
|
|
393
|
+
|
|
394
|
+
# Collapse to wide
|
|
395
|
+
def collapse_to_wide(
|
|
396
|
+
input_parquet_path,
|
|
397
|
+
group_key,
|
|
398
|
+
output_parquet_path,
|
|
399
|
+
output_dta_path,
|
|
400
|
+
):
|
|
401
|
+
# Read in long parquet data (which already drops late apps)
|
|
402
|
+
long_input = pl.scan_parquet(input_parquet_path)
|
|
403
|
+
print(f"Starting N (Long): {long_input.select(pl.len()).collect().item()}")
|
|
404
|
+
# Define cols we need to take max of and the grouping key
|
|
405
|
+
max_cols = [
|
|
406
|
+
"enroll",
|
|
407
|
+
"enroll_kipp",
|
|
408
|
+
"enrollment_years",
|
|
409
|
+
"offer_accepted",
|
|
410
|
+
"offer",
|
|
411
|
+
"offer_imputed_enr",
|
|
412
|
+
"offer_imputed_ln",
|
|
413
|
+
"offer_imputed_wn",
|
|
414
|
+
"initial_offer",
|
|
415
|
+
"waitlist_offer",
|
|
416
|
+
"sibling",
|
|
417
|
+
"transfer",
|
|
418
|
+
"zoned",
|
|
419
|
+
"staff",
|
|
420
|
+
"sibling_concur",
|
|
421
|
+
]
|
|
422
|
+
# Within each CMO, group to unique group_key level
|
|
423
|
+
first_cols = [
|
|
424
|
+
c for c in long_input.collect_schema().names()
|
|
425
|
+
if c not in group_key + max_cols + ["risk_id_name"]
|
|
426
|
+
]
|
|
427
|
+
# Create collapsed dataset
|
|
428
|
+
collapsed = (
|
|
429
|
+
long_input
|
|
430
|
+
.group_by(group_key)
|
|
431
|
+
.agg(
|
|
432
|
+
# Max variables
|
|
433
|
+
[pl.max(c).alias(c) for c in max_cols]
|
|
434
|
+
+
|
|
435
|
+
# All risk ids concatenated together
|
|
436
|
+
[
|
|
437
|
+
pl.col("risk_id_name")
|
|
438
|
+
.unique()
|
|
439
|
+
.sort()
|
|
440
|
+
.str.join("_")
|
|
441
|
+
.alias("risk_set")
|
|
442
|
+
]
|
|
443
|
+
+
|
|
444
|
+
# Everything else from first row
|
|
445
|
+
[
|
|
446
|
+
pl.first(c).alias(c) for c in first_cols
|
|
447
|
+
]
|
|
448
|
+
)
|
|
449
|
+
)
|
|
450
|
+
collapsed_final = collapsed.collect()
|
|
451
|
+
print(f"Ending N (Wide): {len(collapsed_final)}")
|
|
452
|
+
# Write datasets
|
|
453
|
+
collapsed_final.to_pandas().to_stata(output_dta_path, write_index=False, version = 118)
|
|
454
|
+
collapsed_final.write_parquet(output_parquet_path)
|
|
455
|
+
return collapsed_final
|
|
456
|
+
|
|
457
|
+
# Merge on nsc
|
|
458
|
+
def merge_nsc(
|
|
459
|
+
input_parquet_path,
|
|
460
|
+
output_parquet_path,
|
|
461
|
+
output_dta_path,
|
|
462
|
+
):
|
|
463
|
+
# Read in parquet data (assuming it already drops late apps)
|
|
464
|
+
input = (
|
|
465
|
+
pl.scan_parquet(input_parquet_path)
|
|
466
|
+
)
|
|
467
|
+
# Read in NSC outcomes
|
|
468
|
+
nsc = scan_readstat(NSC).with_columns(pl.col("sid_cepr").cast(pl.String))
|
|
469
|
+
output = (
|
|
470
|
+
input
|
|
471
|
+
.with_columns(
|
|
472
|
+
pl.col("sid_cepr").cast(pl.Float64, strict=False).cast(pl.Int64).cast(pl.String).str.strip_chars().alias("sid_cepr"),
|
|
473
|
+
)
|
|
474
|
+
.join(nsc, on="sid_cepr", how="left", validate="m:1")
|
|
475
|
+
.collect()
|
|
476
|
+
)
|
|
477
|
+
# Write datasets
|
|
478
|
+
output.to_pandas().to_stata(output_dta_path, write_index=False, version = 118)
|
|
479
|
+
output.write_parquet(output_parquet_path)
|
|
480
|
+
return output
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
# Creates kiteworks folder corresponding to current date
|
|
485
|
+
def kiteworks():
|
|
486
|
+
today = datetime.today().strftime("%Y%m%d")
|
|
487
|
+
# kiteworks folders
|
|
488
|
+
KITEWORKS_DATE = KITEWORKS_FOLDER / today
|
|
489
|
+
KITEWORKS_DATE.mkdir(parents=True, exist_ok=True)
|
|
490
|
+
# define data and codebook output
|
|
491
|
+
DATA_OUTPUT = KITEWORKS_DATE / "combined_data.csv"
|
|
492
|
+
CODEBOOK_OUTPUT = KITEWORKS_DATE / "combined_codebook.csv"
|
|
493
|
+
# Create csv and codebook files set
|
|
494
|
+
# 7/13: subset
|
|
495
|
+
csv_files = [
|
|
496
|
+
CMO_ID_NO_PII / "uncommon.csv",
|
|
497
|
+
]
|
|
498
|
+
codebook_files = [
|
|
499
|
+
CMO_ID_NO_PII / "uncommon_codebook.csv",
|
|
500
|
+
]
|
|
501
|
+
# csv_files = [
|
|
502
|
+
# f
|
|
503
|
+
# for f in sorted(CMO_ID_NO_PII.glob("*.csv"))
|
|
504
|
+
# if "codebook" not in f.stem
|
|
505
|
+
# ]
|
|
506
|
+
# codebook_files = [
|
|
507
|
+
# f
|
|
508
|
+
# for f in sorted(CMO_ID_NO_PII.glob("*.csv"))
|
|
509
|
+
# if "codebook" in f.stem
|
|
510
|
+
# ]
|
|
511
|
+
# output combined data
|
|
512
|
+
if csv_files:
|
|
513
|
+
combined_lf = pl.concat(
|
|
514
|
+
[pl.scan_csv(f, infer_schema=False) for f in csv_files],
|
|
515
|
+
how = "diagonal",
|
|
516
|
+
)
|
|
517
|
+
combined_lf.collect().write_csv(DATA_OUTPUT)
|
|
518
|
+
# output combined codebook
|
|
519
|
+
if codebook_files:
|
|
520
|
+
combined_codebook = pl.concat(
|
|
521
|
+
[
|
|
522
|
+
pl.scan_csv(f, infer_schema=False)
|
|
523
|
+
for f in codebook_files
|
|
524
|
+
],
|
|
525
|
+
how = "diagonal",
|
|
526
|
+
)
|
|
527
|
+
# De-dupe common vars
|
|
528
|
+
final_codebook = (
|
|
529
|
+
combined_codebook
|
|
530
|
+
.group_by("Variable Name")
|
|
531
|
+
.agg(
|
|
532
|
+
pl.col("Type").first(),
|
|
533
|
+
pl.col("Count Non-Null").cast(pl.Int64).sum().alias("Count Non-Null"),
|
|
534
|
+
pl.col("Description").first(),
|
|
535
|
+
)
|
|
536
|
+
.sort("Count Non-Null", descending=True)
|
|
537
|
+
.drop("Count Non-Null")
|
|
538
|
+
)
|
|
539
|
+
# Save it
|
|
540
|
+
final_codebook.collect().write_csv(CODEBOOK_OUTPUT)
|
|
541
|
+
|
|
542
|
+
# Collapse to unique schools
|
|
543
|
+
def summarize_schools(
|
|
544
|
+
input_parquet_path,
|
|
545
|
+
output_csv_path
|
|
546
|
+
):
|
|
547
|
+
# Read in long parquet data (which already drops late apps)
|
|
548
|
+
long_input = pl.scan_parquet(input_parquet_path)
|
|
549
|
+
# Collapse to unique cmo, region, school_name combos
|
|
550
|
+
# NOTE: Old build CMOs may not have cmo_region and ren_num consistently filled
|
|
551
|
+
sch_ren_map = (
|
|
552
|
+
long_input
|
|
553
|
+
.filter(
|
|
554
|
+
pl.col("school_name").is_not_null()
|
|
555
|
+
& (pl.col("school_name") != "")
|
|
556
|
+
)
|
|
557
|
+
.select(["cmo_name", "cmo_region", "ren_num", "school_name"])
|
|
558
|
+
# unique row-school-ren_num combos
|
|
559
|
+
.group_by(["cmo_name", "cmo_region", "school_name"])
|
|
560
|
+
.agg(
|
|
561
|
+
pl.len().alias("n_obs"),
|
|
562
|
+
pl.col("ren_num")
|
|
563
|
+
.drop_nulls()
|
|
564
|
+
.unique()
|
|
565
|
+
.sort()
|
|
566
|
+
.cast(pl.String)
|
|
567
|
+
.str.join("+")
|
|
568
|
+
.alias("ren_nums")
|
|
569
|
+
)
|
|
570
|
+
.select(["cmo_name", "cmo_region", "school_name", "n_obs", "ren_nums"])
|
|
571
|
+
.sort(["cmo_name", "cmo_region", "school_name"])
|
|
572
|
+
)
|
|
573
|
+
# Save
|
|
574
|
+
sch_ren_map.collect().write_csv(output_csv_path)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
if __name__ == "__main__":
|
|
578
|
+
"""
|
|
579
|
+
|
|
580
|
+
# Create kiteworks output for cmo-id transfers
|
|
581
|
+
kiteworks()
|
|
582
|
+
|
|
583
|
+
# Run cmo apps scripts
|
|
584
|
+
batch_start = time.perf_counter()
|
|
585
|
+
logging.info("Starting applications build")
|
|
586
|
+
with ThreadPoolExecutor(max_workers=2) as pool:
|
|
587
|
+
futures = [pool.submit(run_cmo, s) for s in apps_scripts]
|
|
588
|
+
for fut in as_completed(futures):
|
|
589
|
+
fut.result()
|
|
590
|
+
batch_elapsed_min_apps = (time.perf_counter() - batch_start) / 60
|
|
591
|
+
logging.info(f"Finished applications build; total runtime was {batch_elapsed_min_apps: .2f} minutes")
|
|
592
|
+
"""
|
|
593
|
+
# Append the CMO apps builds together
|
|
594
|
+
batch_start = time.perf_counter()
|
|
595
|
+
logging.info("Starting CMO appending")
|
|
596
|
+
|
|
597
|
+
append_cmos(
|
|
598
|
+
"aspire",
|
|
599
|
+
"yes_prep",
|
|
600
|
+
"christel_house",
|
|
601
|
+
"green_dot",
|
|
602
|
+
"democracy_prep",
|
|
603
|
+
"ilt",
|
|
604
|
+
"rocketship",
|
|
605
|
+
"kipp_nj",
|
|
606
|
+
"kipp_tx",
|
|
607
|
+
"uncommon",
|
|
608
|
+
"kipp_ma",
|
|
609
|
+
"great_oaks",
|
|
610
|
+
"harmony",
|
|
611
|
+
"ascend",
|
|
612
|
+
"kipp_nc",
|
|
613
|
+
)
|
|
614
|
+
batch_elapsed_min_cmo = (time.perf_counter() - batch_start) / 60
|
|
615
|
+
logging.info(f"Finished CMO appending; total runtime was {batch_elapsed_min_cmo: .2f} minutes")
|
|
616
|
+
|
|
617
|
+
# Collapse to wide
|
|
618
|
+
batch_start = time.perf_counter()
|
|
619
|
+
logging.info("Starting wide collapse")
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
collapse_to_wide(OUTPUT_LONG_PARQUET, ["cmo_name", "sid_cepr"], OUTPUT_WIDE_SID_PARQUET, OUTPUT_WIDE_SID_DTA)
|
|
623
|
+
|
|
624
|
+
batch_elapsed_min_wide = (time.perf_counter() - batch_start) / 60
|
|
625
|
+
logging.info(f"Finished wide collapse; total runtime was {batch_elapsed_min_wide: .2f} minutes")
|
|
626
|
+
|
|
627
|
+
# Merge on nsc
|
|
628
|
+
batch_start = time.perf_counter()
|
|
629
|
+
logging.info("Starting NSC merge")
|
|
630
|
+
merge_nsc(OUTPUT_LONG_PARQUET, OUTPUT_NSC_PARQUET, OUTPUT_NSC_DTA)
|
|
631
|
+
merge_nsc(OUTPUT_WIDE_SID_PARQUET, OUTPUT_WIDE_NSC_PARQUET, OUTPUT_WIDE_NSC_DTA)
|
|
632
|
+
batch_elapsed_min_nsc = (time.perf_counter() - batch_start) / 60
|
|
633
|
+
logging.info(f"Finished NSC merge; total runtime was {batch_elapsed_min_nsc: .2f} minutes")
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
|