Skip to content

API Reference

Common interface

recommender_systems.base

The common interface shared by all recommenders.

Recommender

Bases: ABC

Abstract base class for recommendation algorithms.

Concrete recommenders learn from interaction data in :meth:fit and produce a ranked list of item ids in :meth:recommend. Sharing one interface lets algorithms be swapped and evaluated interchangeably.

fit abstractmethod
fit(ratings: DataFrame) -> Recommender

Train on a ratings frame.

Parameters:

Name Type Description Default
ratings DataFrame

Interaction data with at least user_id, item_id and rating columns.

required

Returns:

Type Description
Recommender

The fitted instance, so calls can be chained.

recommend abstractmethod
recommend(user_id: Hashable, n: int = 10) -> list[Hashable]

Return the top-n recommended item ids for user_id.

Parameters:

Name Type Description Default
user_id Hashable

The user to recommend for.

required
n int

Maximum number of items to return.

10

Returns:

Type Description
list of Hashable

Item ids ordered from most to least recommended.

Recommenders

Baselines

recommender_systems.baselines

Non-personalized baseline recommenders.

MostPopular

MostPopular()

Bases: _RankingBaseline

Rank items by how often they have been rated.

MeanRating

MeanRating(min_ratings: int = 1)

Bases: _RankingBaseline

Rank items by mean rating, requiring a minimum number of ratings.

Parameters:

Name Type Description Default
min_ratings int

Minimum number of ratings an item needs to be eligible.

1

Neighborhood collaborative filtering

recommender_systems.neighborhood

Neighborhood (k-NN) collaborative filtering on sparse matrices.

ItemKNN

ItemKNN(k: int = 20)

Bases: _SparseMatrixBackedRecommender

Score items by similarity to the items a user has already rated.

