ltc-code 0.1.96__tar.gz → 0.1.97__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.97}/PKG-INFO +1 -1
  2. {ltc_code-0.1.96 → ltc_code-0.1.97}/pyproject.toml +1 -1
  3. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/june13.py +77 -41
  4. {ltc_code-0.1.96 → ltc_code-0.1.97}/README.md +0 -0
  5. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/__init__.py +0 -0
  6. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/aspire.py +0 -0
  7. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/check_cmo_apps.do +0 -0
  8. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/christel_house.py +0 -0
  9. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/green_dot.py +0 -0
  10. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/helpers.py +0 -0
  11. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/june2.py +0 -0
  12. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/june5.py +0 -0
  13. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/june7.py +0 -0
  14. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/kipp_nj.py +0 -0
  15. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/kipp_tx.py +0 -0
  16. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/main.py +0 -0
  17. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/mappings.py +0 -0
  18. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/may27.py +0 -0
  19. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/polars_dates.py +0 -0
  20. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/rocketship.py +0 -0
  21. {ltc_code-0.1.96 → ltc_code-0.1.97}/src/ltc_code/schema_mapping.py +0 -0
  22. {ltc_code-0.1.96 → ltc_code-0.1.97}/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.97
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.97"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -2,6 +2,13 @@ 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
+
5
12
 
6
13
  def impute_offers(
7
14
  frame: Union[pl.DataFrame, pl.LazyFrame],
@@ -13,6 +20,7 @@ def impute_offers(
13
20
  waitlist_imputation: Optional[Union[pl.Expr, Callable[[Frame], pl.Expr]]] = None,
14
21
  enrollment_imputation: Optional[Union[pl.Expr, Callable[[Frame], pl.Expr]]] = None,
15
22
  sibling_priority_cols: Optional[Sequence[str]] = None,
23
+ within_cols: Optional[Sequence[str]] = None,
16
24
  ) -> Union[pl.DataFrame, pl.LazyFrame]:
17
25
  """
18
26
  Impute offer indicators in a Polars DataFrame or LazyFrame.
@@ -28,48 +36,73 @@ def impute_offers(
28
36
  already had an observed offer are never flagged. Multiple flags may be 1
29
37
  for the same row if multiple rules independently imply an offer.
30
38
 
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.
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.
48
87
 
49
88
  Example
50
89
  -------
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(
90
+ >>> result = impute_offers(
60
91
  ... df,
61
92
  ... 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"],
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"],
66
98
  ... )
67
- shape: (4, 9)
68
- ...
69
99
  """
70
100
  if not isinstance(frame, (pl.DataFrame, pl.LazyFrame)):
71
101
  raise TypeError("frame must be a Polars DataFrame or LazyFrame.")
72
102
 
103
+ within_cols = list(within_cols or [])
104
+ sibling_priority_cols = list(sibling_priority_cols or [])
105
+
73
106
  original_offer = "__original_offer_for_imputation__"
74
107
 
75
108
  def as_rule_expr(rule: Union[pl.Expr, Callable[[Frame], pl.Expr]]) -> pl.Expr:
@@ -78,16 +111,21 @@ def impute_offers(
78
111
  raise TypeError("Imputation rules must be Polars expressions.")
79
112
  return expr.fill_null(False)
80
113
 
81
- def any_sibling_priority(cols: Optional[Sequence[str]]) -> pl.Expr:
82
- if not cols:
83
- return pl.lit(False)
84
-
114
+ def any_sibling_priority(cols: Sequence[str]) -> pl.Expr:
85
115
  priority_expr = pl.lit(False)
86
116
  for col in cols:
87
117
  priority_expr = priority_expr | (pl.col(col).fill_null(0) == 1)
88
118
  return priority_expr
89
119
 
90
- work = frame.with_columns(pl.col(offer_col).fill_null(0).alias(original_offer))
120
+ def maybe_over(expr: pl.Expr) -> pl.Expr:
121
+ if within_cols:
122
+ return expr.over(within_cols)
123
+ return expr
124
+
125
+ work = frame.with_columns(
126
+ pl.col(offer_col).fill_null(0).cast(pl.Int8, strict=False).alias(original_offer)
127
+ )
128
+
91
129
  originally_no_offer = pl.col(original_offer) == 0
92
130
 
93
131
  lottery_rule = None
@@ -99,7 +137,7 @@ def impute_offers(
99
137
  & ~any_sibling_priority(sibling_priority_cols)
100
138
  )
101
139
 
102
- lottery_cutoff = (
140
+ lottery_cutoff = maybe_over(
103
141
  pl.col(lottery_number_col)
104
142
  .filter(observed_regular_offer)
105
143
  .max()
@@ -125,7 +163,7 @@ def impute_offers(
125
163
  else:
126
164
  waitlist_cutoff_source = pl.col(original_offer) == 1
127
165
 
128
- waitlist_cutoff = (
166
+ waitlist_cutoff = maybe_over(
129
167
  pl.col(waitlist_number_col)
130
168
  .filter(waitlist_cutoff_source)
131
169
  .max()
@@ -176,8 +214,6 @@ def impute_offers(
176
214
  )
177
215
 
178
216
 
179
-
180
-
181
217
  # mappings.py
182
218
 
183
219
  import polars as pl
File without changes