ltc-code 0.1.96__tar.gz → 0.1.98__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 (22) hide show
  1. {ltc_code-0.1.96 → ltc_code-0.1.98}/PKG-INFO +1 -1
  2. {ltc_code-0.1.96 → ltc_code-0.1.98}/pyproject.toml +1 -1
  3. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/june13.py +58 -70
  4. {ltc_code-0.1.96 → ltc_code-0.1.98}/README.md +0 -0
  5. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/__init__.py +0 -0
  6. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/aspire.py +0 -0
  7. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/check_cmo_apps.do +0 -0
  8. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/christel_house.py +0 -0
  9. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/green_dot.py +0 -0
  10. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/helpers.py +0 -0
  11. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/june2.py +0 -0
  12. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/june5.py +0 -0
  13. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/june7.py +0 -0
  14. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/kipp_nj.py +0 -0
  15. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/kipp_tx.py +0 -0
  16. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/main.py +0 -0
  17. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/mappings.py +0 -0
  18. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/may27.py +0 -0
  19. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/polars_dates.py +0 -0
  20. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/rocketship.py +0 -0
  21. {ltc_code-0.1.96 → ltc_code-0.1.98}/src/ltc_code/schema_mapping.py +0 -0
  22. {ltc_code-0.1.96 → ltc_code-0.1.98}/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.1.96
3
+ Version: 0.1.98
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.96"
3
+ version = "0.1.98"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -2,6 +2,18 @@ from typing import Callable, Optional, Sequence, Union
2
2
 
3
3
  import polars as pl
4
4
 
5
+ from typing import Callable, Optional, Sequence, Union
6
+
7
+ import polars as pl
8
+
9
+ Frame =
10
+ Rule = Union[pl.Expr, Callable[[Frame], pl.Expr]]
11
+
12
+
13
+ from typing import Callable, Optional, Sequence, Union
14
+
15
+ import polars as pl
16
+
5
17
 
