Predict Conditional Median Paid Price in the Next 30 Days
Task type: RegressionTask
This regression objective predicts the median paid price in the next 30 days for each customer with at least four historical purchases. It evaluates only customers who later buy in that window. That is a retrospective conditional population: the model predicts purchase price among later buyers, not whether a customer will buy.
Target contract
The example treats one event row as one purchase. It uses calendar days 1 through 30 after the cutoff, so it starts 86,400 seconds after SPLIT_TIMESTAMP and requires data through the exclusive end of day 30. Replace transactions and price only when your configured event source uses different names.
from datetime import timedelta
from typing import Dict
import numpy as np
from monad.batch import SPLIT_TIMESTAMP
from monad.targets import Attributes, Events
from monad.ui.target_function import has_incomplete_training_window
TRANSACTIONS = "transactions"
PAID_PRICE = "price"
MIN_HISTORY_PURCHASES = 4
TARGET_WINDOW_DAYS = 30
SECONDS_PER_DAY = 86_400
def conditional_median_paid_price_target_fn(
history: Events,
future: Events,
attributes: Attributes,
ctx: Dict,
) -> np.ndarray | None:
if history[TRANSACTIONS].count() < MIN_HISTORY_PURCHASES:
return None
complete_horizon = timedelta(days=TARGET_WINDOW_DAYS + 1)
if has_incomplete_training_window(ctx, complete_horizon):
return None
target_events = future.interval_from(
ctx[SPLIT_TIMESTAMP] + SECONDS_PER_DAY,
timedelta(days=TARGET_WINDOW_DAYS),
)
prices = target_events[TRANSACTIONS][PAID_PRICE].events
if len(prices) == 0:
return None
median_price = float(np.nanmedian(prices))
if not np.isfinite(median_price):
return None
return np.array([median_price], dtype=np.float32)
The historical-count guard is known at the cutoff. The no-future-purchase guard implements the stated conditional evaluation cohort. If the business question must score every eligible customer, return a defined no-purchase outcome instead and evaluate that different population.
Configure and verify
Use RegressionTask(num_targets=1, max_value=...). Select max_value from training-period labels only, before reading fixed-test errors. Then run verify_target() and confirm:
- accepted labels have shape
(n, 1)and dtypefloat32; - the accepted count is large enough for both training and validation loaders;
- the
Nonerate matches the declared history and future-purchase conditions; - the evaluation joins model and baseline predictions to the same accepted entities.
For price-band diagnostics, pre-register fixed thresholds or a quantile rule with tie handling. Apply the resulting bands to the same held-out rows for the model and baseline, report each band's count and MAE, and name the band with the largest model MAE. Do not change the rule after inspecting test errors.