ltc-code 0.1.84__tar.gz → 0.1.86__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ltc-code
3
- Version: 0.1.84
3
+ Version: 0.1.86
4
4
  Summary: Add your description here
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ltc-code"
3
- version = "0.1.84"
3
+ version = "0.1.86"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -0,0 +1,794 @@
1
+ def _parse_dob_expr(col: str) -> pl.Expr:
2
+ return pl.coalesce(
3
+ [
4
+ pl.col(col).cast(pl.String).str.strptime(pl.Date, "%m/%d/%Y", strict=False),
5
+ pl.col(col).cast(pl.String).str.strptime(pl.Date, "%m/%d/%y", strict=False),
6
+ pl.col(col).cast(pl.String).str.strptime(pl.Date, "%Y-%m-%d", strict=False),
7
+ ]
8
+ )
9
+
10
+
11
+ def _month_day_expr(col: str) -> pl.Expr:
12
+ return pl.col(col).dt.strftime("%m-%d")
13
+
14
+
15
+ def _first_word_expr(col: str) -> pl.Expr:
16
+ return pl.col(col).cast(pl.String).str.split(" ").list.first()
17
+
18
+
19
+ def _second_word_expr(col: str) -> pl.Expr:
20
+ return pl.col(col).cast(pl.String).str.split(" ").list.get(1, null_on_oob=True)
21
+
22
+
23
+ def _compact_expr(expr: pl.Expr) -> pl.Expr:
24
+ return (
25
+ expr.cast(pl.String)
26
+ .str.to_uppercase()
27
+ .str.replace_all(r"[^A-Z]", "")
28
+ )
29
+
30
+
31
+ def _build_lookup(
32
+ census: pl.DataFrame,
33
+ *,
34
+ fname_expr: pl.Expr,
35
+ lname_expr: pl.Expr,
36
+ dob_col: str,
37
+ label: str,
38
+ compact: bool = False,
39
+ ) -> pl.DataFrame:
40
+
41
+ if compact:
42
+ fname_expr = _compact_expr(fname_expr)
43
+ lname_expr = _compact_expr(lname_expr)
44
+
45
+ lookup = (
46
+ census
47
+ .select(
48
+ fname_expr.alias("_fname_key"),
49
+ lname_expr.alias("_lname_key"),
50
+ pl.col(dob_col).alias("_dob_key"),
51
+ pl.col("sid_cepr"),
52
+ )
53
+ .drop_nulls(["_fname_key", "_lname_key", "_dob_key", "sid_cepr"])
54
+ .filter(
55
+ pl.col("_fname_key").cast(pl.String).str.len_chars().gt(0)
56
+ & pl.col("_lname_key").cast(pl.String).str.len_chars().gt(0)
57
+ )
58
+ .group_by(["_fname_key", "_lname_key", "_dob_key"])
59
+ .agg(pl.col("sid_cepr").unique().alias("_sids"))
60
+ .select(
61
+ "_fname_key",
62
+ "_lname_key",
63
+ "_dob_key",
64
+ pl.col("_sids").list.sort().list.first().alias("sid_cepr"),
65
+ )
66
+ )
67
+
68
+ print(f"built lookup: {label} ({len(lookup):,} usable keys)")
69
+ return lookup
70
+
71
+
72
+ def _add_name_lookups(
73
+ lookups: dict[str, pl.DataFrame],
74
+ census: pl.DataFrame,
75
+ *,
76
+ prefix: str,
77
+ dob_col: str,
78
+ compact: bool = False,
79
+ ) -> None:
80
+
81
+ lookups[f"{prefix}exact"] = _build_lookup(
82
+ census,
83
+ fname_expr=pl.col("fname"),
84
+ lname_expr=pl.col("lname"),
85
+ dob_col=dob_col,
86
+ label=f"{prefix}exact",
87
+ compact=compact,
88
+ )
89
+
90
+ lookups[f"{prefix}mname"] = _build_lookup(
91
+ census,
92
+ fname_expr=pl.concat_str([pl.col("fname"), pl.col("mname")], separator=" "),
93
+ lname_expr=pl.col("lname"),
94
+ dob_col=dob_col,
95
+ label=f"{prefix}right fname + mname",
96
+ compact=compact,
97
+ )
98
+
99
+ lookups[f"{prefix}mname_lname"] = _build_lookup(
100
+ census,
101
+ fname_expr=pl.col("fname"),
102
+ lname_expr=pl.concat_str([pl.col("mname"), pl.col("lname")], separator=" "),
103
+ dob_col=dob_col,
104
+ label=f"{prefix}right mname + lname",
105
+ compact=compact,
106
+ )
107
+
108
+ lookups[f"{prefix}mname_nospace"] = _build_lookup(
109
+ census,
110
+ fname_expr=pl.concat_str([pl.col("fname"), pl.col("mname")], separator=""),
111
+ lname_expr=pl.col("lname"),
112
+ dob_col=dob_col,
113
+ label=f"{prefix}right fname + mname no space",
114
+ compact=compact,
115
+ )
116
+
117
+ lookups[f"{prefix}suffix"] = _build_lookup(
118
+ census,
119
+ fname_expr=pl.col("fname"),
120
+ lname_expr=pl.concat_str([pl.col("lname"), pl.col("suffix")], separator=" "),
121
+ dob_col=dob_col,
122
+ label=f"{prefix}right lname + suffix",
123
+ compact=compact,
124
+ )
125
+
126
+ lookups[f"{prefix}suffix_fname"] = _build_lookup(
127
+ census,
128
+ fname_expr=pl.concat_str([pl.col("fname"), pl.col("suffix")], separator=" "),
129
+ lname_expr=pl.col("lname"),
130
+ dob_col=dob_col,
131
+ label=f"{prefix}right fname + suffix",
132
+ compact=compact,
133
+ )
134
+
135
+ lookups[f"{prefix}suffix_fname_nospace"] = _build_lookup(
136
+ census,
137
+ fname_expr=pl.concat_str([pl.col("fname"), pl.col("suffix")], separator=""),
138
+ lname_expr=pl.col("lname"),
139
+ dob_col=dob_col,
140
+ label=f"{prefix}right fname + suffix no space",
141
+ compact=compact,
142
+ )
143
+
144
+ lookups[f"{prefix}suffix_lname_nospace"] = _build_lookup(
145
+ census,
146
+ fname_expr=pl.col("fname"),
147
+ lname_expr=pl.concat_str([pl.col("lname"), pl.col("suffix")], separator=""),
148
+ dob_col=dob_col,
149
+ label=f"{prefix}right lname + suffix no space",
150
+ compact=compact,
151
+ )
152
+
153
+ lookups[f"{prefix}fname_first_word"] = _build_lookup(
154
+ census,
155
+ fname_expr=_first_word_expr("fname"),
156
+ lname_expr=pl.col("lname"),
157
+ dob_col=dob_col,
158
+ label=f"{prefix}right fname first word",
159
+ compact=compact,
160
+ )
161
+
162
+ lookups[f"{prefix}lname_first_word"] = _build_lookup(
163
+ census,
164
+ fname_expr=pl.col("fname"),
165
+ lname_expr=_first_word_expr("lname"),
166
+ dob_col=dob_col,
167
+ label=f"{prefix}right lname first word",
168
+ compact=compact,
169
+ )
170
+
171
+ lookups[f"{prefix}lname_second_word"] = _build_lookup(
172
+ census,
173
+ fname_expr=pl.col("fname"),
174
+ lname_expr=_second_word_expr("lname"),
175
+ dob_col=dob_col,
176
+ label=f"{prefix}right lname second word",
177
+ compact=compact,
178
+ )
179
+
180
+
181
+ def build_census_lookups(*, cmo_name: str) -> dict[str, pl.DataFrame]:
182
+ annual_frames = []
183
+
184
+ for year in range(1994, 2023):
185
+ path = CENSUS_STUDENTS / f"census_student_{year}.csv"
186
+
187
+ annual = (
188
+ pl.scan_csv(
189
+ path,
190
+ infer_schema=False,
191
+ null_values=[],
192
+ try_parse_dates=False,
193
+ ignore_errors=False,
194
+ )
195
+ .select(
196
+ "cmo_code",
197
+ "sid_cepr",
198
+ "fname_clean",
199
+ "lname_clean",
200
+ "mname_clean",
201
+ "suff_clean",
202
+ "birthdate_clean",
203
+ "birthdate_imp",
204
+ )
205
+ .rename(
206
+ {
207
+ "fname_clean": "fname",
208
+ "lname_clean": "lname",
209
+ "mname_clean": "mname",
210
+ "suff_clean": "suffix",
211
+ "birthdate_clean": "dob",
212
+ "birthdate_imp": "dob_imp",
213
+ }
214
+ )
215
+ .with_columns(
216
+ pl.col("cmo_code").replace(cmo_map).alias("cmo_name")
217
+ )
218
+ .filter(pl.col("cmo_name") == cmo_name)
219
+ .with_columns(
220
+ *clean_name("fname"),
221
+ *clean_name("lname"),
222
+ *clean_other_name("mname"),
223
+ *clean_other_name("suffix"),
224
+ )
225
+ .with_columns(*clean_dob(col="dob"))
226
+ .with_columns(*clean_dob(col="dob_imp"))
227
+ .with_columns(
228
+ _parse_dob_expr("dob_clean").alias("dob"),
229
+ _parse_dob_expr("dob_imp_clean").alias("dob_imp"),
230
+ )
231
+ .select(
232
+ "sid_cepr",
233
+ pl.col("fname_clean").alias("fname"),
234
+ pl.col("lname_clean").alias("lname"),
235
+ pl.col("mname_clean").alias("mname"),
236
+ pl.col("suffix_clean").alias("suffix"),
237
+ "dob",
238
+ "dob_imp",
239
+ )
240
+ )
241
+
242
+ annual_frames.append(annual)
243
+
244
+ census = (
245
+ pl.concat(annual_frames, how="vertical_relaxed")
246
+ .collect()
247
+ .with_columns(
248
+ _month_day_expr("dob").alias("dob_md"),
249
+ _month_day_expr("dob_imp").alias("dob_imp_md"),
250
+ )
251
+ )
252
+
253
+ print(f"census rows after CMO filter: {len(census):,}")
254
+
255
+ lookups = {}
256
+
257
+ # Normal full-DOB lookups.
258
+ _add_name_lookups(lookups, census, prefix="", dob_col="dob", compact=False)
259
+
260
+ # Compact full-DOB lookups.
261
+ _add_name_lookups(lookups, census, prefix="compact_", dob_col="dob", compact=True)
262
+
263
+ # Normal month-day lookups using dob.
264
+ _add_name_lookups(lookups, census, prefix="md_", dob_col="dob_md", compact=False)
265
+
266
+ # Compact month-day lookups using dob.
267
+ _add_name_lookups(lookups, census, prefix="compact_md_", dob_col="dob_md", compact=True)
268
+
269
+ # Normal month-day lookups using dob_imp.
270
+ _add_name_lookups(lookups, census, prefix="mdi_", dob_col="dob_imp_md", compact=False)
271
+
272
+ # Compact month-day lookups using dob_imp.
273
+ _add_name_lookups(lookups, census, prefix="compact_mdi_", dob_col="dob_imp_md", compact=True)
274
+
275
+ lookups["dob_imp"] = _build_lookup(
276
+ census,
277
+ fname_expr=pl.col("fname"),
278
+ lname_expr=pl.col("lname"),
279
+ dob_col="dob_imp",
280
+ label="dob_imp",
281
+ )
282
+
283
+ lookups["compact_dob_imp"] = _build_lookup(
284
+ census,
285
+ fname_expr=pl.col("fname"),
286
+ lname_expr=pl.col("lname"),
287
+ dob_col="dob_imp",
288
+ label="compact dob_imp",
289
+ compact=True,
290
+ )
291
+
292
+ for offset, key in [
293
+ ("-1y", "dob_imp_minus_1"),
294
+ ("1y", "dob_imp_plus_1"),
295
+ ("-2y", "dob_imp_minus_2"),
296
+ ("2y", "dob_imp_plus_2"),
297
+ ]:
298
+ lookups[key] = _build_lookup(
299
+ census.with_columns(pl.col("dob_imp").dt.offset_by(offset).alias(key)),
300
+ fname_expr=pl.col("fname"),
301
+ lname_expr=pl.col("lname"),
302
+ dob_col=key,
303
+ label=key,
304
+ )
305
+
306
+ lookups[f"compact_{key}"] = _build_lookup(
307
+ census.with_columns(pl.col("dob_imp").dt.offset_by(offset).alias(key)),
308
+ fname_expr=pl.col("fname"),
309
+ lname_expr=pl.col("lname"),
310
+ dob_col=key,
311
+ label=f"compact {key}",
312
+ compact=True,
313
+ )
314
+
315
+ return lookups
316
+
317
+
318
+ def _run_match_stage(
319
+ unmatched: pl.DataFrame,
320
+ *,
321
+ lookup: pl.DataFrame,
322
+ fname_expr: pl.Expr,
323
+ lname_expr: pl.Expr,
324
+ dob_expr: pl.Expr,
325
+ label: str,
326
+ compact: bool = False,
327
+ ) -> tuple[pl.DataFrame, pl.DataFrame]:
328
+
329
+ before = len(unmatched)
330
+
331
+ if compact:
332
+ fname_expr = _compact_expr(fname_expr)
333
+ lname_expr = _compact_expr(lname_expr)
334
+
335
+ stage = (
336
+ unmatched
337
+ .with_columns(
338
+ fname_expr.alias("_fname_key"),
339
+ lname_expr.alias("_lname_key"),
340
+ dob_expr.alias("_dob_key"),
341
+ )
342
+ .join(
343
+ lookup,
344
+ on=["_fname_key", "_lname_key", "_dob_key"],
345
+ how="left",
346
+ validate="m:1",
347
+ )
348
+ .drop(["_fname_key", "_lname_key", "_dob_key"])
349
+ )
350
+
351
+ matched = stage.filter(pl.col("sid_cepr").is_not_null())
352
+ unmatched = stage.filter(pl.col("sid_cepr").is_null()).drop("sid_cepr")
353
+
354
+ print(f"{label}: matched {len(matched):,}/{before:,}")
355
+ return matched, unmatched
356
+
357
+
358
+ def _run_right_side_name_stages(
359
+ unmatched: pl.DataFrame,
360
+ matched_frames: list[pl.DataFrame],
361
+ *,
362
+ cols: Mapping[str, str],
363
+ lookups: dict[str, pl.DataFrame],
364
+ prefix: str,
365
+ dob_expr: pl.Expr,
366
+ label_prefix: str,
367
+ compact: bool = False,
368
+ ) -> pl.DataFrame:
369
+
370
+ for label, key in [
371
+ ("left exact -> right fname + mname", f"{prefix}mname"),
372
+ ("left exact -> right mname + lname", f"{prefix}mname_lname"),
373
+ ("left exact -> right fname + mname no space", f"{prefix}mname_nospace"),
374
+ ("left exact -> right lname + suffix", f"{prefix}suffix"),
375
+ ("left exact -> right fname + suffix", f"{prefix}suffix_fname"),
376
+ ("left exact -> right fname + suffix no space", f"{prefix}suffix_fname_nospace"),
377
+ ("left exact -> right lname + suffix no space", f"{prefix}suffix_lname_nospace"),
378
+ ("left exact -> right fname first word", f"{prefix}fname_first_word"),
379
+ ("left exact -> right lname first word", f"{prefix}lname_first_word"),
380
+ ("left exact -> right lname second word", f"{prefix}lname_second_word"),
381
+ ]:
382
+ matched, unmatched = _run_match_stage(
383
+ unmatched,
384
+ lookup=lookups[key],
385
+ fname_expr=pl.col(f"{cols['fname']}_clean"),
386
+ lname_expr=pl.col(f"{cols['lname']}_clean"),
387
+ dob_expr=dob_expr,
388
+ label=f"{label_prefix}{label}",
389
+ compact=compact,
390
+ )
391
+ matched_frames.append(matched)
392
+
393
+ return unmatched
394
+
395
+
396
+ def _run_left_side_name_stages(
397
+ unmatched: pl.DataFrame,
398
+ matched_frames: list[pl.DataFrame],
399
+ *,
400
+ cols: Mapping[str, str],
401
+ lookups: dict[str, pl.DataFrame],
402
+ exact_key: str,
403
+ dob_expr: pl.Expr,
404
+ label_prefix: str,
405
+ compact: bool = False,
406
+ ) -> pl.DataFrame:
407
+
408
+ for label, fname_expr, lname_expr in [
409
+ (
410
+ "left fname first word -> right exact",
411
+ _first_word_expr(f"{cols['fname']}_clean"),
412
+ pl.col(f"{cols['lname']}_clean"),
413
+ ),
414
+ (
415
+ "left lname first word -> right exact",
416
+ pl.col(f"{cols['fname']}_clean"),
417
+ _first_word_expr(f"{cols['lname']}_clean"),
418
+ ),
419
+ (
420
+ "left lname second word -> right exact",
421
+ pl.col(f"{cols['fname']}_clean"),
422
+ _second_word_expr(f"{cols['lname']}_clean"),
423
+ ),
424
+ ]:
425
+ matched, unmatched = _run_match_stage(
426
+ unmatched,
427
+ lookup=lookups[exact_key],
428
+ fname_expr=fname_expr,
429
+ lname_expr=lname_expr,
430
+ dob_expr=dob_expr,
431
+ label=f"{label_prefix}{label}",
432
+ compact=compact,
433
+ )
434
+ matched_frames.append(matched)
435
+
436
+ if "mname" in cols:
437
+ unmatched = unmatched.with_columns(*clean_other_name(cols["mname"]))
438
+
439
+ for label, fname_expr, lname_expr in [
440
+ (
441
+ "left fname + mname -> right exact",
442
+ pl.concat_str(
443
+ [pl.col(f"{cols['fname']}_clean"), pl.col(f"{cols['mname']}_clean")],
444
+ separator=" ",
445
+ ),
446
+ pl.col(f"{cols['lname']}_clean"),
447
+ ),
448
+ (
449
+ "left mname + lname -> right exact",
450
+ pl.col(f"{cols['fname']}_clean"),
451
+ pl.concat_str(
452
+ [pl.col(f"{cols['mname']}_clean"), pl.col(f"{cols['lname']}_clean")],
453
+ separator=" ",
454
+ ),
455
+ ),
456
+ (
457
+ "left fname + mname no space -> right exact",
458
+ pl.concat_str(
459
+ [pl.col(f"{cols['fname']}_clean"), pl.col(f"{cols['mname']}_clean")],
460
+ separator="",
461
+ ),
462
+ pl.col(f"{cols['lname']}_clean"),
463
+ ),
464
+ ]:
465
+ matched, unmatched = _run_match_stage(
466
+ unmatched,
467
+ lookup=lookups[exact_key],
468
+ fname_expr=fname_expr,
469
+ lname_expr=lname_expr,
470
+ dob_expr=dob_expr,
471
+ label=f"{label_prefix}{label}",
472
+ compact=compact,
473
+ )
474
+ matched_frames.append(matched)
475
+
476
+ if "suffix" in cols:
477
+ unmatched = unmatched.with_columns(*clean_other_name(cols["suffix"]))
478
+
479
+ for label, fname_expr, lname_expr in [
480
+ (
481
+ "left fname + suffix -> right exact",
482
+ pl.concat_str(
483
+ [pl.col(f"{cols['fname']}_clean"), pl.col(f"{cols['suffix']}_clean")],
484
+ separator=" ",
485
+ ),
486
+ pl.col(f"{cols['lname']}_clean"),
487
+ ),
488
+ (
489
+ "left fname + suffix no space -> right exact",
490
+ pl.concat_str(
491
+ [pl.col(f"{cols['fname']}_clean"), pl.col(f"{cols['suffix']}_clean")],
492
+ separator="",
493
+ ),
494
+ pl.col(f"{cols['lname']}_clean"),
495
+ ),
496
+ (
497
+ "left lname + suffix -> right exact",
498
+ pl.col(f"{cols['fname']}_clean"),
499
+ pl.concat_str(
500
+ [pl.col(f"{cols['lname']}_clean"), pl.col(f"{cols['suffix']}_clean")],
501
+ separator=" ",
502
+ ),
503
+ ),
504
+ (
505
+ "left lname + suffix no space -> right exact",
506
+ pl.col(f"{cols['fname']}_clean"),
507
+ pl.concat_str(
508
+ [pl.col(f"{cols['lname']}_clean"), pl.col(f"{cols['suffix']}_clean")],
509
+ separator="",
510
+ ),
511
+ ),
512
+ ]:
513
+ matched, unmatched = _run_match_stage(
514
+ unmatched,
515
+ lookup=lookups[exact_key],
516
+ fname_expr=fname_expr,
517
+ lname_expr=lname_expr,
518
+ dob_expr=dob_expr,
519
+ label=f"{label_prefix}{label}",
520
+ compact=compact,
521
+ )
522
+ matched_frames.append(matched)
523
+
524
+ return unmatched
525
+
526
+
527
+ def lookup_sid_cepr(
528
+ frame,
529
+ *,
530
+ cols: Mapping[str, str],
531
+ lookups: dict[str, pl.DataFrame],
532
+ ):
533
+ is_lazy = isinstance(frame, pl.LazyFrame)
534
+ current = frame.collect() if is_lazy else frame
535
+
536
+ input_columns = current.columns
537
+
538
+ current = current.with_row_index("_row_id")
539
+
540
+ current = current.with_columns(
541
+ *clean_name(cols["fname"]),
542
+ *clean_name(cols["lname"]),
543
+ )
544
+
545
+ current = (
546
+ current
547
+ .with_columns(*clean_dob(col=cols["dob"]))
548
+ .with_columns(
549
+ _parse_dob_expr(f"{cols['dob']}_clean").alias(f"{cols['dob']}_clean")
550
+ )
551
+ .with_columns(
552
+ _month_day_expr(f"{cols['dob']}_clean").alias("_lhs_dob_md")
553
+ )
554
+ )
555
+
556
+ matched_frames = []
557
+ unmatched = current
558
+
559
+ # Exact full DOB.
560
+ matched, unmatched = _run_match_stage(
561
+ unmatched,
562
+ lookup=lookups["exact"],
563
+ fname_expr=pl.col(f"{cols['fname']}_clean"),
564
+ lname_expr=pl.col(f"{cols['lname']}_clean"),
565
+ dob_expr=pl.col(f"{cols['dob']}_clean"),
566
+ label="exact",
567
+ )
568
+ matched_frames.append(matched)
569
+
570
+ # Compact exact full DOB. Handles SALDAN A vs SALDANA, apostrophes, hyphens, backticks, etc.
571
+ matched, unmatched = _run_match_stage(
572
+ unmatched,
573
+ lookup=lookups["compact_exact"],
574
+ fname_expr=pl.col(f"{cols['fname']}_clean"),
575
+ lname_expr=pl.col(f"{cols['lname']}_clean"),
576
+ dob_expr=pl.col(f"{cols['dob']}_clean"),
577
+ label="compact exact",
578
+ compact=True,
579
+ )
580
+ matched_frames.append(matched)
581
+
582
+ unmatched = _run_right_side_name_stages(
583
+ unmatched,
584
+ matched_frames,
585
+ cols=cols,
586
+ lookups=lookups,
587
+ prefix="",
588
+ dob_expr=pl.col(f"{cols['dob']}_clean"),
589
+ label_prefix="",
590
+ compact=False,
591
+ )
592
+
593
+ unmatched = _run_left_side_name_stages(
594
+ unmatched,
595
+ matched_frames,
596
+ cols=cols,
597
+ lookups=lookups,
598
+ exact_key="exact",
599
+ dob_expr=pl.col(f"{cols['dob']}_clean"),
600
+ label_prefix="",
601
+ compact=False,
602
+ )
603
+
604
+ unmatched = _run_right_side_name_stages(
605
+ unmatched,
606
+ matched_frames,
607
+ cols=cols,
608
+ lookups=lookups,
609
+ prefix="compact_",
610
+ dob_expr=pl.col(f"{cols['dob']}_clean"),
611
+ label_prefix="compact ",
612
+ compact=True,
613
+ )
614
+
615
+ unmatched = _run_left_side_name_stages(
616
+ unmatched,
617
+ matched_frames,
618
+ cols=cols,
619
+ lookups=lookups,
620
+ exact_key="compact_exact",
621
+ dob_expr=pl.col(f"{cols['dob']}_clean"),
622
+ label_prefix="compact ",
623
+ compact=True,
624
+ )
625
+
626
+ for key in [
627
+ "dob_imp",
628
+ "dob_imp_minus_1",
629
+ "dob_imp_plus_1",
630
+ "dob_imp_minus_2",
631
+ "dob_imp_plus_2",
632
+ "compact_dob_imp",
633
+ "compact_dob_imp_minus_1",
634
+ "compact_dob_imp_plus_1",
635
+ "compact_dob_imp_minus_2",
636
+ "compact_dob_imp_plus_2",
637
+ ]:
638
+ matched, unmatched = _run_match_stage(
639
+ unmatched,
640
+ lookup=lookups[key],
641
+ fname_expr=pl.col(f"{cols['fname']}_clean"),
642
+ lname_expr=pl.col(f"{cols['lname']}_clean"),
643
+ dob_expr=pl.col(f"{cols['dob']}_clean"),
644
+ label=key,
645
+ compact=key.startswith("compact_"),
646
+ )
647
+ matched_frames.append(matched)
648
+
649
+ # Month-day fallback on RHS dob.
650
+ matched, unmatched = _run_match_stage(
651
+ unmatched,
652
+ lookup=lookups["md_exact"],
653
+ fname_expr=pl.col(f"{cols['fname']}_clean"),
654
+ lname_expr=pl.col(f"{cols['lname']}_clean"),
655
+ dob_expr=pl.col("_lhs_dob_md"),
656
+ label="month-day exact dob",
657
+ )
658
+ matched_frames.append(matched)
659
+
660
+ matched, unmatched = _run_match_stage(
661
+ unmatched,
662
+ lookup=lookups["compact_md_exact"],
663
+ fname_expr=pl.col(f"{cols['fname']}_clean"),
664
+ lname_expr=pl.col(f"{cols['lname']}_clean"),
665
+ dob_expr=pl.col("_lhs_dob_md"),
666
+ label="compact month-day exact dob",
667
+ compact=True,
668
+ )
669
+ matched_frames.append(matched)
670
+
671
+ unmatched = _run_right_side_name_stages(
672
+ unmatched,
673
+ matched_frames,
674
+ cols=cols,
675
+ lookups=lookups,
676
+ prefix="md_",
677
+ dob_expr=pl.col("_lhs_dob_md"),
678
+ label_prefix="month-day dob: ",
679
+ compact=False,
680
+ )
681
+
682
+ unmatched = _run_left_side_name_stages(
683
+ unmatched,
684
+ matched_frames,
685
+ cols=cols,
686
+ lookups=lookups,
687
+ exact_key="md_exact",
688
+ dob_expr=pl.col("_lhs_dob_md"),
689
+ label_prefix="month-day dob: ",
690
+ compact=False,
691
+ )
692
+
693
+ unmatched = _run_right_side_name_stages(
694
+ unmatched,
695
+ matched_frames,
696
+ cols=cols,
697
+ lookups=lookups,
698
+ prefix="compact_md_",
699
+ dob_expr=pl.col("_lhs_dob_md"),
700
+ label_prefix="compact month-day dob: ",
701
+ compact=True,
702
+ )
703
+
704
+ unmatched = _run_left_side_name_stages(
705
+ unmatched,
706
+ matched_frames,
707
+ cols=cols,
708
+ lookups=lookups,
709
+ exact_key="compact_md_exact",
710
+ dob_expr=pl.col("_lhs_dob_md"),
711
+ label_prefix="compact month-day dob: ",
712
+ compact=True,
713
+ )
714
+
715
+ # Month-day fallback on RHS dob_imp.
716
+ matched, unmatched = _run_match_stage(
717
+ unmatched,
718
+ lookup=lookups["mdi_exact"],
719
+ fname_expr=pl.col(f"{cols['fname']}_clean"),
720
+ lname_expr=pl.col(f"{cols['lname']}_clean"),
721
+ dob_expr=pl.col("_lhs_dob_md"),
722
+ label="month-day dob_imp exact",
723
+ )
724
+ matched_frames.append(matched)
725
+
726
+ matched, unmatched = _run_match_stage(
727
+ unmatched,
728
+ lookup=lookups["compact_mdi_exact"],
729
+ fname_expr=pl.col(f"{cols['fname']}_clean"),
730
+ lname_expr=pl.col(f"{cols['lname']}_clean"),
731
+ dob_expr=pl.col("_lhs_dob_md"),
732
+ label="compact month-day dob_imp exact",
733
+ compact=True,
734
+ )
735
+ matched_frames.append(matched)
736
+
737
+ unmatched = _run_right_side_name_stages(
738
+ unmatched,
739
+ matched_frames,
740
+ cols=cols,
741
+ lookups=lookups,
742
+ prefix="mdi_",
743
+ dob_expr=pl.col("_lhs_dob_md"),
744
+ label_prefix="month-day dob_imp: ",
745
+ compact=False,
746
+ )
747
+
748
+ unmatched = _run_left_side_name_stages(
749
+ unmatched,
750
+ matched_frames,
751
+ cols=cols,
752
+ lookups=lookups,
753
+ exact_key="mdi_exact",
754
+ dob_expr=pl.col("_lhs_dob_md"),
755
+ label_prefix="month-day dob_imp: ",
756
+ compact=False,
757
+ )
758
+
759
+ unmatched = _run_right_side_name_stages(
760
+ unmatched,
761
+ matched_frames,
762
+ cols=cols,
763
+ lookups=lookups,
764
+ prefix="compact_mdi_",
765
+ dob_expr=pl.col("_lhs_dob_md"),
766
+ label_prefix="compact month-day dob_imp: ",
767
+ compact=True,
768
+ )
769
+
770
+ unmatched = _run_left_side_name_stages(
771
+ unmatched,
772
+ matched_frames,
773
+ cols=cols,
774
+ lookups=lookups,
775
+ exact_key="compact_mdi_exact",
776
+ dob_expr=pl.col("_lhs_dob_md"),
777
+ label_prefix="compact month-day dob_imp: ",
778
+ compact=True,
779
+ )
780
+
781
+ result = (
782
+ pl.concat(matched_frames + [unmatched], how="diagonal_relaxed")
783
+ .sort("_row_id")
784
+ .drop(["_row_id", "_lhs_dob_md"])
785
+ )
786
+
787
+ if "sid_cepr" not in input_columns:
788
+ input_columns = input_columns + ["sid_cepr"]
789
+
790
+ result = result.select(input_columns)
791
+
792
+ print(f"final matched: {result['sid_cepr'].is_not_null().sum():,}/{len(result):,}")
793
+
794
+ return result
File without changes