Chapter 23 — Recommender Systems
Recommender systems suggest items users may like. This chapter builds the two classic approaches — collaborative filtering with similarity and model-based matrix factorization with SVD — on a small synthetic ratings matrix.
Learning Objectives
- Represent user–item interactions as a utility matrix.
- Compute user-based and item-based collaborative filtering with cosine similarity.
- Predict missing ratings from similar users/items.
- Apply TruncatedSVD for model-based recommendations.
- Generate top-N recommendations for a user.
- Discuss the cold-start problem.
Prerequisites / Imports
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.decomposition import TruncatedSVD
1 A Ratings Matrix
Rows are users, columns are movies; NaN marks unrated items.
ratings = pd.DataFrame({
'Matrix': [5, 5, np.nan, 1, np.nan, 4],
'Inception': [4, np.nan, 5, 1, 4, 5],
'Titanic': [1, 2, 5, 5, np.nan, np.nan],
'Avatar': [np.nan, 4, 4, 4, 5, 3],
'Interstellar': [5, 5, 4, 2, 4, 5],
'Notebook': [np.nan, 1, 5, 5, 1, np.nan],
}, index=['Alice','Bob','Carol','Dave','Eve','Frank'])
ratings
| Matrix | Inception | Titanic | Avatar | Interstellar | Notebook | |
|---|---|---|---|---|---|---|
| Alice | 5.0 | 4.0 | 1.0 | NaN | 5 | NaN |
| Bob | 5.0 | NaN | 2.0 | 4.0 | 5 | 1.0 |
| Carol | NaN | 5.0 | 5.0 | 4.0 | 4 | 5.0 |
| Dave | 1.0 | 1.0 | 5.0 | 4.0 | 2 | 5.0 |
| Eve | NaN | 4.0 | NaN | 5.0 | 4 | 1.0 |
| Frank | 4.0 | 5.0 | NaN | 3.0 | 5 | NaN |
2 User-Based Collaborative Filtering
Find users most similar to a target, then predict unrated items from their ratings.
filled = ratings.fillna(0)
user_sim = cosine_similarity(filled)
user_sim_df = pd.DataFrame(user_sim, index=ratings.index, columns=ratings.index)
np.fill_diagonal(user_sim_df.values, 0)
user_sim_df.round(2)
| Alice | Bob | Carol | Dave | Eve | Frank | |
|---|---|---|---|---|---|---|
| Alice | 0.00 | 0.75 | 0.53 | 0.35 | 0.58 | 0.92 |
| Bob | 0.75 | 0.00 | 0.59 | 0.64 | 0.64 | 0.78 |
| Carol | 0.53 | 0.59 | 0.00 | 0.90 | 0.77 | 0.64 |
| Dave | 0.35 | 0.64 | 0.90 | 0.00 | 0.57 | 0.42 |
| Eve | 0.58 | 0.64 | 0.77 | 0.57 | 0.00 | 0.83 |
| Frank | 0.92 | 0.78 | 0.64 | 0.42 | 0.83 | 0.00 |
3 Predict a Rating
Predict Alice's rating for Avatar using a similarity-weighted average of other users.
target = 'Alice'
item = 'Avatar'
sim = user_sim_df.loc[target]
item_ratings = ratings[item].dropna()
weights = sim.loc[item_ratings.index]
pred = np.average(item_ratings, weights=weights)
print(f'Predicted {target} rating for {item}: {pred:.2f}')
Predicted Alice rating for Avatar: 3.89
4 Item-Based Collaborative Filtering
Instead, compare items by who rated them; predict from similar items the user rated.
item_sim = cosine_similarity(filled.T)
item_sim_df = pd.DataFrame(item_sim, index=ratings.columns, columns=ratings.columns)
target = 'Alice'
item = 'Avatar'
sim_items = item_sim_df[item].drop(item).sort_values(ascending=False).head(3)
alice_ratings = ratings.loc[target, sim_items.index].dropna()
if len(alice_ratings):
pred = np.average(alice_ratings, weights=sim_items.loc[alice_ratings.index])
print(f'Item-based prediction for {target}->{item}: {pred:.2f} (from {list(alice_ratings.index)}')
else:
print(f'{target} has no ratings for items similar to {item}')
Item-based prediction for Alice->Avatar: 4.54 (from ['Interstellar', 'Inception']
5 Model-Based: Matrix Factorization with SVD
SVD learns latent factors for users and items, filling the whole matrix at once.
svd = TruncatedSVD(n_components=2, random_state=42)
latent = svd.fit_transform(filled)
print('user latent factors shape:', latent.shape)
pred_all = svd.inverse_transform(latent).dot(np.eye(len(ratings.columns)))
pred_df = pd.DataFrame(pred_all, index=ratings.index, columns=ratings.columns).round(1)
pred_df
user latent factors shape: (6, 2)
| Matrix | Inception | Titanic | Avatar | Interstellar | Notebook | |
|---|---|---|---|---|---|---|
| Alice | 4.4 | 3.5 | -0.1 | 2.0 | 5.0 | -0.6 |
| Bob | 3.3 | 3.4 | 1.3 | 2.9 | 4.6 | 1.0 |
| Carol | 0.8 | 3.5 | 4.9 | 5.1 | 3.9 | 5.0 |
| Dave | -0.4 | 2.3 | 4.4 | 4.1 | 2.3 | 4.6 |
| Eve | 2.2 | 2.9 | 2.0 | 3.0 | 3.7 | 1.8 |
| Frank | 4.5 | 3.9 | 0.6 | 2.7 | 5.5 | 0.1 |
6 Top-N Recommendations
Recommend items a user hasn't rated, ranked by predicted rating.
target = 'Alice'
unrated = ratings.loc[target].isna()
recs = pred_df.loc[target, unrated].sort_values(ascending=False).head(3)
print(f'Recommendations for {target}:')
for item, score in recs.items():
print(f' {item}: predicted {score:.2f}')
Recommendations for Alice: Avatar: predicted 2.00 Notebook: predicted -0.60
7 The Cold-Start Problem
New users or items have no ratings, breaking similarity-based methods. Remedies include content-based features, popularity baselines, and hybrid recommenders.
Case Study: Movie Recommendations for a New User
A new user rates two sci-fi films; we recommend more via item-based similarity.
new_user = pd.Series({'Matrix': 5, 'Inception': 5, 'Titanic': np.nan, 'Avatar': np.nan, 'Interstellar': np.nan, 'Notebook': np.nan})
new_filled = new_user.fillna(ratings.mean().round(1))
sim_items = item_sim_df['Matrix'].sort_values(ascending=False)
print('Items most similar to Matrix (that the new user can be recommended):')
print(sim_items.drop('Matrix').head(3).round(2))
Items most similar to Matrix (that the new user can be recommended): Interstellar 0.83 Inception 0.55 Avatar 0.49 Name: Matrix, dtype: float64
Exercises
- Build a 4x5 ratings matrix with some NaNs and display it.
- Compute user–user cosine similarity and identify each user's nearest neighbor.
- Predict a missing rating using user-based CF.
- Compute item–item similarity and predict via item-based CF.
- Fit TruncatedSVD with 2 components and print the latent factors.
- Produce top-3 recommendations for one user.
- Explain the difference between user-based and item-based CF.
- Describe the cold-start problem and one solution.
- Discuss how a popularity baseline helps new users.
- Add a new user and recommend items using item-based similarity.
Python Data Science: From Foundations to Applications — Chapter 23