Other domains and outcomes

Apply the same history-and-outcome pattern to retail retention, reviewer engagement, study reporting, and price forecasting.

The payment-history workflow combines account context, repeated transactions, and later outcomes. The account model adds several levels of context and targets. Once that pattern is familiar, apply it to a retailer’s purchase histories and customer retention decisions.

The review, study-reporting, and market examples later on this page are optional applications to other event domains. Each keeps attributes at the root, related records in a branch, and a target describing a later outcome.

Retail: who will make no purchase next week?

A retail team can rank recently active customers by their estimated chance of making no purchase next week, then choose whom to contact. Whether that contact changes purchasing requires a separate intervention evaluation.

Define the population as customers who bought something in the preceding seven days. For each customer, predict inactive_next_7_days: true when there is no purchase after the decision time through seven days later. This gives the team a specific measure of short-term inactivity.

Customer attributes describe the person. Their purchase history describes interactions: which articles they bought, through which channels, and when they were booked. Your application joins those records and keeps only information available at the decision time before supplying the observation to RelFlow.

One illustrative observation

At the May 28, 2026 decision, a training observation contains customer attributes and purchases booked by that date. The root Boolean records customer inactivity during the seven days after that common snapshot cutoff and is attached once that window has completed:

customer_age: 34
club_member_status: ACTIVE
recent_purchases:
  - booked_at: "2026-05-22T10:00:00"
    article_id: article-17
    amount: 32.00
    sales_channel: online
  - booked_at: "2026-05-26T17:30:00"
    article_id: article-03
    amount: 56.20
    sales_channel: store
inactive_next_7_days: true

Describe the customer and their purchases

import relflow as rf

model = rf.Model.sm(
    customer_age=rf.Number,
    club_member_status=rf.Category,
    recent_purchases=rf.Branch(
        length=32,
        overflow="tail",
        booked_at=rf.DateParts(
            dateparts=["day_of_month", "day_of_week", "hour_of_day"],
        ),
        article_id=rf.Category,
        amount=rf.Number,
        sales_channel=rf.Category,
    ),
    inactive_next_7_days=rf.Boolean(mask=True),
)

A customer root contains customer_age Number, club_member_status Category, a repeated recent_purchases branch with booked_at DateParts, article_id Category, amount Number, and sales_channel Category, and a hidden inactive_next_7_days Boolean target at the root.

A customer root contains customer_age Number, club_member_status Category, a repeated recent_purchases branch with booked_at DateParts, article_id Category, amount Number, and sales_channel Category, and a hidden inactive_next_7_days Boolean target at the root.

Figure 1: The customer is the subject: attributes at the root, purchases in a branch, and one hidden outcome for the customer.

The customer is the subject of the prediction; each purchase keeps its own article, amount, channel, and booking date together. Supply purchases oldest to newest so the branch retains the most recent 32. Choose that capacity for the history your application needs. mask=True keeps the later outcome out of the inputs while using it as supervision. This customer target is always masked, so omit it when requesting a new prediction. Retain customer identifiers and decision times in your application to associate the result with the right customer snapshot.

A table containing days since the last purchase, purchase counts, spending totals, and channel counts is a sensible baseline. Keeping individual purchases gives the model access to combinations of article, amount, channel, and timing that those summaries may omit. Compare both approaches on later decision windows with the same eligible customers and available evidence. Use relational context when that comparison justifies its preparation and processing cost.

Train and predict from Parquet files

Prepare your application’s training and validation files with one customer observation per row and the fields above. Use earlier decision windows for training and later windows for validation:

data = rf.ArrowDataModule(
    train="retention/train.parquet",
    validate="retention/validation.parquet",
)
model.fit(data, batch_size=128, max_epochs=20)

For new decisions, prepare the same customer attributes and purchase history in a request file, omitting inactive_next_7_days:

requests = rf.ArrowDataModule(
    predict="retention/requests.parquet",
)
predictions = model.predict(datamodule=requests, batch_size=256, devices=1)

Read predictions explains how to read the result under predictions.inactive_next_7_days. The schema address remains /inactive_next_7_days.

Engagement: will an active reviewer stop posting?

A team seeking fresh product feedback can use the same structure to anticipate review inactivity. Choose customers who posted a review in the preceding 91 days, and define the target as no review during the following 91 days.

Customer attributes stay at the root. Earlier reviews form the history, keeping each product, rating, and review date together. This outcome measures participation in a feedback channel: a customer can keep buying without posting a review. Compare a richer review history with review recency and frequency summaries before using it to prioritize invitations for feedback.

Clinical studies: which studies will report larger totals?

A trial operations team may want to anticipate which ongoing studies will report larger totals of affected subjects. For studies that have already started, define a 365-day outcome: the sum of subjects_affected in reports marked serious or deaths that arrive after the decision time through 365 days later.

Study attributes provide the root context. Earlier reports form a branch, keeping report type, affected-subject count, and report date together. The later total is a masked numeric target. This sum can count a person in more than one report, so it does not measure unique affected people or an individual’s risk. Define how incomplete reporting affects label availability; a missing report does not establish a zero total.

Markets: what will tomorrow’s close be?

A market-data application can supply daily bars and ask for the next trading day’s closing price. A bar is a multivariate event: opening, high, low, and closing prices plus volume belong to the same interval. The history-and-target pattern stays the same:

market_model = rf.Model.xs(
    daily_bars=rf.Branch(
        length=64,
        overflow="tail",
        booked_at=rf.DateParts(
            dateparts=["day_of_month", "day_of_week", "hour_of_day"],
        ),
        open=rf.Number,
        high=rf.Number,
        low=rf.Number,
        close=rf.Number,
        volume=rf.Number,
    ),
    next_close=rf.Number(mask=True),
)

Each bar keeps its price and volume measurements together. Prepare observations at the end of a trading day, including only completed bars known then. booked_at supplies calendar context for each completed bar. The next_close label comes from the following trading day and becomes available after that day closes. Train from Parquet files through ArrowDataModule, as above, using files prepared for this market schema.

This defines one future-value prediction. If the application needs several future steps, define their target structure and evaluation horizon explicitly. Include a last-known-close baseline when evaluating this forecast.

Keep the decision time in every example

Every example follows Prepare observations: construct each observation as it could have been seen at its decision time, attach labels from the specified future window, and let that window finish before treating them as observed outcomes. Two points matter more outside payments. An event that happened earlier but was reported later is unavailable until its report arrives, and mutable customer or study attributes need the values known at the decision time.

The same entity can appear at several decision times. Keep later evaluation windows separate and control overlap between their label periods and training examples. Evaluation explains the comparison; Preprocessing covers preparing the nested inputs.

This is the end of the learning path. To understand how the model combines the evidence, continue with the model tree and how fields interact. To put a model into production, see Serving and Batch inference.