Skip to navigation

Anti-Targeting Solution

View as Markdown

A campaign team has a limited budget and wants to recognize the people least likely to buy before spending another impression on them. Anti-targeting uses a prediction of nonpurchase to identify candidates for an audience exclusion or a lower spending priority. This example makes three kinds of that decision: it predicts which campaign users will make no purchase in the next 30 days, restricts that prediction to users who have not bought before, and uses prepared labels for a campaign with a fixed outcome period. Kumo Engine learns from the user profiles and their connected data; the prediction describes who is likely to buy, not whether showing an advertisement would change their behavior.

Data and relationships

The temporal examples assume a prepared Kumo Engine graph, a matching snapshot, and these tables:

TableRequired columnsPurpose
usersuser_id as the primary key, plus campaign_idThe users to score. Each user belongs to one campaign in this example.
sessionssession_id as the primary key, plus user_id, session_time, and purchasedThe activity log. purchased is a Boolean that is true when the session contains a reconciled purchase of the promoted product.

Connect sessions.user_id to users.user_id. Mark session_time as the time column of the sessions table. Use sessions for the promoted product and campaign represented by each user, so a purchase of an unrelated product does not count as a conversion. The users table has no time column of its own, so the forward window starts at the scoring time rather than at a date on the user row. The 30-day window excludes sessions at the exact scoring time and includes sessions through 30 days after that time.

The static example uses a separate prepared graph and snapshot with the same users table and a labels table:

TableRequired columnsPurpose
labelslabel_id as the primary key, plus user_id and targetOne row per user for a campaign. Set target to 1 for a purchase during the chosen outcome period, 0 for no purchase, and null when the outcome is unknown and the row should be scored.

Connect labels.user_id to users.user_id. Neither table has a time column in this static graph. Prepare the user features from information available before the campaign’s outcome period, and keep later activity out of the feature data. Unlike the temporal queries, the static query does not define a time window: the labels must already encode the campaign and period you want to predict.

Predict which campaign users will not purchase

This query flags users in campaign 1 who will make no purchase of the promoted product during the next 30 days. COUNT(sessions.* WHERE sessions.purchased = true, 0, 30, days) counts purchase sessions, and = 0 makes the target true when there are none. A user who browses and then buys has a false target; a user who has no sessions at all has a true target. Counting nonpurchase sessions instead would answer a different question, because a buyer can also browse without buying.

from kumo.engine import PredictiveQuery
query_text = (
"PREDICT COUNT(sessions.* "
"WHERE sessions.purchased = true, 0, 30, days) = 0 "
"FOR EACH users.user_id "
"WHERE users.campaign_id = 1"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # binary_classification

Replace 1 with the campaign you want to score. This version includes both previous buyers and users who have never bought, so it also supports a campaign where repeat purchases matter.

Exclude users who already purchased

For a product customers usually buy once, the campaign may focus on people who have not purchased it yet. This query keeps the same 30-day nonpurchase target but adds a condition on purchase history. The -inf lower bound covers all history available in the graph, and the upper bound 0 includes purchases at the scoring time. The filter removes users with any recorded purchase by that moment; it cannot account for purchases missing from the source data.

query_text = (
"PREDICT COUNT(sessions.* "
"WHERE sessions.purchased = true, 0, 30, days) = 0 "
"FOR EACH users.user_id "
"WHERE users.campaign_id = 1 "
"AND COUNT(sessions.* "
"WHERE sessions.purchased = true, -inf, 0, days) = 0"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # binary_classification

The extra condition changes which users receive a prediction, not what the target means. Among the eligible campaign users, a true target still means no purchase over the following 30 days.

Predict a fixed campaign outcome from prepared labels

A campaign with a predefined outcome period may already have a label for every historical user. Use the separate static graph described above for this query, with graph and snapshot referring to that graph. labels.target = 0 asks whether the user represented by a label row will finish the campaign’s outcome period without purchasing. Known labels supply the training outcomes; rows with a null target are the candidates to score.

query_text = (
"PREDICT labels.target = 0 "
"FOR EACH labels.label_id"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # binary_classification

This query predicts for label_id, so retain labels.user_id to associate each result with its user. A user identifier and a label identifier are different entities even when the example has one label per user. Keep the label definition consistent across historical and scoring rows, including the promoted product and the length of the outcome period.

Reading the predictions

Each query returns a probability of nonpurchase. A higher probability identifies a stronger candidate for an audience exclusion or a lower spending priority. The first two predictions identify users and look ahead 30 days from the scoring time. The third identifies label rows and uses the outcome period encoded in the prepared labels.

These scores estimate purchase behavior, not the incremental effect of an advertisement. Use them alongside campaign rules and consent requirements, and evaluate an exclusion policy before applying it broadly. Change the campaign identifier and the 30-day horizon to match your decision, then refresh the temporal scores as new sessions arrive.