6
18
  def impute_offers(
7
19
  frame: Union[pl.DataFrame, pl.LazyFrame],
@@ -9,131 +21,110 @@ def impute_offers(
9
21
  lottery_number_col: Optional[str] = None,
10
22
  waitlist_number_col: Optional[str] = None,
11
23
  enrollment_col: Optional[str] = None,
12
- lottery_imputation: Optional[Union[pl.Expr, Callable[[Frame], pl.Expr]]] = None,
13
- waitlist_imputation: Optional[Union[pl.Expr, Callable[[Frame], pl.Expr]]] = None,
14
- enrollment_imputation: Optional[Union[pl.Expr, Callable[[Frame], pl.Expr]]] = None,
24
+ lottery_imputation: Optional[Union[pl.Expr, Callable[[Union[pl.DataFrame, pl.LazyFrame]], pl.Expr]]] = None,
25
+ waitlist_imputation: Optional[Union[pl.Expr, Callable[[Union[pl.DataFrame, pl.LazyFrame]], pl.Expr]]] = None,
26
+ enrollment_imputation: Optional[Union[pl.Expr, Callable[[Union[pl.DataFrame, pl.LazyFrame]], pl.Expr]]] = None,
15
27
  sibling_priority_cols: Optional[Sequence[str]] = None,
28
+ within_cols: Optional[Sequence[str]] = None,
16
29
  ) -> Union[pl.DataFrame, pl.LazyFrame]:
17
30
  """
18
31
  Impute offer indicators in a Polars DataFrame or LazyFrame.
19
32
 
20
- The function treats null offers as 0. It sets ``offer_col`` to 1 for rows
21
- that satisfy any supplied or default imputation rule, and creates:
22
-
23
- - ``offer_imputed_ln``: newly imputed from lottery number information
24
- - ``offer_imputed_wn``: newly imputed from waitlist number information
25
- - ``offer_imputed_enr``: newly imputed from enrollment information
26
-
27
- Flags are only set for rows whose original offer was 0 or null. Rows that
28
- already had an observed offer are never flagged. Multiple flags may be 1
29
- for the same row if multiple rules independently imply an offer.
30
-
31
- Default rules:
32
- - Lottery: find the highest lottery number among observed offers, excluding
33
- sibling-priority offers when ``sibling_priority_cols`` is supplied. Impute
34
- offers for all originally non-offered rows with lottery numbers at or
35
- below that cutoff.
36
- - Enrollment: impute offers for originally non-offered enrolled rows.
37
- - Waitlist:
38
- * If enrollment information is supplied, first infer offers from
39
- enrollment, then use the highest waitlist number among those
40
- enrollment-implied offers as the waitlist cutoff.
41
- * If enrollment information is not supplied, use the highest waitlist
42
- number among observed offers as the waitlist cutoff.
43
- Then impute offers for all originally non-offered rows with waitlist
44
- numbers at or below the cutoff.
45
-
46
- Custom rules may be supplied as Polars expressions or as callables that
47
- accept the frame and return a Polars expression.
48
-
49
- Example
50
- -------
51
- >>> df = pl.DataFrame({
52
- ... "offer": [1, 0, 0, None],
53
- ... "lottery_num": [10, 11, 12, 13],
54
- ... "waitlist_num": [None, 1, 2, 3],
55
- ... "enrolled": [0, 0, 1, 0],
56
- ... "sib_1": [0, 0, 0, 0],
57
- ... "sib_2": [0, 0, 0, 0],
58
- ... })
59
- >>> impute_offers(
60
- ... df,
61
- ... offer_col="offer",
62
- ... lottery_number_col="lottery_num",
63
- ... waitlist_number_col="waitlist_num",
64
- ... enrollment_col="enrolled",
65
- ... sibling_priority_cols=["sib_1", "sib_2"],
66
- ... )
67
- shape: (4, 9)
68
- ...
33
+ The function safely casts provided 0/1 and numeric order columns before
34
+ comparing them, so columns stored as strings like ``"0"``, ``"1"``, or
35
+ ``"12"`` can still be used.
36
+
37
+ Binary columns are interpreted as 1 only when they cast cleanly to numeric
38
+ value 1. Nulls and non-numeric strings are treated as 0.
39
+
40
+ Lottery and waitlist number columns are cast to Float64 for cutoff
41
+ comparisons. Nulls and non-numeric strings are ignored.
69
42
  """
70
43
  if not isinstance(frame, (pl.DataFrame, pl.LazyFrame)):
71
44
  raise TypeError("frame must be a Polars DataFrame or LazyFrame.")
72
45
 
46
+ within_cols = list(within_cols or [])
47
+ sibling_priority_cols = list(sibling_priority_cols or [])
48
+
73
49
  original_offer = "__original_offer_for_imputation__"
74
50
 
75
- def as_rule_expr(rule: Union[pl.Expr, Callable[[Frame], pl.Expr]]) -> pl.Expr:
51
+ def as_rule_expr(rule: Union[pl.Expr, Callable[[Union[pl.DataFrame, pl.LazyFrame]], pl.Expr]]) -> pl.Expr:
76
52
  expr = rule(frame) if callable(rule) else rule
77
53
  if not isinstance(expr, pl.Expr):
78
54
  raise TypeError("Imputation rules must be Polars expressions.")
79
55
  return expr.fill_null(False)
80
56
 
81
- def any_sibling_priority(cols: Optional[Sequence[str]]) -> pl.Expr:
82
- if not cols:
83
- return pl.lit(False)
57
+ def binary_col(col: str) -> pl.Expr:
58
+ return pl.col(col).cast(pl.Float64, strict=False).fill_null(0)
59
+
60
+ def number_col(col: str) -> pl.Expr:
61
+ return pl.col(col).cast(pl.Float64, strict=False)
84
62
 
63
+ def any_sibling_priority(cols: Sequence[str]) -> pl.Expr:
85
64
  priority_expr = pl.lit(False)
86
65
  for col in cols:
87
- priority_expr = priority_expr | (pl.col(col).fill_null(0) == 1)
66
+ priority_expr = priority_expr | (binary_col(col) == 1)
88
67
  return priority_expr
89
68
 
90
- work = frame.with_columns(pl.col(offer_col).fill_null(0).alias(original_offer))
69
+ def maybe_over(expr: pl.Expr) -> pl.Expr:
70
+ if within_cols:
71
+ return expr.over(within_cols)
72
+ return expr
73
+
74
+ work = frame.with_columns(
75
+ binary_col(offer_col).cast(pl.Int8, strict=False).alias(original_offer)
76
+ )
77
+
91
78
  originally_no_offer = pl.col(original_offer) == 0
92
79
 
93
80
  lottery_rule = None
94
81
  if lottery_imputation is not None:
95
82
  lottery_rule = as_rule_expr(lottery_imputation)
96
83
  elif lottery_number_col is not None:
84
+ lottery_number = number_col(lottery_number_col)
85
+
97
86
  observed_regular_offer = (
98
87
  (pl.col(original_offer) == 1)
99
88
  & ~any_sibling_priority(sibling_priority_cols)
100
89
  )
101
90
 
102
- lottery_cutoff = (
103
- pl.col(lottery_number_col)
91
+ lottery_cutoff = maybe_over(
92
+ lottery_number
104
93
  .filter(observed_regular_offer)
105
94
  .max()
106
95
  )
107
96
 
108
97
  lottery_rule = (
109
- pl.col(lottery_number_col).is_not_null()
110
- & (pl.col(lottery_number_col) <= lottery_cutoff)
98
+ lottery_number.is_not_null()
99
+ & (lottery_number <= lottery_cutoff)
111
100
  ).fill_null(False)
112
101
 
113
102
  enrollment_rule = None
114
103
  if enrollment_imputation is not None:
115
104
  enrollment_rule = as_rule_expr(enrollment_imputation)
116
105
  elif enrollment_col is not None:
117
- enrollment_rule = (pl.col(enrollment_col).fill_null(0) == 1).fill_null(False)
106
+ enrollment_rule = (binary_col(enrollment_col) == 1).fill_null(False)
118
107
 
119
108
  waitlist_rule = None
120
109
  if waitlist_imputation is not None:
121
110
  waitlist_rule = as_rule_expr(waitlist_imputation)
122
111
  elif waitlist_number_col is not None:
112
+ waitlist_number = number_col(waitlist_number_col)
113
+
123
114
  if enrollment_rule is not None:
124
115
  waitlist_cutoff_source = originally_no_offer & enrollment_rule
125
116
  else:
126
117
  waitlist_cutoff_source = pl.col(original_offer) == 1
127
118
 
128
- waitlist_cutoff = (
129
- pl.col(waitlist_number_col)
119
+ waitlist_cutoff = maybe_over(
120
+ waitlist_number
130
121
  .filter(waitlist_cutoff_source)
131
122
  .max()
132
123
  )
133
124
 
134
125
  waitlist_rule = (
135
- pl.col(waitlist_number_col).is_not_null()
136
- & (pl.col(waitlist_number_col) <= waitlist_cutoff)
126
+ waitlist_number.is_not_null()
127
+ & (waitlist_number <= waitlist_cutoff)
137
128
  ).fill_null(False)
138
129
 
139
130
  false_expr = pl.lit(False)
@@ -175,9 +166,6 @@ def impute_offers(
175
166
  .drop(original_offer)
176
167
  )
177
168
 
178
-
179
-
180
-
181
169
  # mappings.py
182
170
 
183
171
  import polars as pl
File without changes