stream.iter_arff now casts sparse numeric values to floats and uses the first declared category for omitted nominal targets. Explicitly missing targets now return None with either string or list target arguments.
Removed support for pandas<=2.x. River now requires pandas>=3.1.
In OneHotEncoder, missing values are not dropped anymore when drop_first=True, and the value that is dropped is now stable across observations.
Fixed the radius of cluster.DenStream micro-clusters, which used the norm of the vector of squared sums instead of the sum of its components and was 0 away from the origin. The docstring example now uses epsilon=1.0.
cluster.DenStream.predict_one now expands a cluster past the direct neighbors of its first micro-cluster. The neighbors of a neighbor were only queued if they already had a label, so a chain of micro-clusters came out as several clusters.
New cluster.DenStream micro-clusters no longer reuse the key of a deleted one, which overwrote an existing micro-cluster.
The river.drift sub-package is now clean under strict mypy, and the river.drift.* entry was removed from the non-strict overrides in pyproject.toml. Public signatures and docstrings are unchanged.
Fixed a latent bug in drift.datasets.base.Dataset._annotations_aggregated, which read a nonexistent self._annotations attribute and keyed the intersection branch on the integer 0 instead of the annotator key. The method has no callers today, so behavior is unchanged.
DriftRetrainingClassifier now passes a bool instead of an int as the error indicator to its wrapped binary drift detector.
Added CategoricalNB, a Naive Bayes classifier for categorical features that maintains per-class frequencies for every value of every feature, with additive (Laplace) smoothing and support for both online and mini-batch modes.
MultinomialNB.learn_many, predict_many, and predict_proba_many now accept any narwhals-supported eager backend (pandas, polars, pyarrow, ...) instead of being pandas-only, preserving the input backend (including the pandas index) on output. A backend-agnostic BaseNB.predict_many was added so the argmax-over-probabilities logic is shared by all Naive Bayes variants.
BayesianLinearRegression.predict_dist_one now replaces BayesianLinearRegression.predict_one(..., with_dist=True) to better respect the Regressor interface while still being able to predict distributions.
sketch.Counter.__matmul__ (often written as Counter @ Counter) now returns a Python integer instead of a NumPy integer. You may need to remove existing NumPy-to-Python conversions (.item()) to make it work again.
sketch.Histogram now consistently has integer counts, even after merging two histograms. It previously could convert values to floating-point after a merge operation.
Compression inference now recognizes .gz and .zip extensions regardless of case.
ZIP input must contain exactly one file. Empty archives and archives with multiple files now raise ValueError.
stream.iter_arff now accepts empty sparse rows and whitespace around sparse entries, and skips blank and comment lines in sparse data. Omitted features remain omitted.
stream.iter_csv now preserves extra fields and fills missing columns correctly.
stream.Cache now writes a pass to a temporary file and renames it into place once the stream is exhausted. An interrupted first pass (a break, an exception, an abandoned generator) used to leave a truncated file behind, which every later pass then read back as if it were the whole dataset.
stream.iter_csv no longer yields an empty x for a blank line in the middle of a file. csv.DictReader skips those, and stream.iter_arff already did.
stream.iter_csv now closes the file it opened and restores csv.field_size_limit even when the stream is not exhausted, e.g. when the caller breaks out of the loop. Only the file iter_csv opened itself is closed; a buffer passed in by the caller is still left open.
stream.iter_sql now closes the result it iterates, so the underlying cursor is released once the stream is exhausted or abandoned.
stream.cache, stream.iter_csv, and stream.iter_sql are now clean under strict mypy. sqlalchemy is type-checked rather than ignored, so the query and conn arguments of stream.iter_sql are checked against the SQLAlchemy 2.0 types.
stream.simulate_qa is now clean under strict mypy. Its overloads type the yields: (i, x, y) for a dataset of (x, y) pairs, with the kwargs riding along for (x, y, kwargs) triples. Type checkers now also accept datasets keyed by plain str.
Added a test suite for stream.iter_array that runs every case on both NumPy arrays and Python lists.
Fixed the stream.iter_array bugs reported in #2046. Python lists now work with shuffle=True and as multi-output targets, str enums, None and dicts are yielded as single targets, NumPy texts are yielded as str instead of np.str_, and an empty X yields an empty stream. X and y of different lengths now raise a ValueError instead of being padded with None, and so do rows of X that are scalars or dicts, e.g. a 1D numeric array (use X.reshape(-1, 1)). A list of dicts used to be accepted, with each dict's keys yielded as feature values.
stream.iter_array is now clean under strict mypy. Its annotations accept Python lists: X is a NumPy array or a list of rows (strings or collections), y a NumPy array or a list, and feature_names and target_names any sequence.
stream.iter_frame now converts nullable pandas values to native Python scalars and converts missing values to None. Large integers keep their precision.
stream.iter_sklearn_dataset now supports pandas-backed scikit-learn datasets. It preserves DataFrame column names and supports multi-output targets and reproducible shuffling.
stream.iter_sklearn_dataset is now always exported from river.stream, even when scikit-learn is not installed, and importing river.stream no longer imports scikit-learn.
Refactored LASTClassifier to reuse a single tree-level split criterion for class-distribution change detection, reducing per-leaf state without changing classifier behaviour.
Fixed underestimated memory usage in trees using ExhaustiveSplitter, which could let models exceed their configured max_size without deactivating leaves.
Fixed seasonal initialization in time_series.HoltWinters, which incorrectly used ratios for additive seasonality, flattening the seasonal pattern. Additive mode now uses differences (y - level), while multiplicative mode retains ratios (y / level).