Skip to navigation

Item-to-Item Recommendation

View as Markdown

Recommend products to show beside an item a shopper is viewing. This example ranks candidate products by whether they will appear in an order with the viewed item during the next 30 days. The target is a future pair of products, not whether this particular shopper will buy either one.

Data and relationships

The example assumes a Kumo Engine graph and matching snapshot with these tables:

TableRequired data and role
anchor_itemsitem_id primary key. Products to recommend beside.
candidate_itemsitem_id primary key. Products to recommend. This can use the same catalog extract.
item_pairspair_id primary key; anchor_item_id, related_item_id, and timestamp. Products observed together in an order.

For each distinct product pair in an order, record a row in each direction. Connect item_pairs.anchor_item_id to anchor_items.item_id and item_pairs.related_item_id to candidate_items.item_id. Mark item_pairs.timestamp as the time column. Keep the two item roles separate in the graph so each pair column has one unambiguous relationship. Do not create pairs from products that merely share a category or description: those would change the prediction target.

Rank future product pairs

The query asks Kumo Engine to rank candidate products for each anchor product using pair events in a 30 day future window. LIST_DISTINCT counts a candidate once as a target even when it appears in several orders in that window.

from datetime import datetime
from kumo.engine import PredictiveQuery
query_text = (
"PREDICT LIST_DISTINCT(item_pairs.related_item_id, 0, 30, days) "
"RANK TOP 20 FOR EACH anchor_items.item_id"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type()) # temporal_link_prediction
prediction_time = datetime(2024, 4, 30, 12) # Replace with your chosen UTC time.
prediction_table = query.generate_prediction_table(
snapshot, prediction_time=prediction_time
)
ranked = trainer.predict(
prediction_table, snapshot=snapshot, num_classes_to_return=20
).frame
display = ranked.loc[ranked["ENTITY"] != ranked["CLASS"]]

Here trainer is a fitted model for this query. Choose a prediction time after the latest pair event in the snapshot. The prediction table selects anchor products and a time; it does not train the model. In the ranked output, ENTITY is an anchor item, CLASS is a candidate item, and SCORE orders candidates for that anchor. A score does not guarantee that the products will share an order.

The display rows omit any candidate that is the same product as the anchor. You can use the remaining ranked candidates for a product detail page. This query learns from observed order pairs; it does not establish that products are visually or semantically similar, or that a specific shopper prefers them. The request for 20 output classes is applied before the display filter, so fewer than 20 candidates may remain.