Red Salud — 30-day readmission
A hospital network wants to know which discharges are likely to come back within 30 days,
so the care-transitions team can call them. Score every encounter in data/holdout.csv.
Time: 90 minutes. Write everything in solution.py.
One row is one encounter, not one patient. Patients come back: a single patient can contribute five rows.
holdout.csvcontains no patient who appears inencounters.csv— the question is whether the model works on someone new.
Files
| File | What it is |
|---|---|
data/encounters.csv |
~12 000 encounters from ~5 000 patients. Has readmitted_30d. |
data/holdout.csv |
~8 400 encounters from ~3 400 different patients. No target. |
Columns
| Column | Notes |
|---|---|
encounter_id |
Unique per row. |
patient_id |
Repeats. Not available for a patient the model has never seen. |
age, severity, insurance, admission_source |
severity is mild < moderate < severe < critical. |
prior_encounters |
How many earlier encounters this patient had in the file. |
admitted_on, discharged_on |
ISO dates. See rule 1. |
diagnosis_codes |
A list in one cell, semicolon separated: D03;D07;D14. One to four codes. |
lab_hba1c |
Blank when the test was never ordered. See rule 2. |
readmitted_30d |
Target. encounters.csv only. About 19% are 1. |
Known defects, stated as rules
- Some encounters have
discharged_onbeforeadmitted_on— the two dates were transcribed the wrong way round. Their length of stay is unknown, not negative and not its absolute value. lab_hba1cis missing not at random. The test is ordered when someone suspects metabolic disease, so the blank itself carries information. Imputing the mean over it throws that away.
Tasks
T1 — length_of_stay(df) (15 pts)
Return the frame with a length_of_stay column in whole days, or a Series of the same
length. Rule 1's encounters must be missing, not negative.
T2 — missingness_report(df) -> dict (15 pts)
Exactly three keys, about lab_hba1c:
| Key | Meaning |
|---|---|
missing |
How many encounters have no HbA1c. |
rate_when_missing |
Readmission rate among those, rounded to 4 decimals. |
rate_when_present |
Readmission rate among the rest, rounded to 4 decimals. |
T3 — expand_diagnoses(df) -> pd.DataFrame (15 pts)
Return the frame with one 0/1 column per diagnosis code, named dx_D01, dx_D02, … Each
row flags exactly the codes it lists. No rows dropped.
T4 — patient_aware_split(df, valid_share) -> (train, valid) (15 pts)
Split into train and validation so that no patient_id appears in both halves, and
valid is roughly valid_share of the rows (within 4 percentage points — patients have
different numbers of encounters, so a quarter of patients is only about a quarter of rows).
T5 — fit_predict(train, holdout) -> np.ndarray (40 pts)
Both frames arrive raw. Return P(readmitted_30d = 1) for every holdout row, in file
order. The holdout's patients are strangers to the training file; anything keyed to a
patient identity will not survive.
Graded on ROC-AUC: >= 0.590 beats ranking by prior visits, >= 0.635 is competent,
>= 0.665 means the diagnosis list and the missing-test indicator are earning their place.
Allowed: pandas, numpy, scikit-learn.