← Back to the full chapter
Lesson

3. Build the data around decision opportunities

Choose offers by incremental value, feasible actions and fair tests.

3 min readApplied extensionUpdated
Read source ↗

Each training row is an eligible opportunity at a decision time, including those assigned no offer. A table of redeemed offers alone cannot estimate the comparison above. For each opportunity, join all relevant purchases, returns and costs over the fixed outcome window. Keep delivered, seen, clicked and redeemed as separate events.

Use at least these tables. The schema is my suggestion, not a library requirement.

TableRequired fieldsWhy it exists
customer_featurescustomer_id, feature_time, available_at, feature values, feature versionRecreate what the service could know at the decision
offer_catalogoffer_id, version, active interval, eligibility, channel, cost rule, margin rule, stock dependencyReproduce the feasible action set and accounting
decisionsdecision_id, customer/account ID, decision_at, eligible action IDs, selected action, policy/model version, experiment ID and arm, action probabilities, feature snapshot reference, budget reservation, fallback reasonAudit and evaluate the policy
delivery_eventsdecision_id, event type, event time, status, position if applicableDistinguish assignment from actual delivery and exposure
transactionscustomer/account ID, transaction time, recorded time, gross revenue, product cost, incentive cost, return/refund, channelCompute comparable outcomes for every arm
outcomesdecision_id, horizon, cutoff, label maturity, label availability, outcome coverage/status, net contribution, contact cost, conversion, guardrailsStore a reproducible training label

For a feature window ending at decision_at, use only events whose event time and availability time are both at or before the decision. A purchase dated Monday but first loaded on Wednesday was unavailable to Tuesday's service. Joins to today's customer profile, today's stock or a backfilled lifetime-spend field can all leak future information. Fit encoders, imputers and other learned transforms on training data only.

Useful inputs include purchase recency and frequency, past contribution, category affinity, tenure, earlier eligible contacts, time since the last offer, pre-decision complaints, channel preference, offer/product attributes, calendar context and available stock. Add missingness and history length so thin histories are visible. Leave out post-assignment opens, coupon redemption and future spend.

Do not invent labels for unchosen actions. When a customer gets free shipping, you see their outcome under that assignment only, not under a coupon or no offer. Products never shown are unobserved, not confirmed dislikes. Negative sampling can keep retrieval training manageable, but it changes the training distribution. It does not turn missing interactions into causal negatives.

Make overlapping decisions explicit. One purchase cannot quietly become an independent positive label for five competing offers. Start with one decision per account per non-overlapping measurement window, or use a documented design that models sequences and concurrent campaigns.

Mature does not mean observed. A closed window with a reconciled transaction and return ledger can give a zero-purchase outcome. A closed window with lost identifiers, an incomplete channel feed or an unresolved refund cannot. Store observation status and label availability separately, and report missing outcomes by assigned arm and pre-decision cohort. Keep every assigned account in the experiment accounting. Dropping missing outcomes can select customers after assignment, and neither randomisation nor correct propensities repair that. Reconcile coverage, and for any remaining exclusions state the assumptions and sensitivity analysis they need, as the experiment inclusion guidance shows.

Something unclear or out of date? Suggest a correction ↗

Find your next idea

Loading local search…

Search is built from this edition. No account needed.