Model or dataset
sb-ai-lab/RePlay avatar
sb-ai-lab/RePlay

RePlay: A Python Framework for Offline Recommender Pipelines, From Polars Preprocessing to PySpark Models

A Comprehensive Framework for Building End-to-End Recommendation Systems with State-of-the-Art Models

412 stars42 forksPythonApache-2.0

At a glance

What is it?
RePlay is an Apache-2.0 Python package that covers data splitting, feature schemas, model training, ensembling and metric evaluation for recommender systems. Its core is Polars-based, its heavier models run on PySpark, and its experimental submodule ships separately.
Who is it for?
Adopt RePlay if your team already runs PySpark or is willing to, needs a single pipeline from splitting to NDCG and HitRate, and can pin an exact version. Do not adopt it if you need a lightweight NumPy-only recommender or a managed serving layer: RePlay stops at offline experiment.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 15 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap RePlay fills between a notebook baseline and a production recsys

Most recommender projects start the same way: a pandas dataframe, a train/test split by timestamp, and a matrix factorization library that has its own opinion about input format. The moment you want to compare two models on the same split with the same metric definition, the glue code starts to dominate. RePlay's stated purpose is to cover that whole lifecycle in one package: preprocessing and splitting, a catalogue of models from baselines to transformer-based sequence models, hyperparameter optimization, evaluation metrics, and ensembling into two-level models. The audience is an engineer or applied researcher who has to produce a ranked list of items per user and defend the number. The README frames it as a path from offline experimentation to online production, though the material supplied describes offline tooling only. There is no serving component, no feature store, and no A/B harness in what is documented here. Treat the online transition claim as an aspiration rather than a shipped capability until you find the specific module that does it.

FeatureSchema and DatasetLabelEncoder: the data contract at the centre of RePlay

The mechanism that makes the rest of the framework composable is the feature schema. You declare each column as a FeatureInfo with a FeatureType (CATEGORICAL or NUMERICAL) and a FeatureHint, such as FeatureHint.QUERY_ID, FeatureHint.ITEM_ID, FeatureHint.RATING or FeatureHint.TIMESTAMP. That declaration is what lets a splitter know which column identifies a user, what lets a model know which column is the target, and what lets the metrics module know which column holds the relevance value. The Dataset object then wraps a feature schema plus the interactions themselves. Before a model sees the data, DatasetLabelEncoder.fit_transform maps raw ids to contiguous integer indices, and the same encoder is used at inference time to map the predictions back through query_and_item_id_encoder.inverse_transform. That round trip is the part people get wrong when they hand-roll a pipeline, and putting it in one object is the strongest design decision visible in the README. The encoder is fit on train and applied to test, which is why the quickstart calls fit_transform on the training dataset and plain transform on the test dataset.

Polars in, PySpark out: the two-engine split in the quickstart

RePlay's quickstart does not use pandas for the interaction data. It imports from_pandas from Polars and converts the MovieLens 1m ratings into a Polars dataframe before splitting. The README describes the preprocessing as polars-based and fast, and links a notebook comparing SASRec dataframe performance, but no timing figure is given in the material, so the speed claim is unverified here. The split itself is a RatioSplitter configured with test_size=0.3, divide_column and query_column both set to user_id, plus item_column, timestamp_column, drop_cold_items=True and drop_cold_users=True. Those two drop flags matter more than they look: they silently remove test rows whose user or item never appeared in train, which changes the effective test set size and makes comparisons against pipelines that do not drop cold entities invalid. After splitting, the datasets are pushed into Spark with train_dataset.to_spark(), and the model runs on the Spark session obtained from State().session. So the data path is Polars for preparation, Spark for modelling. That is a deliberate split, and it means the Spark dependency is not optional for the Spark-backed models even if your data fits in memory.

Installing RePlay: extras, the rc0 suffix, and the CPU-only torch index

The base install is pip install replay-rec, which the README says gives you the core package without PySpark or PyTorch, and without the experimental submodule. To get Spark support, install replay-rec[spark]; for PyTorch and Lightning, replay-rec[torch]; the quickstart uses pip install replay-rec[all]. The experimental submodule is versioned separately: you install it by requesting a version with the rc0 suffix, for example pip install replay-rec==XX.YY.ZZrc0, and that suffix can be combined with extras, as in pip install replay-rec[spark]==XX.YY.ZZrc0. If you want PyTorch without CUDA, the README gives pip install replay-rec[torch] --extra-index-url https://download.pytorch.org/whl/cpu. The README also notes that RePlay has optional features requiring optional dependencies installed manually, but the supplied text cuts off before listing them, so check the installation page for that list. Building from source is documented in CONTRIBUTING.md rather than in the README. The practical consequence of the rc0 scheme is that experimental code is not on the default install path, so anything you build against it is pinned to an exact version string.

