# `DriftStream`

### *class* capymoa.stream.drift.DriftStream[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L126)

Bases: [`Stream`](capymoa.stream.Stream.md#capymoa.stream.Stream)

A stream composed of concepts separated by drifts.

The stream is defined as a list that alternates concepts and drifts,
starting and ending with a concept. There are **two forms**, and a
definition must use one or the other throughout.

**Positions** – each drift says where it happens:

```pycon
>>> from capymoa.stream.drift import DriftStream, AbruptDrift, GradualDrift
>>> from capymoa.stream.generator import SEA
>>> stream = DriftStream(stream=[
...     SEA(function=1),
...     AbruptDrift(position=5000),
...     SEA(function=3),
...     GradualDrift(position=10000, width=2000),
...     SEA(function=1),
... ])
>>> stream.get_num_drifts()
2
```

**Ranges** – each concept says how long it lasts, and the drifts are
placed by what surrounds them. Every concept is wrapped in
[`Concept`](capymoa.stream.drift.Concept.md#capymoa.stream.drift.Concept), drifts carry no position, and a [`GradualDrift`](capymoa.stream.drift.GradualDrift.md#capymoa.stream.drift.GradualDrift)
gives its length instead of a width:

```pycon
>>> from capymoa.stream.drift import Concept
>>> stream = DriftStream(stream=[
...     Concept(SEA(function=1), num_instances=5000),
...     AbruptDrift(),
...     Concept(SEA(function=3), num_instances=5000),
...     GradualDrift(num_instances=2000),
...     Concept(SEA(function=1), num_instances=1000),
... ])
>>> for drift in stream.get_drifts():
...     print(drift)
AbruptDrift(position=5000)
GradualDrift(position=11000, start=10000, end=12000, width=2000)
```

The two cannot be combined. A range definition does not say where its
drifts land, so a position supplied alongside it would describe a location
the rest of the definition contradicts. Mixing them is rejected rather than
resolved by guesswork.

Use [`describe()`](#capymoa.stream.drift.DriftStream.describe) to see how many instances each concept actually
contributed, which around a [`GradualDrift`](capymoa.stream.drift.GradualDrift.md#capymoa.stream.drift.GradualDrift) is not what the declared
lengths suggest.

Composition happens in Python: instances are drawn from the concept
selected for the current position, and across a [`GradualDrift`](capymoa.stream.drift.GradualDrift.md#capymoa.stream.drift.GradualDrift) the
choice is a per-instance draw against the transition ramp. Earlier versions
delegated this to MOA’s `ConceptDriftStream`, which meant every concept
had to be a MOA-backed stream.

`DriftStream` is therefore a plain [`Stream`](capymoa.stream.Stream.md#capymoa.stream.Stream) rather
than a [`MOAStream`](capymoa.stream.MOAStream.md#capymoa.stream.MOAStream). Where a MOA object is genuinely
needed – to hand the stream to MOA code, or to print a MOA CLI – use
[`to_moa_stream()`](#capymoa.stream.drift.DriftStream.to_moa_stream), which builds the equivalent nested
`ConceptDriftStream`. That conversion needs every concept to be
MOA-backed, so it is offered explicitly instead of being the implementation.

#### \_\_init_\_(schema=None, CLI=None, moa_stream=None, stream=None)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L188)

Initialize the stream.

* **Parameters:**
  * **schema** – The schema of the stream. Taken from the first concept
    when not given.
  * **CLI** – Command Line Interface string describing a MOA
    `ConceptDriftStream`. Kept for backward compatibility; the stream
    is then backed by MOA rather than composed in Python.
  * **moa_stream** – A pre-configured `ConceptDriftStream` from MOA,
    used together with `CLI`.
  * **stream** – The list of concepts and drifts to compose, alternating
    and starting with a concept.

#### \_\_iter_\_() → [Self](https://docs.python.org/3/library/typing.html#typing.Self)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/_stream.py#L356)

Get an iterator over the stream.

This will NOT restart the stream if it has already been iterated over.
Please use the [`restart()`](#capymoa.stream.drift.DriftStream.restart) method to restart the stream.

* **Yield:**
  An iterator over the stream.

#### \_\_next_\_() → \_AnyInstance[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/_stream.py#L366)

Get the next instance in the stream.

* **Returns:**
  The next instance in the stream.

#### cli_help() → [str](https://docs.python.org/3/builtins/stdtypes.html#str)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/_stream.py#L379)

Return a help message

#### describe(horizon: [int](https://docs.python.org/3/builtins/functions.html#int) | [None](https://docs.python.org/3/builtins/constants.html#None) = None) → [str](https://docs.python.org/3/builtins/stdtypes.html#str)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L411)

A readable summary of where the first `num_instances` come from.

Intended for reporting a stream in a paper or notebook, where the drift
positions alone do not say how much of each concept was actually seen.

* **Parameters:**
  **horizon** – How many instances to account for. Defaults to the
  length the definition implies, which the report labels as exact or
  estimated.
* **Returns:**
  A table of concepts, their counts and shares, followed by the
  drifts. When the stream was defined with lengths, the declared
  length is shown alongside for comparison.

#### get_concept_counts(horizon: [int](https://docs.python.org/3/builtins/functions.html#int) | [None](https://docs.python.org/3/builtins/constants.html#None) = None)[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L313)

How many of the first `num_instances` come from each concept.

Around an [`AbruptDrift`](capymoa.stream.drift.AbruptDrift.md#capymoa.stream.drift.AbruptDrift) this follows from the definition, but
around a [`GradualDrift`](capymoa.stream.drift.GradualDrift.md#capymoa.stream.drift.GradualDrift) the concepts overlap and the split is a
property of the transition ramp, which can keep drawing from the older
concept past the nominal end of the window. This reports what actually
happens.

The stream does not need to be run. Each transition decides using only
its own seeded generator and its own counter – the instances never
enter the decision – so the routing is replayed with fresh generators
and reproduces the same branch pattern without generating any data.
That makes it exact rather than an estimate, and fast enough to ask
about millions of instances.

```pycon
>>> from capymoa.stream.drift import Concept, DriftStream, GradualDrift
>>> from capymoa.stream.generator import SEA
>>> stream = DriftStream(stream=[
...     Concept(SEA(function=1), num_instances=1000),
...     GradualDrift(num_instances=500),
...     Concept(SEA(function=3), num_instances=500),
... ])
>>> stream.get_concept_counts(2000)
[1251, 749]
```

* **Parameters:**
  **horizon** – How many instances to account for, counted from the
  start of the stream. Defaults to the length the definition implies
  – exact for the range form, estimated for the position form, whose
  final concept is open-ended.
* **Returns:**
  One count per concept, in the order they were defined.
  Concepts appearing more than once are counted separately.

#### get_drifts()[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L727)

#### get_moa_stream()[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L612)

Return the backing MOA stream, if there is one.

A Python-composed `DriftStream` has no single MOA object behind it.
Use [`to_moa_stream()`](#capymoa.stream.drift.DriftStream.to_moa_stream) to build one.

#### get_num_drifts()[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L724)

#### get_schema()[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L602)

Return the schema of the stream.

#### has_more_instances()[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L597)

Return `True` if the stream have more instances to read.

#### next_instance()[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L593)

Return the next instance in the stream.

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If the machine learning task is neither a regression
  nor a classification task.
* **Returns:**
  A labeled instances or a regression depending on the schema.

#### restart()[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L605)

Restart the stream to read instances from the beginning.

#### to_moa_stream()[[source]](https://github.com/adaptive-machine-learning/CapyMOA/blob/3e255b1/src/capymoa/stream/drift.py#L634)

Build the equivalent MOA `ConceptDriftStream`.

Provided for interoperability with MOA code and for inspecting the
generated CLI. Requires every concept to be MOA-backed, which is the
restriction Python composition exists to remove – so this raises when
the stream contains a concept MOA cannot represent.

* **Returns:**
  A [`MOAStream`](capymoa.stream.MOAStream.md#capymoa.stream.MOAStream) wrapping the nested
  `ConceptDriftStream`.

#### *property* length

How many instances the stream produces, if that is known.

A range definition states its own length – every concept and gradual
drift contributes its declared number of instances – and the stream
ends there. A position definition has an open-ended final concept, so
this is `None`.

#### *property* moa_stream

The backing MOA stream object, or `None` when composed in Python.

Kept so a `DriftStream` built from a MOA CLI still works with code
that reaches for the attribute directly, such as the optimised
evaluation loops.
