ltc-code 0.1.97__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.97 → ltc_code-0.1.98}/PKG-INFO +1 -1
  2. {ltc_code-0.1.97 → ltc_code-0.1.98}/pyproject.toml +1 -1
  3. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/june13.py +37 -85
  4. {ltc_code-0.1.97 → ltc_code-0.1.98}/README.md +0 -0
  5. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/__init__.py +0 -0
  6. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/aspire.py +0 -0
  7. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/check_cmo_apps.do +0 -0
  8. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/christel_house.py +0 -0
  9. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/green_dot.py +0 -0
  10. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/helpers.py +0 -0
  11. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/june2.py +0 -0
  12. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/june5.py +0 -0
  13. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/june7.py +0 -0
  14. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/kipp_nj.py +0 -0
  15. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/kipp_tx.py +0 -0
  16. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/main.py +0 -0
  17. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/mappings.py +0 -0
  18. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/may27.py +0 -0
  19. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/polars_dates.py +0 -0
  20. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/rocketship.py +0 -0
  21. {ltc_code-0.1.97 → ltc_code-0.1.98}/src/ltc_code/schema_mapping.py +0 -0
  22. {ltc_code-0.1.97 → 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.97
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.97"
3
+ version = "0.1.98"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -10,92 +10,35 @@ Frame =
10
10
  Rule = Union[pl.Expr, Callable[[Frame], pl.Expr]]
11
11
 
12
12
 
13
+ from typing import Callable, Optional, Sequence, Union
14
+
15
+ import polars as pl
16
+
17
+
13
18
  def impute_offers(
14
19
  frame: Union[pl.DataFrame, pl.LazyFrame],
15
20
  offer_col: str,
16
21
  lottery_number_col: Optional[str] = None,
17
22
  waitlist_number_col: Optional[str] = None,
18
23
  enrollment_col: Optional[str] = None,
19
- lottery_imputation: Optional[Union[pl.Expr, Callable[[Frame], pl.Expr]]] = None,
20
- waitlist_imputation: Optional[Union[pl.Expr, Callable[[Frame], pl.Expr]]] = None,
21
- 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,
22
27
  sibling_priority_cols: Optional[Sequence[str]] = None,
23
28
  within_cols: Optional[Sequence[str]] = None,
24
29
  ) -> Union[pl.DataFrame, pl.LazyFrame]:
25
30
  """
26
31
  Impute offer indicators in a Polars DataFrame or LazyFrame.
27
32
 
28
- The function treats null offers as 0. It sets ``offer_col`` to 1 for rows
29
- that satisfy any supplied or default imputation rule, and creates:
30
-
31
- - ``offer_imputed_ln``: newly imputed from lottery number information
32
- - ``offer_imputed_wn``: newly imputed from waitlist number information
33
- - ``offer_imputed_enr``: newly imputed from enrollment information
34
-
35
- Flags are only set for rows whose original offer was 0 or null. Rows that
36
- already had an observed offer are never flagged. Multiple flags may be 1
37
- for the same row if multiple rules independently imply an offer.
38
-
39
- Parameters
40
- ----------
41
- frame:
42
- Polars DataFrame or LazyFrame.
43
- offer_col:
44
- Name of the observed offer column. Null is treated as 0.
45
- lottery_number_col:
46
- Optional lottery number column.
47
- waitlist_number_col:
48
- Optional waitlist number column.
49
- enrollment_col:
50
- Optional enrollment indicator column.
51
- lottery_imputation, waitlist_imputation, enrollment_imputation:
52
- Optional custom imputation rules. Each may be a Polars expression or a
53
- callable that accepts ``frame`` and returns a Polars expression.
54
- sibling_priority_cols:
55
- Optional columns indicating sibling priority. If supplied, observed
56
- sibling-priority offers are excluded when computing the default lottery
57
- cutoff.
58
- within_cols:
59
- Optional columns defining the risk set within which lottery and waitlist
60
- cutoffs are computed. For example:
61
-
62
- ["school_name", "school_year", "entry_grade_clean", "priority_group"]
63
-
64
- If omitted, cutoffs are computed globally.
65
-
66
- Default Rules
67
- -------------
68
- Lottery:
69
- Within each ``within_cols`` group, find the highest lottery number among
70
- observed offers, excluding sibling-priority offers if
71
- ``sibling_priority_cols`` is supplied. Impute offers for originally
72
- non-offered rows with lottery numbers at or below that cutoff.
73
-
74
- Enrollment:
75
- Impute offers for originally non-offered enrolled rows.
76
-
77
- Waitlist:
78
- If enrollment information is supplied, first infer offers from
79
- enrollment, then within each group use the highest waitlist number among
80
- those enrollment-implied offers as the waitlist cutoff.
81
-
82
- If enrollment information is not supplied, within each group use the
83
- highest waitlist number among observed offers as the waitlist cutoff.
84
-
85
- Then impute offers for originally non-offered rows with waitlist numbers
86
- at or below that cutoff.
87
-
88
- Example
89
- -------
90
- >>> result = impute_offers(
91
- ... df,
92
- ... offer_col="offer",
93
- ... lottery_number_col="lottery_number",
94
- ... waitlist_number_col="waitlist_number",
95
- ... enrollment_col="enroll_school_year",
96
- ... sibling_priority_cols=["sibling", "sibling_concur"],
97
- ... within_cols=["school_name", "school_year", "entry_grade_clean", "priority_group"],
98
- ... )
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.
99
42
  """
100
43
  if not isinstance(frame, (pl.DataFrame, pl.LazyFrame)):
101
44
  raise TypeError("frame must be a Polars DataFrame or LazyFrame.")
@@ -105,16 +48,22 @@ def impute_offers(
105
48
 
106
49
  original_offer = "__original_offer_for_imputation__"
107
50
 
108
- 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:
109
52
  expr = rule(frame) if callable(rule) else rule
110
53
  if not isinstance(expr, pl.Expr):
111
54
  raise TypeError("Imputation rules must be Polars expressions.")
112
55
  return expr.fill_null(False)
113
56
 
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)
62
+
114
63
  def any_sibling_priority(cols: Sequence[str]) -> pl.Expr:
115
64
  priority_expr = pl.lit(False)
116
65
  for col in cols:
117
- priority_expr = priority_expr | (pl.col(col).fill_null(0) == 1)
66
+ priority_expr = priority_expr | (binary_col(col) == 1)
118
67
  return priority_expr
119
68
 
120
69
  def maybe_over(expr: pl.Expr) -> pl.Expr:
@@ -123,7 +72,7 @@ def impute_offers(
123
72
  return expr
124
73
 
125
74
  work = frame.with_columns(
126
- pl.col(offer_col).fill_null(0).cast(pl.Int8, strict=False).alias(original_offer)
75
+ binary_col(offer_col).cast(pl.Int8, strict=False).alias(original_offer)
127
76
  )
128
77
 
129
78
  originally_no_offer = pl.col(original_offer) == 0
@@ -132,46 +81,50 @@ def impute_offers(
132
81
  if lottery_imputation is not None:
133
82
  lottery_rule = as_rule_expr(lottery_imputation)
134
83
  elif lottery_number_col is not None:
84
+ lottery_number = number_col(lottery_number_col)
85
+
135
86
  observed_regular_offer = (
136
87
  (pl.col(original_offer) == 1)
137
88
  & ~any_sibling_priority(sibling_priority_cols)
138
89
  )
139
90
 
140
91
  lottery_cutoff = maybe_over(
141
- pl.col(lottery_number_col)
92
+ lottery_number
142
93
  .filter(observed_regular_offer)
143
94
  .max()
144
95
  )
145
96
 
146
97
  lottery_rule = (
147
- pl.col(lottery_number_col).is_not_null()
148
- & (pl.col(lottery_number_col) <= lottery_cutoff)
98
+ lottery_number.is_not_null()
99
+ & (lottery_number <= lottery_cutoff)
149
100
  ).fill_null(False)
150
101
 
151
102
  enrollment_rule = None
152
103
  if enrollment_imputation is not None:
153
104
  enrollment_rule = as_rule_expr(enrollment_imputation)
154
105
  elif enrollment_col is not None:
155
- enrollment_rule = (pl.col(enrollment_col).fill_null(0) == 1).fill_null(False)
106
+ enrollment_rule = (binary_col(enrollment_col) == 1).fill_null(False)
156
107
 
157
108
  waitlist_rule = None
158
109
  if waitlist_imputation is not None:
159
110
  waitlist_rule = as_rule_expr(waitlist_imputation)
160
111
  elif waitlist_number_col is not None:
112
+ waitlist_number = number_col(waitlist_number_col)
113
+
161
114
  if enrollment_rule is not None:
162
115
  waitlist_cutoff_source = originally_no_offer & enrollment_rule
163
116
  else:
164
117
  waitlist_cutoff_source = pl.col(original_offer) == 1
165
118
 
166
119
  waitlist_cutoff = maybe_over(
167
- pl.col(waitlist_number_col)
120
+ waitlist_number
168
121
  .filter(waitlist_cutoff_source)
169
122
  .max()
170
123
  )
171
124
 
172
125
  waitlist_rule = (
173
- pl.col(waitlist_number_col).is_not_null()
174
- & (pl.col(waitlist_number_col) <= waitlist_cutoff)
126
+ waitlist_number.is_not_null()
127
+ & (waitlist_number <= waitlist_cutoff)
175
128
  ).fill_null(False)
176
129
 
177
130
  false_expr = pl.lit(False)
@@ -213,7 +166,6 @@ def impute_offers(
213
166
  .drop(original_offer)
214
167
  )
215
168
 
216
-
217
169
  # mappings.py
218
170
 
219
171
  import polars as pl
File without changes