Builds a sparse item-item cosine similarity matrix, keeps the k largest entries per row, and scores by S @ user_row. The full similarity is materialized in CSR form, which is fine even for goodbooks-10k (10k by 10k items would be 100M entries dense; in sparse form after the top-k trim it's k * n_items = a few hundred thousand non-zeros).

Parameters:

Name Type Description Default
k int

Number of nearest neighbors retained per item.

20

UserKNN

UserKNN(k: int = 20)

Bases: _SparseMatrixBackedRecommender

Score items by the ratings of a user's nearest neighbors.

Builds the neighborhood with :class:sklearn.neighbors.NearestNeighbors (cosine metric on the sparse user-item matrix) rather than materializing the full users-by-users similarity — that's n_users**2 and blows up past a few thousand users. Per-user scoring weights the neighbors' rating rows by their similarities.

Parameters:

Name Type Description Default
k int

Number of nearest neighbors retained per user.

20

Matrix factorization

recommender_systems.svd

Matrix-factorization recommender via truncated SVD.

SVD

SVD(n_factors: int = 20, random_state: int | None = None)

Bases: _SparseMatrixBackedRecommender

Recommend from a low-rank reconstruction of the user-item matrix.

Operates on the sparse user-item matrix directly. TruncatedSVD accepts CSR natively, so the dense (n_users, n_items) matrix never has to be materialized — the win that opens the goodbooks-full corpus to SVD.

Parameters:

Name Type Description Default
n_factors int

Number of latent factors. Clamped to the matrix rank when smaller.

20
random_state int | None

Seed for the SVD solver.

None

Bayesian Personalized Ranking

recommender_systems.bpr

Bayesian Personalized Ranking for implicit feedback.

BPR

BPR(n_factors: int = 32, epochs: int = 20, learning_rate: float = 0.05, reg: float = 0.01, random_state: int | None = None)

Bases: _MatrixBackedRecommender

Bayesian Personalized Ranking — learns item embeddings from implicit feedback.

Every observed (user, item) interaction is treated as a positive signal; random unobserved items are sampled as negatives. The objective is to score each positive higher than its sampled negative — implemented as SGD on the sigmoid-margin loss with L2 regularization. The rating values themselves are ignored; only presence of an interaction matters.

Parameters:

Name Type Description Default
n_factors int

Dimensionality of the user and item embeddings.

32
epochs int

Number of full passes over the observed interactions.

20
learning_rate float

SGD step size.

0.05
reg float

L2 regularization strength applied to user and item factors.

0.01
random_state int | None

Seed for initialization and negative sampling.

None

Alternating Least Squares

recommender_systems.als

Alternating Least Squares for implicit-feedback matrix factorization.

ALS

ALS(n_factors: int = 32, epochs: int = 15, regularization: float = 0.01, alpha: float = 40.0, random_state: int | None = None)

Bases: _PredictedScoreRecommender

Implicit-feedback matrix factorization via alternating least squares.

Follows Hu, Koren & Volinsky (2008): binary preferences p_ui = 1 if r_ui > 0 with confidence c_ui = 1 + alpha * r_ui, then alternate closed-form solves for the user factors X and item factors Y of the regularized weighted least-squares loss.

Same algorithm family as :class:recommender_systems.bpr.BPR but a different optimization story — closed-form alternating solves instead of SGD on a sigmoid margin — so it tends to converge in many fewer epochs.

Parameters:

Name Type Description Default
n_factors int

Latent factor dimensionality.

32
epochs int

Full passes over the (X, Y) update cycle.

15
regularization float

L2 regularization strength applied to the factor solves.

0.01
alpha float

Confidence scaling: a rating of r contributes weight 1 + alpha * r. Higher alpha makes the model trust observed ratings more.

40.0
random_state int | None

Seed for factor initialization.

None

Two-tower neural CF

recommender_systems.neural

Two-tower neural collaborative filtering (PyTorch).

Optional — requires the neural extra:

pip install 'recommender-systems[neural]'

TwoTowerCF

TwoTowerCF(n_factors: int = 32, epochs: int = 20, learning_rate: float = 0.01, batch_size: int = 256, random_state: int | None = None)

Bases: _PredictedScoreRecommender

Two-tower neural CF trained with a BPR-style ranking loss.

Each user and item is a learned embedding; the score for a (user, item) pair is the dot product of their vectors. Training samples observed (positive) and random unobserved (negative) items per user and maximizes the sigmoid margin between them — the same objective as :class:recommender_systems.bpr.BPR, lifted onto PyTorch for autograd, batched SGD, and easy extension (e.g. adding side information into either tower).

Parameters:

Name Type Description Default
n_factors int

Embedding dimensionality.

32
epochs int

Full passes over the observed interactions.

20
learning_rate float

Adam optimizer step size.

0.01
batch_size int

Triples per gradient step.

256
random_state int | None

Seed for embedding init and negative sampling.

None

Content-based

recommender_systems.content

Content-based recommendation by item-feature similarity.

ContentBased

ContentBased(item_features: DataFrame)

Bases: _PredictedScoreRecommender

Recommend items whose features resemble those a user has liked.

The user's profile is a ratings-weighted mean of the features of the items they rated; recommendations are ranked by cosine similarity between that profile and each candidate item.

Side information is passed at construction time rather than to fit. This keeps fit(ratings) uniform across the library so algorithms stay interchangeable; see CONTRIBUTING.md for the convention.

Parameters:

Name Type Description Default
item_features DataFrame

DataFrame indexed by item id with one numerical feature per column. Callers pre-compute features from raw inputs (TF-IDF over descriptions, embeddings, one-hot categoricals, etc.) before passing them in.

required
explain
explain(user_id: Hashable, item_id: Hashable, top_features: int = 3) -> str

Return the top features driving item_id's score for user_id.

For each feature, the contribution to the user-item similarity is the product of the user's profile weight and the item's feature weight. The top non-zero features are returned as a comma-separated string — e.g. "fantasy, magic, epic" for a fantasy-genre recommendation. Returns "" if the user or item is unknown or the user has no profile.

recommend_with_reasons
recommend_with_reasons(user_id: Hashable, n: int = 10, top_features: int = 3) -> list[tuple[Hashable, str]]

Top-n recommendations for user_id paired with their explanations.

Each entry is (item_id, reason) where reason is the output of :meth:explain for that item — empty string if no features contribute.

Hybrid

recommender_systems.hybrid

Combine several recommenders by weighted reciprocal-rank fusion.

HybridRecommender

HybridRecommender(recommenders: Sequence[Recommender], weights: Sequence[float] | None = None, *, rank_constant: int = 60, pool: int = 100)

Bases: Recommender

Blend several recommenders via weighted reciprocal-rank fusion.

Each component produces its own ranked list; an item scores sum_r weight_r / (rank_constant + rank_r), so items ranked highly by several weighted components rise to the top. Because the components already exclude items the user has rated, the fused list does too. Rank fusion needs only the public recommend output, so any recommender can be combined.

Parameters:

Name Type Description Default
recommenders Sequence[Recommender]

Component recommenders to combine.

required
weights Sequence[float] | None

Per-component weights; defaults to equal weighting.

None
rank_constant int

Dampens the contribution of top ranks (the standard RRF constant).

60
pool int

How many items to pull from each component before fusing.

100

Book-specific helpers

recommender_systems.books

Book-specific helpers built on the goodbooks-10k loaders.

Drop-in pipelines for the book recommender — turn the tag table from load_goodbooks_tags into a content-based recommender in one call, so callers don't have to repeat the join + vectorize + ContentBased boilerplate.

tag_text_per_book

tag_text_per_book(tags: DataFrame) -> dict[Hashable, str]

Concatenate each book's tag names into a single space-separated string.

The schema matches what recommender_systems.datasets.load_goodbooks_tags returns: columns book_id, tag_name, count. Books that share many tags end up with overlapping vocabularies, which is what drives TF-IDF similarity downstream.

build_tag_recommender

build_tag_recommender(tags: DataFrame, **vectorizer_kwargs: Any) -> ContentBased

Build a ContentBased recommender from goodbooks-style tag data.

Each book's tags are joined into one document; text_features runs TF-IDF over the corpus to weigh rare-across-catalog tags higher than common ones. The returned recommender follows the side-information convention — fit it on the ratings frame (with item_id matching the book_id values used here) and call recommend(user_id, n) as usual.

Parameters:

Name Type Description Default
tags DataFrame

DataFrame with columns book_id, tag_name, count.

required
**vectorizer_kwargs Any

Forwarded to text_features (and thence to scikit-learn's TfidfVectorizer) — e.g. stop_words="english" or min_df=2.

{}

build_hybrid_book_recommender

build_hybrid_book_recommender(tags: DataFrame, *, collaborative: Recommender | None = None, weights: tuple[float, float] = (3.0, 1.0), rank_constant: int = 60, **vectorizer_kwargs: Any) -> HybridRecommender

Blend a collaborative recommender with the tag-based content recommender.

Defaults to ItemKNN(k=20) on the collaborative side because item-item kNN composes well with content signal (both rank items by similarity, just over different spaces). Pass any other Recommender to swap that out.

The default weights=(3.0, 1.0) puts the collaborative signal in charge — on benchmarked datasets the dense CF signal is much stronger than the tag-only content one, so equal weighting dilutes accuracy. The content half still contributes useful boosts for items both signals agree on, plus a cold-start fallback for items the CF half has never seen.

Parameters:

Name Type Description Default
tags DataFrame

DataFrame with columns book_id, tag_name, count — fed into :func:build_tag_recommender.

required
collaborative Recommender | None

Recommender to use on the collaborative side. None (default) uses ItemKNN(k=20).

None
weights tuple[float, float]

(collab_weight, content_weight) for the underlying :class:HybridRecommender (RRF fusion).

(3.0, 1.0)
rank_constant int

Forwarded to :class:HybridRecommender.

60
**vectorizer_kwargs Any

Forwarded to :func:build_tag_recommender (and thence to scikit-learn's TfidfVectorizer) — useful for max_features on big tag catalogs.

{}

Data

Loading benchmarks

recommender_systems.datasets

Dataset loaders for common recommender benchmarks.

load_movielens_100k

load_movielens_100k(data_home: str | Path | None = None) -> pd.DataFrame

Load the MovieLens 100k ratings, downloading and caching on first use.

Parameters:

Name Type Description Default
data_home str | Path | None

Directory to cache the dataset in. Defaults to ~/.recommender_systems.

None

Returns:

Type Description
DataFrame

Ratings with columns user_id, item_id, rating, timestamp.

load_goodbooks_10k

load_goodbooks_10k(data_home: str | Path | None = None) -> pd.DataFrame

Load the goodbooks-10k ratings, downloading and caching on first use.

For the open-source benchmark/demo only: the dataset derives from Goodreads and is not licensed for shipping in a commercial app (see the README).

Returns:

Type Description
DataFrame

Ratings with columns user_id, book_id, rating.

load_goodbooks_books

load_goodbooks_books(data_home: str | Path | None = None) -> pd.DataFrame

Load goodbooks-10k book metadata (title, authors, ids, average rating, ...).

load_goodbooks_tags

load_goodbooks_tags(data_home: str | Path | None = None) -> pd.DataFrame

Load goodbooks-10k tags joined to book_id and tag name.

Returns:

Type Description
DataFrame

Columns book_id, tag_name, count — ready to build content features.

Preparing interaction data

recommender_systems.data

Utilities for preparing interaction data for recommenders.

build_user_item_matrix

build_user_item_matrix(ratings: DataFrame, *, user_col: str = 'user_id', item_col: str = 'item_id', rating_col: str = 'rating', fill_value: float | None = None) -> pd.DataFrame

Pivot a long ratings frame into a user-by-item matrix.

Parameters:

Name Type Description Default
ratings DataFrame

Long-format interactions with one row per (user, item) rating.

required
user_col str

Column names to read from ratings.

'user_id'
item_col str

Column names to read from ratings.

'user_id'
rating_col str

Column names to read from ratings.

'user_id'
fill_value float | None

Value for user-item pairs with no rating. None (default) leaves them as NaN.

None

Returns:

Type Description
DataFrame

Rows indexed by user, columns by item, values the rating. Duplicate (user, item) pairs are averaged.

split_ratings

split_ratings(ratings: DataFrame, *, test_size: float = 0.2, random_state: int | None = None) -> tuple[pd.DataFrame, pd.DataFrame]

Split interactions into train and test sets by random row sampling.

Parameters:

Name Type Description Default
ratings DataFrame

Interactions to split.

required
test_size float

Fraction of rows to place in the test set, in the open interval (0, 1).

0.2
random_state int | None

Seed for reproducible splits.

None

Returns:

Type Description
tuple of pandas.DataFrame

(train, test) with disjoint rows whose union is ratings.

densest_subset

densest_subset(ratings: DataFrame, *, n_users: int = 1000, n_items: int = 1000, user_col: str = 'user_id', item_col: str = 'item_id') -> pd.DataFrame

Restrict ratings to the most active users and most popular items.

Lets dense-matrix algorithms run on large datasets (e.g. goodbooks-10k) without materializing a full user-by-item matrix. Keeps the n_users users with the most interactions and the n_items most-interacted items, then the rows in both.

Returns:

Type Description
DataFrame

The filtered ratings.

holdout_per_user

holdout_per_user(ratings: DataFrame, *, test_size: float = 0.2, random_state: int | None = None, user_col: str = 'user_id') -> tuple[pd.DataFrame, pd.DataFrame]

Hold out a fraction of each user's interactions for testing.

Unlike :func:split_ratings (a global row split), this guarantees every user with at least two interactions keeps training history — the standard protocol for top-N recommender evaluation, so no user is left cold in the test set. Users with a single interaction stay entirely in train.

Returns:

Type Description
tuple of pandas.DataFrame

(train, test).

build_sparse_user_item_matrix

build_sparse_user_item_matrix(ratings: DataFrame, *, user_col: str = 'user_id', item_col: str = 'item_id', rating_col: str = 'rating') -> tuple[sparse.csr_matrix, pd.Index, pd.Index]

Build a sparse user-item matrix plus its id-to-position index maps.

Scales to corpora where the dense :func:build_user_item_matrix would not fit in memory (e.g. full goodbooks-10k). Duplicate (user, item) pairs are averaged, matching the dense builder.

Returns:

Type Description
tuple

(matrix, users, items) — a CSR matrix, an index mapping row to user id, and an index mapping column to item id (use .get_loc(id) for the reverse direction).

Building feature matrices from text

recommender_systems.features

Build item feature matrices from text, for use with ContentBased.

text_features

text_features(item_text: Mapping[Hashable, str], *, method: str = 'tfidf', **vectorizer_kwargs: object) -> pd.DataFrame

Vectorize each item's text into a numerical feature matrix.

The result is indexed by item id with one column per term, ready to pass to ContentBased(item_features=...). Item descriptions, concatenated tags, or any per-item text all work.

Parameters:

Name Type Description Default
item_text Mapping[Hashable, str]

Mapping from item id to its text.

required
method str

"tfidf" (TF-IDF weights), "count" (term counts), or "binary" (term presence).

'tfidf'
**vectorizer_kwargs object

Forwarded to the underlying scikit-learn vectorizer (e.g. stop_words, max_features, ngram_range).

{}

Returns:

Type Description
DataFrame

Item-by-term feature matrix.

Evaluation metrics

recommender_systems.metrics

Metrics for top-N recommendation evaluation.

Accuracy metrics (precision, recall, MAP, NDCG) take parallel sequences of predicted ranked lists and held-out relevant item sets — one entry per user — and return a macro-averaged score across users that have at least one held-out item. Beyond-accuracy metrics (diversity, novelty, coverage, serendipity) capture different aspects of recommendation quality and are documented inline. Users with no relevant items are skipped from each mean so they don't drag the score toward zero.

precision_at_k

precision_at_k(predicted: Sequence[Sequence[Hashable]], actual: Sequence[Collection[Hashable]], k: int) -> float

Macro-averaged precision@k across users.

Parameters:

Name Type Description Default
predicted Sequence[Sequence[Hashable]]

For each user, an ordered iterable of recommended item ids (most relevant first).

required
actual Sequence[Collection[Hashable]]

For each user, the items considered relevant in the holdout.

required
k int

Cutoff; only the first k recommendations per user are considered.

required

Returns:

Type Description
float

Mean of hits / k across users with at least one relevant item; 0.0 if no such users exist.

recall_at_k

recall_at_k(predicted: Sequence[Sequence[Hashable]], actual: Sequence[Collection[Hashable]], k: int) -> float

Macro-averaged recall@k across users.

Parameters:

Name Type Description Default
predicted Sequence[Sequence[Hashable]]

Per-user ranked predictions.

required
actual Sequence[Collection[Hashable]]

Per-user relevant items.

required
k int

Cutoff applied to the predicted lists.

required

Returns:

Type Description
float

Mean of hits / |relevant| across users with at least one relevant item; 0.0 if no such users exist.

mean_average_precision

mean_average_precision(predicted: Sequence[Sequence[Hashable]], actual: Sequence[Collection[Hashable]], k: int) -> float

Mean Average Precision at k.

For each user with at least one relevant item, AP@k = (1 / min(|relevant|, k)) * sum_{i=1..k} rel(i) * precision_at_i, where rel(i) is 1 if the i-th recommendation is relevant. The min(|relevant|, k) denominator keeps the per-user score in [0, 1] when |relevant| exceeds the cutoff.

ndcg_at_k

ndcg_at_k(predicted: Sequence[Sequence[Hashable]], actual: Sequence[Collection[Hashable]], k: int) -> float

Normalized Discounted Cumulative Gain at k with binary relevance.

DCG = sum_{i=1..k} rel(i) / log2(i + 1); IDCG is the best possible DCG for the user (all relevant items ranked first). Per-user NDCG is DCG / IDCG (or 0 when no relevant items exist), averaged across users with relevant items.

intra_list_diversity

intra_list_diversity(predicted: Sequence[Sequence[Hashable]], similarity: Callable[[Hashable, Hashable], float], k: int) -> float

Macro-averaged intra-list dissimilarity across users.

For each user, dissimilarity averages 1 - similarity(a, b) over all distinct pairs in the top-k recommendations. Users with fewer than two recommendations are skipped — diversity is undefined for singletons.

Parameters:

Name Type Description Default
predicted Sequence[Sequence[Hashable]]

Per-user ranked predictions.

required
similarity Callable[[Hashable, Hashable], float]

Symmetric similarity in [0, 1]. Only off-diagonal pairs are evaluated.

required
k int

Cutoff for the top of each prediction list.

required

novelty

novelty(predicted: Sequence[Sequence[Hashable]], item_popularity: Mapping[Hashable, float], k: int) -> float

Mean self-information of recommended items, averaged across users.

Self-information of item i is -log2(p_i); popular items contribute little novelty, rare ones contribute more. Items absent from item_popularity are skipped within a user's list; users whose entire top-k is unknown are skipped from the mean.

Parameters:

Name Type Description Default
predicted Sequence[Sequence[Hashable]]

Per-user ranked predictions.

required
item_popularity Mapping[Hashable, float]

Mapping from item id to its popularity in (0, 1] (e.g., the fraction of users that have interacted with it).

required
k int

Cutoff for the top of each prediction list.

required

catalog_coverage

catalog_coverage(predicted: Sequence[Sequence[Hashable]], catalog: Collection[Hashable], k: int) -> float

Fraction of the catalog that appears in at least one user's top-k.

Items recommended outside catalog are ignored; recommending the same item to many users still only counts it once.

Parameters:

Name Type Description Default
predicted Sequence[Sequence[Hashable]]

Per-user ranked predictions.

required
catalog Collection[Hashable]

Items the recommender could have recommended.

required
k int

Cutoff applied to each user's prediction list.

required

serendipity_at_k

serendipity_at_k(predicted: Sequence[Sequence[Hashable]], actual: Sequence[Collection[Hashable]], expected: Sequence[Collection[Hashable]], k: int) -> float

Macro-averaged share of top-k that are both relevant and unexpected.

A recommendation counts as serendipitous when the user finds it relevant (item in actual) and a baseline recommender would not have surfaced it (item not in expected). The expected list per user is typically drawn from a popularity or otherwise trivial recommender. Users with no relevant items are skipped from the mean.

Parameters:

Name Type Description Default
predicted Sequence[Sequence[Hashable]]

Per-user ranked predictions.

required
actual Sequence[Collection[Hashable]]

Per-user relevant items.

required
expected Sequence[Collection[Hashable]]

Per-user baseline recommendations; items already inside this set are not serendipitous even if relevant.

required
k int

Cutoff applied to each user's prediction list.

required

Persistence

recommender_systems.persistence

Save and load fitted recommenders.

save

save(model: Recommender, path: str | Path) -> None

Persist a fitted recommender to path using pickle.

load

load(path: str | Path) -> Recommender

Load a recommender previously saved with :func:save.

Only load files you trust: unpickling executes arbitrary code.

Raises:

Type Description
ValueError

If path does not contain a valid pickle stream.

TypeError

If the loaded object is not a :class:Recommender.

Command-line interface

recommender_systems.cli

Command-line interface for the recommender_systems library.

Installed as the recsys console script via the project's [project.scripts] entry point. Subcommands::

recsys recommend --algo item-knn --user 42 --n 10
recsys evaluate  --algo svd
recsys list-algos

recommend and evaluate train on MovieLens 100k (downloaded and cached on first use). --version prints the installed package version.