DriftStream#

class capymoa.stream.drift.DriftStream[source]#

Bases: 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:

>>> 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, drifts carry no position, and a GradualDrift gives its length instead of a width:

>>> 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() to see how many instances each concept actually contributed, which around a 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 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 rather than a 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(), 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]#

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__() Iterator[_AnyInstance][source]#

Get an iterator over the stream.

This will NOT restart the stream if it has already been iterated over. Please use the restart() method to restart the stream.

Yield:

An iterator over the stream.

__next__() _AnyInstance[source]#

Get the next instance in the stream.

Returns:

The next instance in the stream.

cli_help() str[source]#

Return a help message

describe(horizon: int = None) str[source]#

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 = None)[source]#

How many of the first num_instances come from each concept.

Around an AbruptDrift this follows from the definition, but around a 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.

>>> 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]#
get_moa_stream()[source]#

Return the backing MOA stream, if there is one.

A Python-composed DriftStream has no single MOA object behind it. Use to_moa_stream() to build one.

get_num_drifts()[source]#
get_schema()[source]#

Return the schema of the stream.

has_more_instances()[source]#

Return True if the stream have more instances to read.

next_instance()[source]#

Return the next instance in the stream.

Raises:

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]#

Restart the stream to read instances from the beginning.

to_moa_stream()[source]#

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 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.