Skip to content

Commit 2973ff8

Browse files
committed
fix(customer): accurate number_of_purchases type; empty-input percentile test
- find_purchase_percentile's number_of_purchases is now annotated `float` (ruff PYI041: float subsumes int) with a docstring noting fractional thresholds are valid and compared against the integer counts — matching what ensure_number actually accepts. Fractional thresholds like <= 2.5 are well-defined (equivalent to <= 2), so they are accepted, not rejected. - Add an empty-input test for DaysBetweenPurchases.purchases_percentile (returns NaN), for parity with the PurchasesPerCustomer case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G93Y8H2PH8oaNNfKssd99f
1 parent 3802851 commit 2973ff8

2 files changed

Lines changed: 15 additions & 2 deletions

File tree

openretailscience/analysis/customer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,15 @@ def purchases_percentile(self, percentile: float = 0.5) -> float:
153153

154154
def find_purchase_percentile(
155155
self,
156-
number_of_purchases: int,
156+
number_of_purchases: float,
157157
comparison: str = "less_than_equal_to",
158158
) -> float:
159159
"""Return the share of customers whose purchase count matches a comparison.
160160
161161
Args:
162-
number_of_purchases (int): Threshold to compare against.
162+
number_of_purchases (float): Threshold to compare against. Typically an integer count,
163+
but fractional values are compared directly against the integer purchase counts
164+
(e.g. ``<= 2.5`` is equivalent to ``<= 2``).
163165
comparison (str): One of ``less_than``, ``less_than_equal_to``,
164166
``equal_to``, ``not_equal_to``, ``greater_than``,
165167
``greater_than_equal_to``. Defaults to ``less_than_equal_to``.

tests/analysis/test_customer.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,17 @@ def test_purchases_percentile_uses_linear_interpolation(self, transactions_df, p
393393
dbp = DaysBetweenPurchases(transactions_df)
394394
assert dbp.purchases_percentile(percentile) == pytest.approx(expected)
395395

396+
def test_purchases_percentile_returns_nan_on_empty_input(self):
397+
"""purchases_percentile returns NaN on empty input (parity with PurchasesPerCustomer)."""
398+
empty = pd.DataFrame(
399+
{
400+
"customer_id": pd.Series([], dtype="int64"),
401+
"transaction_date": pd.Series([], dtype="datetime64[ns]"),
402+
},
403+
)
404+
dbp = DaysBetweenPurchases(empty)
405+
assert math.isnan(dbp.purchases_percentile(0.5))
406+
396407

397408
class TestTransactionChurn:
398409
"""Behavioral tests for TransactionChurn."""

0 commit comments

Comments
 (0)