<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Sushmeet Kaur Saluja on Medium]]></title>
        <description><![CDATA[Stories by Sushmeet Kaur Saluja on Medium]]></description>
        <link>https://medium.com/@sushmeetsaluja1?source=rss-2fbc03421f27------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*dmbNkD5D-u45r44go_cf0g.png</url>
            <title>Stories by Sushmeet Kaur Saluja on Medium</title>
            <link>https://medium.com/@sushmeetsaluja1?source=rss-2fbc03421f27------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 01:07:53 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@sushmeetsaluja1/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How Netflix and Amazon Recommendations Work]]></title>
            <link>https://medium.com/@sushmeetsaluja1/how-netflix-and-amazon-recommendations-work-74481d24d5c1?source=rss-2fbc03421f27------2</link>
            <guid isPermaLink="false">https://medium.com/p/74481d24d5c1</guid>
            <category><![CDATA[recommendation-system]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[design-systems]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[data-structures]]></category>
            <dc:creator><![CDATA[Sushmeet Kaur Saluja]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 06:59:01 GMT</pubDate>
            <atom:updated>2026-11-04T06:59:01.979Z</atom:updated>
            <content:encoded><![CDATA[<h4>A deep dive into collaborative filtering: sparse matrices, cosine similarity and k-NN with a heap</h4><p>When we talk about Netflix or Amazon recommendations, the conversation usually stops at “AI knows what I like”. The real story is simpler and more interesting: a sparse matrix, one similarity formula and a heap.</p><p>Recommendations drive a large share of what people watch on Netflix and buy on Amazon, yet the core idea fits in a few dozen lines of code. This post walks through the data structure, the maths and the flow, with a small example you can run.</p><p>Let’s break this down.</p><h3>The Core Idea: “People Who Liked This Also Liked…”</h3><p>This is <strong>collaborative filtering</strong>. The system never needs to know what a movie is about. It only looks at behaviour: who rated, watched or bought what.</p><p>Consider five users rating movies from 1 to 5 (- = not watched):</p><pre>User   | Inception | Interstellar | Matrix | Titanic | Notebook<br>-------+-----------+--------------+--------+---------+---------<br>Alice  |     5     |      4       |   5    |    -    |    -<br>Bob    |     4     |      5       |   -    |    1    |    -<br>Carol  |     5     |      -       |   4    |    -    |    1<br>Dave   |     -     |      -       |   -    |    5    |    5<br>Eve    |     4     |      5       |   5    |    -    |    2</pre><p>Alice loves sci-fi, and so do Bob, Carol and Eve. They disliked the romance films Alice hasn’t seen, so the system won’t push romance to her. Nobody told it what “sci-fi” means; the pattern comes from the ratings alone.</p><h4>Two Flavours</h4><ul><li><strong>User-based:</strong> find people like you, recommend what they liked. (This post.)</li><li><strong>Item-based:</strong> find items liked by the same people as this item. This is Amazon’s “Customers who bought this also bought”.</li></ul><h3>Storing It: The Sparse User-Item Matrix</h3><p>Every recommender starts with a matrix: one row per user, one column per item. The catch is that it is almost entirely empty. In the public Netflix Prize dataset, about 99% of the cells had no rating.</p><h4>Why This Matters</h4><p>A full matrix for millions of users and items would be billions of cells of mostly zeros. Storing them wastes memory and slows every calculation.</p><h4>How to Store It</h4><ul><li><strong>Dictionary of dictionaries:</strong> each user maps to only the items they rated. Fast inserts and lookups, ideal for collecting live data.</li><li><strong>CSR (Compressed Sparse Row):</strong> three flat arrays (data, indices, indptr) that make bulk maths fast. Used by SciPy and most ML pipelines.</li></ul><pre>ratings = {<br>    &quot;alice&quot;: {&quot;inception&quot;: 5, &quot;interstellar&quot;: 4, &quot;matrix&quot;: 5},<br>    &quot;bob&quot;:   {&quot;inception&quot;: 4, &quot;interstellar&quot;: 5, &quot;titanic&quot;: 1},<br>    &quot;carol&quot;: {&quot;inception&quot;: 5, &quot;matrix&quot;: 4, &quot;notebook&quot;: 1},<br>    &quot;dave&quot;:  {&quot;titanic&quot;: 5, &quot;notebook&quot;: 5},<br>    &quot;eve&quot;:   {&quot;inception&quot;: 4, &quot;interstellar&quot;: 5, &quot;matrix&quot;: 5, &quot;notebook&quot;: 2},<br>}</pre><p>The same data in CSR, with items numbered 0 to 4:</p><pre>data    = [5, 4, 5,  4, 5, 1,  5, 4, 1,  5, 5,  4, 5, 5, 2]<br>indices = [0, 1, 2,  0, 1, 3,  0, 2, 4,  3, 4,  0, 1, 2, 4]<br>indptr  = [0, 3, 6, 9, 11, 15]   # where each user&#39;s row starts</pre><p>Memory now grows with the number of ratings, not with users × items.</p><h3>Measuring Taste: Cosine Similarity</h3><p>Treat each user as an arrow pointing through “movie space”. Cosine similarity measures the angle between two arrows: <strong>1</strong> means identical taste, <strong>0</strong> means nothing in common.</p><pre>cos(a, b) = Σ aᵢ·bᵢ / ( √Σ aᵢ²  ×  √Σ bᵢ² )</pre><pre>from math import sqrt</pre><pre>def cosine(a, b):<br>    dot = sum(r * b[i] for i, r in a.items() if i in b)<br>    if not dot:<br>        return 0.0<br>    norm = lambda v: sqrt(sum(x * x for x in v.values()))<br>    return dot / (norm(a) * norm(b))</pre><pre>cosine(ratings[&quot;alice&quot;], ratings[&quot;eve&quot;])   # 0.956</pre><h4>Why Angle, Not Distance?</h4><p>Some people rate everything a 5; others never go above a 3. They might still love the <em>same</em> films. Distance would call them different because their numbers differ. Cosine only cares about direction, so a generous rater and a strict rater with the same taste still match.</p><p>For star ratings, go one step further: subtract each user’s average rating first (<strong>Pearson / adjusted cosine</strong>). Then “below my usual” becomes a negative signal instead of a weak positive one.</p><h3>Finding Taste Twins: k-Nearest Neighbours with a Heap</h3><p>We don’t want every user’s opinion, only the <strong>k most similar</strong> (often 20 to 100). Fewer, closer neighbours means less noise.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1008/1*_XBtTQPgcq1IjUzguHQJwA.png" /></figure><h4>Why a Heap?</h4><p>Sorting all <em>n</em> users costs O(n log n), but we only need the top k. A <strong>min-heap of size k</strong> does it in O(n log k) with only k slots of memory.</p><p>Think of the heap’s root as a bouncer: it holds the <em>weakest</em> of the current top-k. A new candidate gets in only if they beat the bouncer; otherwise they’re turned away in one comparison.</p><pre>import heapq</pre><pre>def neighbours(user, ratings, k=20):<br>    scored = ((cosine(ratings[user], ratings[v]), v) for v in ratings if v != user)<br>    return [(s, v) for s, v in heapq.nlargest(k, scored) if s &gt; 0]</pre><pre>neighbours(&quot;alice&quot;, ratings, k=3)<br># [(0.956, &#39;eve&#39;), (0.855, &#39;carol&#39;), (0.760, &#39;bob&#39;)]</pre><p>At 100 million users with k = 50, that’s roughly 5× fewer comparisons than a full sort.</p><h3>The Flow: From Ratings to Recommendations</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SUqNd9MlitkZV37zPzdZTQ.png" /></figure><p>The one new piece is <strong>scoring unseen items</strong>: each neighbour votes with their rating, and closer neighbours get louder votes.</p><pre>predicted(u, i) = Σ sim(u, v) × rating(v, i)  /  Σ sim(u, v)</pre><pre>from collections import defaultdict</pre><pre>def recommend(user, ratings, k=20, n=10):<br>    if user not in ratings:<br>        return popular(ratings, n)<br>    seen = ratings[user]<br>    num, den = defaultdict(float), defaultdict(float)<br>    for sim, v in neighbours(user, ratings, k):<br>        for item, r in ratings[v].items():<br>            if item not in seen:<br>                num[item] += sim * r<br>                den[item] += sim<br>    scores = ((num[i] / den[i], i) for i in num)<br>    return heapq.nlargest(n, scores) or popular(ratings, n, seen)</pre><pre>def popular(ratings, n, exclude=()):<br>    counts = defaultdict(int)<br>    for row in ratings.values():<br>        for item in row:<br>            counts[item] += 1<br>    return heapq.nlargest(n, ((c, i) for i, c in counts.items() if i not in exclude))</pre><pre>recommend(&quot;alice&quot;, ratings, k=3)<br># [(1.53, &#39;notebook&#39;), (1.0, &#39;titanic&#39;)]  -&gt; both low: don&#39;t recommend romance</pre><p>Alice’s taste twins disliked both romance films, so their predicted scores are low. In a real catalogue, the sci-fi her neighbours loved would rise to the top instead.</p><h3>5 Principles for Good Recommendations</h3><h4>1. Never Store the Zeros</h4><p>A library doesn’t keep a shelf for every book it doesn’t own. Store only the ratings that exist: dict of dicts for live data, CSR for heavy maths.</p><h4>2. Compare Direction, Not Size</h4><p>Two friends can love the same films while one rates everything higher. Cosine (or Pearson for star ratings) matches taste, not enthusiasm.</p><h4>3. Keep Only the Top-k</h4><p>You don’t ask the whole city for a restaurant tip, just the few friends whose taste you trust. A heap keeps exactly those k, cheaply.</p><h4>4. Don’t Trust Weak Neighbours</h4><p>A user who shares one movie with you isn’t really your taste twin. Set a minimum similarity and a minimum number of votes per item, or a few weak opinions will produce confident-looking nonsense.</p><h4>5. Always Have a Fallback</h4><p>A brand-new user has no ratings, so collaborative filtering has nothing to work with (the <strong>cold start</strong> problem). Fall back to popular or trending items, or ask a few onboarding questions, rather than showing an empty screen.</p><h3>How Netflix and Amazon Scale It</h3><ul><li><strong>Item-to-item CF (Amazon):</strong> item relationships change slowly, so “similar items” lists are precomputed offline and looked up in milliseconds.</li><li><strong>Matrix factorization (Netflix Prize):</strong> compress the giant matrix into short dense vectors per user and item, so similarity becomes a fast dot product.</li><li><strong>Approximate nearest neighbours:</strong> libraries like FAISS or ScaNN find the top-k among hundreds of millions of vectors without checking them all.</li><li><strong>Two stages:</strong> cheap candidate generation picks a few hundred items, then a richer ranking model orders them using context like time and device.</li></ul><p>Recommendations go beyond clever AI. They are about storing data sparsely, measuring similarity honestly and keeping only the signals that matter. Get these basics right, and “people who liked this also liked…” stops being magic and becomes something you can build.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=74481d24d5c1" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>