File: README.md
โก๏ธ Quickstart
As a quick example, we'll train a logistic regression to classify the website phishing dataset. Here's a look at the first observation in the dataset.
>>> from pprint import pprint
>>> from river import datasets
>>> dataset = datasets.Phishing()
>>> for x, y in dataset:
... pprint(x)
... print(y)
... break
{'age_of_domain': 1,
'anchor_from_other_domain': 0.0,
'empty_server_form_handler': 0.0,
'https': 0.0,
'ip_in_url': 1,
'is_popular': 0.5,
'long_url': 1.0,
'popup_window': 0.0,
'request_from_other_domain': 0.0}
TrueNow let's run the model on the dataset in a streaming fashion. We sequentially interleave predictions and model updates. Meanwhile, we update a performance metric to see how well the model is doing.
>>> from river import compose
>>> from river import linear_model
>>> from river import metrics
>>> from river import preprocessing
>>> model = compose.Pipeline(
... preprocessing.StandardScaler(),
... linear_model.LogisticRegression()
... )
>>> metric = metrics.Accuracy()
>>> for x, y in dataset:
... y_pred = model.predict_one(x) # make a prediction
... metric.update(y, y_pred) # update the metric
... model.learn_one(x, y) # make the model learn
>>> metric
Accuracy: 89.28%Of course, this is just a contrived example. We welcome you to check the introduction section of the documentation for a more thorough tutorial.
๐ Installation
River is intended to work with Python 3.11 and above. Installation can be done with pip:
pip install riverThere are wheels available for Linux, MacOS, and Windows. This means you most probably won't have to build River from source.
River's core online interface (learn_one / predict_one) has no pandas dependency. The mini-batch interface (learn_many, predict_many, predict_proba_many, transform_many) is built on pandas and is opt-in:
pip install "river[pandas]"You can install the latest development version from GitHub as so:
pip install git+https://github.com/online-ml/river --upgrade
pip install git+ssh://[email protected]/online-ml/river.git --upgrade # using SSHThis method requires having Cython and Rust installed on your machine.
๐ฎ Features
River provides online implementations of the following family of algorithms:
- Linear models, with a wide array of optimizers
- Decision trees and random forests
- (Approximate) nearest neighbors
- Anomaly detection
- Drift detection
- Recommender systems
- Time series forecasting
- Bandits
- Factorization machines
- Imbalanced learning
- Clustering
- Bagging/boosting/stacking
- Active learning
River also provides other online utilities:
- Feature extraction and selection
- Online statistics and metrics
- Preprocessing
- Built-in datasets
- Progressive model validation
- Model pipelines
Check out the API for a comprehensive overview
๐ค Should I be using River?
You should ask yourself if you need online machine learning. The answer is likely no. Most of the time batch learning does the job just fine. An online approach might fit the bill if:
- You want a model that can learn from new data without having to revisit past data.
- You want a model which is robust to concept drift.
- You want to develop your model in a way that is closer to what occurs in a production context, which is usually event-based.
Some specificities of River are that:
- It focuses on clarity and user experience, more so than performance.
- It's very fast at processing one sample at a time. Try it, you'll see.
- It plays nicely with the rest of Python's ecosystem.
๐ Useful links
- Documentation
- Package releases
- awesome-online-machine-learning
- 2022 presentation at GAIA
- Online Clustering: Algorithms, Evaluation, Metrics, Applications and Benchmarking from KDD'22.
๐ Contributing
Feel free to contribute in any way you like, we're always open to new ideas and approaches.
- Open a discussion if you have any question or enquiry whatsoever. It's more useful to ask your question in public rather than sending us a private email. It's also encouraged to open a discussion before contributing, so that everyone is aligned and unnecessary work is avoided.
- Feel welcome to open an issue if you think you've spotted a bug or a performance issue.
- Our roadmap is public. Feel free to work on anything that catches your eye, or to make suggestions.
Please check out the contribution guidelines if you want to bring modifications to the code base.
๐ค Affiliations
๐ฌ Citation
If River has been useful to you, and you would like to cite it in a scientific publication, please refer to the paper published at JMLR:
@article{montiel2021river,
title={River: machine learning for streaming data in Python},
author={Montiel, Jacob and Halford, Max and Mastelini, Saulo Martiello
and Bolmier, Geoffrey and Sourty, Raphael and Vaysse, Robin and Zouitine, Adil
and Gomes, Heitor Murilo and Read, Jesse and Abdessalem, Talel and others},
year={2021}
}๐ License
River is free and open-source software licensed under the 3-clause BSD license.
File: benchmarks/README.md
River Benchmarks
River benchmarks use airspeed velocity (asv) to track the timing performance of models across git history.
Each benchmark runs progressive validation (evaluate.iter_progressive_val_score) for a model on a dataset, measuring wall-clock time. Results are tracked per commit so that performance regressions can be detected automatically.
Prerequisites
pip install asv virtualenvRunning benchmarks locally
All commands should be run from the benchmarks/ directory.
Quick run (current Python, no environment setup)
asv run --python=same --quick --show-stderrRun a specific benchmark class
asv run --python=same --quick --bench BinaryClassification
asv run --python=same --quick --bench RegressionRun a specific model/dataset combination
asv run --python=same --quick --bench "BinaryClassification.*Logistic.*Bananas"Compare two branches
asv continuous main my-feature-branch --bench BinaryClassificationThis runs benchmarks on both branches and highlights significant timing changes.
Find which commit caused a regression
asv find v0.22.0..main --bench "Regression.time_progressive_val_score\(Linear Regression, ChickWeights\)"Viewing results
asv publish
asv previewThis generates a static website and serves it locally. Open the printed URL in your browser.
Adding a new model
- Edit
benchmarks/common.py - Add the model name to the appropriate
*_MODELSlist (e.g.,BINARY_CLF_MODELS) - Add a factory lambda to the corresponding
_*_MODEL_REGISTRYdict - Run
asv checkto verify discovery
Adding a new track
- Create
benchmarks/bench_.pyfollowing the pattern in existing files - Add the model/dataset lists and registries to
benchmarks/common.py - Run
asv checkto verify discovery
CI
Benchmarks run automatically on push to main via GitHub Actions. Results are stored on the asv branch and published to GitHub Pages.
CodSpeed
CodSpeed runs the small pytest benchmark suite in benchmarks/codspeed/python/ and the
Rust criterion benchmarks in benchmarks/codspeed/rust/ on every pull request and every
push to main. Both jobs use CodSpeed's CPU simulation mode, which produces deterministic
instruction-count measurements and flamegraphs. Pull requests get a CodSpeed comment plus
status checks for the Python benchmarks, Rust benchmarks, and the aggregate performance
analysis.
To keep CI fast, the Python job is split into two parallel shards (see the matrix in.github/workflows/codspeed.yml): a "heavy" shard listing the most expensive files
explicitly, and a "rest" shard running everything else. New benchmark files land in the
"rest" shard automatically; if the two shards drift far apart in duration, move files
between them. Sharding does not affect benchmark identity or the CodSpeed report โ all
uploads for a commit are aggregated. Keep each benchmark body around 100 ms of real time
or less: the CPU simulator runs it once at roughly 30โ100ร overhead, so oversized
workloads slow every PR without adding signal.
Run CodSpeed benchmarks locally
make benchmark # all Python benchmarks, local walltime table
make benchmark K=logistic # filter by pytest -k expression
make benchmark-rust # all Rust criterion benches
make benchmark-rust BENCH=stats_benchLocal Python runs are smoke tests, not the merge-gating measurement. Outside CodSpeed CI,pytest-codspeed falls back to a normal walltime table, so results depend on your
machine. The PR comment is the source of truth for comparisons. On Linux, power users can
run simulation locally with the CodSpeed CLI:
curl -fsSL https://codspeed.io/install.sh | bash
codspeed auth login
codspeed run -m simulation -- uv run --no-sync pytest benchmarks/codspeed --codspeed -o addopts=""Add a CodSpeed benchmark
Copy this template into benchmarks/codspeed/python/test_<module>.py and keep the benchmark
name stable after it lands; renaming a benchmark loses its CodSpeed history.
from __future__ import annotations
import pytest
from river import <module>
from workloads import binary_stream # or regression_stream, scalar_series, ...
pytestmark = pytest.mark.benchmark(group="<module>")
def test_<estimator>_learn(benchmark) -> None:
stream = binary_stream()
def run() -> None:
model = <module>.<Estimator>(seed=42)
for x, y in stream:
model.learn_one(x, y)
benchmark(run)Rust benchmarks live in benchmarks/codspeed/rust/. Add each new Rust bench target toCargo.toml explicitly:
[[bench]]
name = "<module>_bench"
path = "benchmarks/codspeed/rust/<module>_bench.rs"
harness = falseUse deterministic workloads only:
- Seed every generator and every stochastic estimator.
- Do not use network access or read the clock inside a benchmark.
- Materialize data in
workloads.py, outside the measured callable. - Treat cached workload lists as frozen; do not mutate shared data.
- Build a fresh estimator inside each measured learning callable.
Reading CodSpeed results
The PR comment links to each benchmark, its comparison against main, and the
differential flamegraph. A red CodSpeed performance status means at least one benchmark
regressed beyond the configured threshold. The default threshold is 10%; noisy benchmarks
can get a per-benchmark threshold in CodSpeed instead of loosening the global one.
Intentional regressions should be explained in the PR description and acknowledged by an
admin in the CodSpeed UI. Do not bypass GitHub branch protection for performance
regressions; acknowledgement keeps the dashboard and branch status consistent.
File: docs/faq/index.md
hide:
- navigation
Frequently Asked Questions
Do all classifiers support multi-class classification?
No, they don't. Although binary classification can be seen as a special case of multi-class classification, there are many optimizations that can be performed if we know that there are only two classes. It would be annoying to have to check whether this is the case in an online setting. All in all we find that separating both cases leads to much cleaner code. Note that the multiclass module contains wrapper models that enable you to perform multi-class classification with binary classifiers.
How do I know if a classifier supports multi-class classification?
Each classifier in River inherits from the base.Classifier class. Each classifier therefore has a _multiclass property which indicates whether or not it can process a non-boolean target value.
>>> from river import linear_model
>>> classifier = linear_model.LogisticRegression()
>>> classifier._multiclass
FalseWhy doesn't river do any input validation?
Python encourages a coding style called EAFP, which stands for "Easier to Ask for Forgiveness than Permission". The idea is to assume that runtime errors don't occur, and instead use try/expects to catch errors. The great benefit is that we don't have to drown our code with if statements, which is symptomatic of the LBYL style, which stands for "look before you leap". This makes our implementations much more readable than, say, scikit-learn, which does a lot of input validation. The catch is that users have to be careful to use sane inputs. As always, there is no free lunch!
What about reinforcement learning?
Reinforcement learning works in an online manner because of the nature of the task. Reinforcement learning can be therefore be seen as a subcase of online machine learning. However, we prefer not to support it because there are already many existing opensource libraries dedicated to it.
What are the differences between scikit-learn's online learning algorithm which have a partial_fit method and their equivalents in River?
The algorithms from sklearn that support incremental learning are mostly meant for mini-batch learning. In a pure streaming context where the observations arrive one by one, then River is much faster than sklearn. This is mostly because sklearn incurs a lot of overhead by performing data checks. Also, sklearn assumes that you're always using the same number of features. This is not the case with River because it use dictionaries which allows you to drop and add features as you wish.
How do I save and load models?
>>> from river import ensemble
>>> import pickle
>>> model = ensemble.ARFClassifier()
# save
>>> with open('model.pkl', 'wb') as f:
... pickle.dump(model, f)
# load
>>> with open('model.pkl', 'rb') as f:
... model = pickle.load(f)We also encourage you to try out dill and cloudpickle.
What about neural networks?
There are many great open-source libraries for building neural network models. We don't feel that we can bring anything of value to the existing Python ecosystem. However, we are open to implementing compatibility wrappers for popular libraries such as PyTorch and Keras.
Who are the authors of this library?
We are research engineers, graduate students, PhDs and machine learning researchers. The members of the development team are mainly located in France, Brazil and New Zealand.
File: docs/introduction/basic-concepts.md
Basic concepts
Here are some concepts to give you a feel for what problems River addresses.
Data streams
River is a library to build online machine learning models. Such models operate on data streams. But a data stream is a bit of a vague concept.
In general, a data stream is a sequence of individual elements. In the case of machine learning, each element is a bunch of features. We call these samples, or observations. Each sample might follow a fixed structure and always contain the same features. But features can also appear and disappear over time. That depends on the use case.
Reactive and proactive data streams
The origin of a data stream can vary, and usually it doesn't matter. You should be able to use River regardless of where your data comes from. It is however important to keep in mind the difference between reactive and proactive data streams.
Reactive data streams are ones where the data comes to you. For instance, when a user visits your website, that's out of your control. You have no influence on the event. It just happens and you have to react to it.
Proactive data streams are ones where you have control on the data stream. For example, you might be reading the data from a file. You decide at which speed you want to read the data, in what order, etc.
If you consider data analysis as a whole, you realize that the general approach is to turn reactive streams into proactive datasets. Events are usually logged into a database and are processed offline. Be it for building KPIs or training models.
The challenge for machine learning is to ensure models you train offline on proactive datasets will perform correctly in production on reactive data streams.
Online processing
Online processing is the act of processing a data stream one element at a time. In the case of machine learning, that means training a model by teaching it one sample at a time. This is completely opposite to the traditional way of doing machine learning, which is to train a model on whole batches of data at a time.
An online model is therefore a stateful, dynamic object. It keeps learning and doesn't have to revisit past data. It's a different way of doing things, and therefore has its own set of pros and cons.
Tasks
Machine learning encompasses many different tasks: classification, regression, anomaly detection, time series forecasting, etc. The ideology behind River is to be a generic machine learning approach which allows these tasks to be performed in a streaming manner. Indeed, many batch machine learning algorithms have online equivalents.
Note that River also supports some more basic tasks. For instance, you might just want to calculate a running average of a data stream. These are usually smaller parts of a whole stream processing pipeline.
Dictionaries everywhere
River is a Python library. It is composed of a bunch of classes which implement various online processing algorithms. Most of these classes are machine learning models which can process a single sample, be it for learning or for inference.
We made the choice to use dictionaries as the basic building block. First of all, online processing is different to batch processing, in that vectorization doesn't bring any speed-up. Therefore numeric processing libraries such as NumPy and PyTorch actually bring too much overhead. Using native Python data structures is faster.
Dictionaries are therefore a perfect fit. They're native to Python and have excellent support in the standard library. They allow the naming of each feature. They can hold any kind of data type. They allow transparent support of JSON payloads, allowing seamless integration with web apps.
Datasets
In production, you're almost always going to face data streams which you have to react to, such as users visiting your website. The advantage of online machine learning is that you can design models that make predictions as well as learn from this data stream as it flows.
But of course, when you're developing a model, you don't usually have access to a real-time feed on which to evaluate your model. You usually have an offline dataset which you want to evaluate your model on. River provides some datasets which can be read in online manner, one sample at a time. It is however crucial to keep in mind that the goal is to reproduce a production scenario as closely as possible, in order to ensure your model will perform just as well in production.
Model evaluation
Online model evaluation differs from its traditional batch counterpart. In the latter, you usually perform cross-validation, whereby your training dataset is split into a learning and an evaluation dataset. This is fine, but it doesn't exactly reflect the data generation process that occurs in production.
Online model evaluation involves learning and inference in the same order as what would happen in production. Indeed, if you know the order in which your data arrives, then you can process it the exact same order. This allows you to replay a production scenario and evaluate your model with higher fidelity than cross-validation.
This is what makes online machine learning powerful. By replaying datasets in the correct order, you ensure you are designing models which will perform as expected in production.
Concept drift
The main reason why an offline model might not perform as expected in production is because of concept drift. But this is true for all machine learning models, be they offline or online.
The advantage of online models over offline models is that they can cope with drift. Indeed, because they can keep learning, they usually adapt to concept drift in a seamless manner. As opposed to batch models which have to be retrained from scratch.
File: docs/introduction/installation.md
Installation
River is meant to work with Python 3.11 and above. Installation can be done via pip:
pip install riverAnd of course with uv:
uv add riverYou can install the latest development version from GitHub, as so:
pip install git+https://github.com/online-ml/river --upgrade
pip install git+ssh://[email protected]/online-ml/river.git --upgrade # using SSHThis method requires having Cython and Rust installed on your machine.
Mini-batch support (optional `pandas` extra)
River's core online interface (learn_one / predict_one) does not require pandas. The mini-batch interface (learn_many, predict_many, predict_proba_many, transform_many) is built on top of pandas.DataFrame and pandas.Series, so pandas is an opt-in dependency.
To install River together with pandas:
pip install "river[pandas]"
# or
uv add "river[pandas]"If you call a mini-batch method without pandas installed, River raises an ImportError pointing you to this extra.
Feel welcome to open an issue on GitHub if you are having any trouble.
File: docs/introduction/next-steps.md
Next steps
The Recipes section is made up of small tutorials. Each one explains how to perform mundane tasks, such as measuring the performance of a model, selecting hyperparameters, etc.
The Examples section contains more involved notebooks with less explanations. Each notebook addresses a particular machine learning problem.
The API section references all the modules, classes, and functions in River. It is automatically generated from the codebase's Python docstrings.
Feel welcome to open a discussion if you have a question. Before that you can check out the FAQ, which has answers to recurring questions.
The released versions are listed in the Releases section. Changes that will be part of the next release are listed in the unreleased section of the documentation's development version, which you may find here.
We recommend checking out Awesome Online Machine Learning if you want to go deeper. There you will find online machine learning related content: research papers, alternative and complementary software, blog posts, etc.
File: docs/introduction/related-projects.md
Related projects
Here is a list of projects which are more or less coupled with River:
- deep-river interfaces PyTorch models with River.
- river-extra regroups experimental features which have yet to prove themselves to make it into the main River repository. Between us we call this "the arena".