I turned grocery order history into a personal shopping experience.
A shopping prototype that uses past orders to suggest familiar groceries and new discoveries. I compared five recommendation approaches, built the interface, and explored how people used it.
Results at a glance
Results at a glance
Overview
Three versions, then a prototype
Data: Instacart’s public 2017 dataset contains anonymous grocery orders, products and purchase sequences. I used an archived copy on Zenodo. These records do not represent all Instacart shoppers.
Compared five methods. Chose a simple rule that gives recent purchases more weight.
SelectedTested whether usual buying gaps improved suggestions. The gain was too small to justify adding it.
Not adoptedUsed patterns across shoppers to suggest unfamiliar products. Found more next-basket matches than popularity alone.
AddedBuilt both recommendation sections. Four early sessions covered repeat purchases; discovery user testing is next.
Discovery testing nextThe problem
Shoppers rebuy the same groceries, but still have to find them
A visible list could help returning shoppers reach familiar groceries with less searching. Historical orders let me test prediction accuracy; testing with people is needed to assess shopping effort.
Does personal history predict the next basket better than one list for everyone?
Matched comparison on 131,209 customers
Does machine learning add enough value over a simple rule?
Two fixed model attempts against the same baskets
Can people understand and use the list?
Four early sessions with the prototype
Does it actually save time without causing mistakes?
Needs a controlled test with more people
Scope
What I chose to build, and what I left out
I started with five familiar products and reversible controls. Discovery came later as a separate feature; prices, stock and checkout stayed outside scope.
In Version 1
- Up to five familiar products
- Add with one tap, never automatically
- Hide one exact product, then Show it again later
- Fewer than five when history is short, never random filler
- Popular products as a clear fallback for a new shopper
Left out
- New-product discovery (tested later in Version 3)
- Prices, stock and checkout
- Real accounts or an Instacart connection
- Learning from clicks in real time
- A separate dashboard
How I tested
Use earlier orders to predict a later basket
Every method faced the same 131,209 shoppers. Lists used earlier purchases only and were saved before checking final baskets.
Recall measures how much of the next basket the list found. Precision measures how many suggestions matched. Hit rate asks whether at least one matched.
Methods
Start simple, then test the extra complexity
I compared three simple rules and two learned models. Recent purchases earned more weight in the rule I selected.
- 1
General popularity
The same popular products for everyone.
- 2
Personal frequency
The products each shopper bought most often.
- 3
Weighted recency
Add purchase points, giving recent orders more weight.
- 4
Logistic regression
Learn how to combine five purchase-history clues.
- 5
Boosted trees
Use small decision trees to combine those clues more flexibly.
Results
Personal history made the biggest difference
Personal history found about five times more of the next basket than one popular list for everyone. The two learned models added almost nothing over the simple rules.
My decision
I chose a simpler method, even though another scored slightly better
Boosted trees scored 0.18 points higher on recall. I picked the simpler rule after validation, before the final check.
Each earlier purchase contributes a visible number of points.
The default uses a fixed rule rather than a separate trained model.
Both ran fast locally, so the choice came down to clarity.
Version 2 · An idea I stopped
Purchase timing added too little
I gave extra points when a product looked due, based on its usual purchase gaps. The recall gain was 0.025 percentage points against a 0.25-point criterion fixed beforehand. I stopped this attempt instead of moving the goalposts.
+0.025 points. Its 95% range (−0.003 to +0.052) even includes zero.
Version 3 · Discovery
A separate list for something unfamiliar
I used shared-buyer patterns to rank products absent from each shopper’s recorded history. This keeps discovery separate from buying familiar products again.
- 1
Start with familiar products
Take up to ten products, weighted by recent purchases.
- 2
Find product connections
Look for shared buyers, adjusted for each product’s popularity.
- 3
Add personal contributions
Multiply each familiar product’s weight by its connection strength.
- 4
Remove familiar candidates
Rank the unfamiliar products; popularity fills any empty slots.
Share of shoppers with at least one match among five suggestions. Related products reached a net 219 more shoppers; most still had no match.
These shoppers’ records had already been reviewed, so new data would be the real check.
Prototype
A shopping page with personal suggestions
Two lists: Buy it again for products the shopper has bought before, and You might also like for products new to them. Shoppers A and B use real anonymous records; C is a new shopper.
Three shoppers, three states




User testing
Faster on the second try
Each person searched for their usual products, then found the same products again using suggestions.
All participants were given the same products in the same order.
Try it
Use the prototype
Add a product, hide one from Buy it again, then switch between Shoppers A, B and C. A and B also show You might also like. Open full screen ↗
What I learned
Good scores are only part of a useful product
Three lessons I would carry into the next product.
A result the team can explain can be worth more than the top score.
Set the bar before testing, and stop when an idea misses it.
Matching past baskets shows a list is accurate, not that it saves shoppers time.