Where RePlay gets in the way: the Spark session, cold-entity dropping and version pinning

The most concrete limitation is the Spark session. State().session is a global, and the quickstart reaches for it before doing anything else. That is fine in a single-process script and awkward inside a service that already manages its own Spark context, or in a test suite that wants isolation between cases. The second limitation is the cold-entity behaviour: drop_cold_items and drop_cold_users are flags you must consciously set, and their effect on reported metrics is large enough that two teams using RePlay with different settings can produce numbers that look comparable and are not. Third, the release cadence is fast, with v0.21.6, v0.21.7 and v0.21.8 landing between late March and mid May 2026, and the experimental submodule gated behind an rc0 suffix. If your deployment pins dependencies loosely, an upgrade can move you onto a different patch of the experimental line. Finally, the framework is offline: the README's feature list promises transition to online production, but nothing in the supplied material describes a serving API, a latency budget or a model export format. If you need to put a model behind an endpoint, RePlay is the wrong tool for that half of the job.

Implicit versus matrix factorization libraries: what RePlay changes

The obvious comparison is with a single-purpose matrix factorization library, and the difference is architectural rather than algorithmic. A library in that family typically hands you one model class that consumes a sparse user-item matrix and returns factors, and you own everything around it: the split, the id mapping, the metric implementation, the negative sampling. ItemKNN in RePlay is one class among a catalogue that the README says spans baselines and state-of-the-art models, and it consumes a Dataset rather than a matrix, which is why the same fit and predict calls work when you swap in a neural model later. The cost of that uniformity is the schema and encoder ceremony in the quickstart: roughly thirty lines before the model is constructed. Whether that trade is worth it depends on whether you intend to compare more than one model. If you have exactly one model in mind and no plan to benchmark it, the schema layer is overhead. If you have three candidates and a deadline, the shared split and shared Experiment object are the reason to use RePlay at all.

Evaluation and ensembling in RePlay, and what the metrics actually measure

Evaluation runs through Experiment, which is constructed with a list of metric objects, here NDCG(K) and HitRate(K) with K=10, plus the test dataframe and the column names for query, item and rating. Results are registered per model with metrics.add_result("ItemKNN", recs) and read back from metrics.results. That structure is what makes the ensembling feature usable: because every model's predictions are registered against the same test set with the same metric instances, a two-level model can be scored on identical footing without re-plumbing the evaluation. The rating column is passed to Experiment, which suggests graded relevance is supported alongside binary hits, though the README does not spell out how NDCG treats a rating when the recs dataframe already carries scores. Note also that the quickstart evaluates against the raw test dataframe while the recommendations have been inverse-transformed back to original ids, so the encoder round trip is not just a convenience for inspection, it is required for the metric call to line up. Get that ordering wrong and the experiment will silently score against mismatched ids.

Licence, maintenance and what to check before you depend on it

RePlay is Apache-2.0, which permits commercial use and modification and includes a patent grant, but it also carries attribution and notice requirements for redistributed code. That is a summary of the identifier, not legal advice; if you are embedding RePlay in a product, have counsel read the LICENSE file rather than this paragraph. On maintenance, the repository is not archived, the last push recorded is 2026-08-31, and releases have been frequent through 2026, which points to active development rather than a frozen project. The upgrade cost is real but bounded: the extras and the rc0 suffix mean your dependency line is explicit, and the parts you are most likely to build on, FeatureSchema, Dataset, DatasetLabelEncoder and Experiment, are the parts the quickstart exercises. The parts most likely to move are the experimental models behind rc0. Before you standardise on RePlay, run the quickstart end to end against your own data with drop_cold_items and drop_cold_users set the way you intend to report, confirm that replay-rec[spark] or replay-rec[torch] resolves on your Python version, and write down the exact version string you installed.

Editorial conclusion

Adopt RePlay if your team already runs PySpark or is willing to, needs a single pipeline from splitting to NDCG and HitRate, and can pin an exact version. Do not adopt it if you need a lightweight NumPy-only recommender or a managed serving layer: RePlay stops at offline experiment. Before committing, verify that the extras you need install cleanly on your Python and CUDA combination, and check the release history for whether the experimental submodule is currently packaged.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. sb-ai-lab/RePlay on GitHub
Community notes

Community notes