cleverly.datasets.make_sequential_offers#
- cleverly.datasets.make_sequential_offers(n: int, *, seed: int | Generator | None = None, backend: Literal['pandas', 'polars'] | str | None = None, policy: str | None = None) tuple[Any, dict[str, float]][source]#
Draw a weekly-offer campaign as a long panel, with the exact truth of its plans.
Customers receive an offer in each of three weeks:
"none","discount", or"shipping". The outcomefull_priceis a full-price order in week 4. The frame has one row per customer and week while the customer is observed.- Parameters:
- n
int Number of customers.
- seed
int,Generator,orNone Seed or NumPy random generator.
- backend{“pandas”, “polars”, “pyarrow”}
orNone, default=None Dataframe backend.
Noneuses pandas when installed, then the first available backend.- policy
strorNone, default=None Nonedraws the logged campaign. A key ofOFFER_PLANSdraws a synthetic deployment check: a fresh cohort in which the CRM sends that plan’s offers and every customer stays tracked.
- n
- Returns:
- panel
dataframe A long panel with columns
customer,week,tenure,engaged,offer,consent,p_none,p_discount,p_shipping, andfull_price.- truth
dictofstrtofloat The result of
offers_truth().
- panel
See also
cleverly.datasets.offers_truthThe exact truth this function returns.
cleverly.datasets.offers_protocolThe protocol of the question the panel answers.
cleverly.LongPanelThe design that reads this panel.
Notes
The law, for weeks \(t = 1, 2, 3\), with \(L_t\) the engagement of week \(t\) and \(A_t\) the offer:
variable
law
tenure\(N(0, 1)\)
engaged\(L_1\)Bernoulli, logit \(0.2 + 0.5\,\text{tenure}\)
offer\(A_t\)logged: engaged (none 0.40, discount 0.25, shipping 0.35); not engaged (none 0.30, discount 0.45, shipping 0.25)
engaged\(L_{t+1}\)Bernoulli, logit \(-0.4 + 1.0 L_t + 2.2\,[A_t = \text{discount}] + 0.5\,[A_t = \text{shipping}] + 0.3\,\text{tenure}\)
consentBernoulli, logit \(2.6 + 0.8 L_t + 0.3\,\text{tenure}\)
full_priceBernoulli, logit \(-0.9 + 2.0 L_4 + 0.4\,\text{tenure} - 0.5 D + 0.5 L_4\,\text{tenure}\)
\(D\) counts the discounts sent to engaged customers. A discount re-engages a customer who is not engaged, and it wears on a customer who already is. So the ordering of the plans is not monotone in the number of discounts. Each code expires at the end of its week, so no code is redeemable in week 4. Account closure is outside the law.
Engagement \(L_4\) is the week-4 engagement. It enters the outcome and no column.
The panel carries the logged probabilities in
p_none,p_discount, andp_shipping, so a design can declare them as the known offer mechanism. Apolicydraw sets the probability of the deployed offer to one.Examples
>>> from cleverly.datasets import make_sequential_offers >>> panel, truth = make_sequential_offers(200, seed=0) >>> list(panel.columns)[:5] ['customer', 'week', 'tenure', 'engaged', 'offer'] >>> int(panel["week"].max()) 3 >>> sorted(panel["offer"].unique()) ['discount', 'none', 'shipping'] >>> round(truth["ate_regimen[win_back vs none]"], 3) 0.072