Skip to content

Dataset

timenet.dataset source

The in-memory TimeF model a connector populates during convert().

TimeAxis module-attribute source

Every shape a series' time axis can have.

AxisType source

Bases: StrEnum

Name which shape a series' time axis has.

TimeF stores this value and dispatches on it before it reads any shape-specific column. TimeF never infers the case from which columns came back null.

IRREGULAR class-attribute instance-attribute source

IRREGULAR = 'irregular'

One stored time offset per value.

ORDINAL class-attribute instance-attribute source

ORDINAL = 'ordinal'

Order only: no cadence, no time offsets, no place on any timeline.

REGULAR class-attribute instance-attribute source

REGULAR = 'regular'

Time offsets computed from a period and an origin.

IrregularAxis dataclass source

A placement no formula produces, so this axis writes down every time offset beside the values.

This axis holds only the pair a builder can state and the writer can verify without a read. That pair is the first and the last stored time offset. The time offsets themselves ride the values plane. Reach them through :attr:~timenet.dataset.TimeSeries.time_offsets_us.

That split is the point. Two ints compare and hash, so the axis goes whole into the writer's series identity and round-trips as a value through the records struct. An axis holding the array does neither: a tuple comparison against an ndarray field raises instead of answering.

The endpoints are metadata about the stream, not an identity for it. Two series whose time offsets differ only in the middle carry equal axes. Nothing can use axis equality to conclude the time offsets agree. Compare the streams instead.

This axis has no time_offset_us and no index_at_or_after on purpose. Both need a read, and a read is a series-level operation. :class:OrdinalAxis sets the precedent: an axis that cannot answer in constant time does not offer the method.

axis_type class-attribute source

The stored discriminator.

first_us instance-attribute source

first_us: int

Time offset of the first value, in microseconds from the record's relative zero.

last_us instance-attribute source

last_us: int

Time offset of the last value. The writer checks it against the stream at write time, so it is verified metadata, not an unbacked claim.

spanning classmethod source

spanning(time_offsets_us: ndarray | Sequence[int]) -> Self

Build the axis describing a stream of time offsets.

Parameters:

Name Type Description Default
time_offsets_us ndarray | Sequence[int]

The per-value time offsets, in microseconds from the record's relative zero.

required

Returns:

Type Description
Self

The axis carrying that stream's endpoints, after :func:to_time_offsets_us has vetted it.

OrdinalAxis dataclass source

An Axis to indicate an order without a cadence, time offsets, or place on any timeline.

axis_type class-attribute source

axis_type: AxisType = AxisType.ORDINAL

The stored discriminator.

Record dataclass source

One logical unit of time-series data: a recording, a session, a sensor bundle, a market window.

Created via :meth:~timenet.dataset.TimeFDataset.add_record. Mutable so task_ids and annotations can be populated after construction.

A record has no metadata field. Record-level facts, such as a subject's age or the recording device, are :class:~timenet.types.Annotation objects with no span. Such an annotation can also state a unit, and it travels with every window drawn later from the record.

annotations class-attribute instance-attribute source

annotations: tuple[Annotation, ...] = ()

Annotations attached to the record.

has_absolute_time property source

has_absolute_time: bool

Whether this record's relative timeline has a Unix-time anchor.

record_id class-attribute instance-attribute source

record_id: str = field(default_factory=new_id)

Unique id for the record (default: an auto-generated uuid7).

start_time class-attribute instance-attribute source

start_time: datetime | int | None = None

Wall-clock timestamp that this record's relative time zero refers to. It applies to every series and annotation on the record. Pass a timezone-aware :class:~datetime.datetime or whole Unix microseconds. Construction normalizes either one to microseconds, so a constructed record holds an int. None means no wall-clock reference exists. Never fabricate one.

A bare float is refused, because seconds and microseconds are both plausible readings of it. If the source hands over seconds, convert at the call site so the unit is visible::

start_time=datetime(2026, 8, 5, tzinfo=timezone.utc)   # 1_785_888_000_000_000
start_time=seconds_to_us(1)                            # 1_000_000, one second past the epoch
start_time=1_000_000                                   # the same moment, written directly

subject_ids class-attribute instance-attribute source

subject_ids: tuple[str, ...] = ()

Subjects this record belongs to (empty for subject-less domains).

task_ids class-attribute instance-attribute source

task_ids: tuple[str, ...] = ()

Ids of the tasks attached to this record.

time_series instance-attribute source

time_series: tuple[TimeSeries, ...]

The logical :class:TimeSeries streams the record uses.

time_span class-attribute instance-attribute source

time_span: TimeInterval | None = None

The session's overall span on the source recording timeline: an :class:~timenet.types.TimeInterval covering the whole record, or None. Declare it when the series have gaps and an event may fall in one, for example a note taken while every sensor was briefly off. This checks an unscoped span against it, rather than against the union of the series' windows. Its time_series_ids must be None, and it must contain every series' window.

add_annotation source

add_annotation(
    annotation: Annotation,
    *,
    warn_when_outside: bool = True,
) -> Annotation

Attach an annotation to the record and return it.

Parameters:

Name Type Description Default
annotation Annotation

The annotation to attach.

required
warn_when_outside bool

Warn and keep the span when it leaves its window, rather than raise.

True

Returns:

Type Description
Annotation

The attached annotation (the same instance).

Raises:

Type Description
TimeFValidationError

If the annotation's span references a series not on this record. If a scoped span names a timeless series. If the span falls outside the window its scope selects: the intersection of named series, the record's time_span, or the union of the series' windows and warn_when_outside is False.

add_annotations source

add_annotations(
    annotations: Iterable[Annotation],
    *,
    warn_when_outside: bool = True,
) -> tuple[Annotation, ...]

Attach several annotations to the record, all together or not at all.

The whole batch is validated before any of it is attached: if one annotation fails a check, the call raises and leaves the record unchanged. To keep the annotations before a failure attached, loop :meth:add_annotation instead.

Parameters:

Name Type Description Default
annotations Iterable[Annotation]

The annotations to attach. Pass a single one to :meth:add_annotation.

required
warn_when_outside bool

As on :meth:add_annotation.

True

Returns:

Type Description
tuple[Annotation, ...]

The attached annotations (the same instances), in the order given.

Raises:

Type Description
TimeFValidationError

as documented on :meth:add_annotation.

time_interval source

time_interval(
    start: datetime,
    end: datetime,
    *,
    time_series_ids: tuple[str, ...] | None = None,
) -> TimeInterval

Build a :class:~timenet.types.TimeInterval between two wall-clock moments on this timeline.

Places start and end on the recording timeline against this record's own start_time. offset_us raises if the record has no start_time to measure against.

Parameters:

Name Type Description Default
start datetime

Wall-clock start, timezone-aware.

required
end datetime

Wall-clock end, exclusive and timezone-aware.

required
time_series_ids tuple[str, ...] | None

Series the interval is scoped to. None covers every series.

None

Returns:

Type Description
TimeInterval

The half-open interval, in microseconds from this record's relative zero.

time_point source

time_point(
    at: datetime,
    *,
    time_series_ids: tuple[str, ...] | None = None,
) -> TimePoint

Build a :class:~timenet.types.TimePoint at a wall-clock moment on this record's timeline.

Places at on the recording timeline against this record's own start_time, so the caller never repeats the anchor. A span's bounds are offsets on that timeline, so this needs an anchored record. offset_us raises if the record has no start_time.

Parameters:

Name Type Description Default
at datetime

The wall-clock moment, timezone-aware.

required
time_series_ids tuple[str, ...] | None

Series the point is scoped to. None covers every series.

None

Returns:

Type Description
TimePoint

The point, in microseconds from this record's relative zero.

to_arrow source

to_arrow() -> Array

Read the sole signal's values as an Arrow array, for the common single-signal record.

Returns:

Type Description
Array

The single :class:TimeSeries' values as a 1-D Arrow array.

Raises:

Type Description
ValueError

If the record has more than one signal, read time_series[i] explicitly then.

to_numpy source

to_numpy() -> ndarray

Read the sole signal's values as a NumPy array (materializes :meth:to_arrow).

Returns:

Type Description
ndarray

The single :class:TimeSeries' values as a 1-D np.ndarray.

RegularAxis dataclass source

A constant cadence: value k sits at (start_index + k) * period_us microseconds.

The origin is an index into the cadence, not a time, because a window rarely starts on a whole microsecond. At 44.1 kHz only 3 of 1000 window starts do, so a microsecond origin is inexact for almost every window. An index is exact for all of them.

axis_type class-attribute source

axis_type: AxisType = AxisType.REGULAR

The stored discriminator. Each shape declares its own tag, so a new shape must declare one too.

period_us instance-attribute source

period_us: Fraction

Microseconds between values. It is a :class:~fractions.Fraction because not every real rate is a whole number of microseconds: 360 Hz is 25000/9 and 256 Hz is 15625/4. TimeF stores it as its numerator and denominator.

start_index class-attribute instance-attribute source

start_index: int = 0

Index of this series' first value on the cadence. It is non-zero for a window cut from a longer recording, which keeps a span written against the recording meaningful on the window.

at_index source

at_index(index: int) -> Self

Return the axis of a window that starts index values into this one.

This is exact at every rate, because the origin it moves is an index, not a derived time.

Parameters:

Name Type Description Default
index int

How many values into this axis the window starts.

required

Returns:

Type Description
Self

The window's axis, which shares this period.

from_rate_hz classmethod source

from_rate_hz(rate_hz: int | Fraction) -> Self

Build the axis of a regularly sampled series from its exact rate.

This method does not accept a float. Every real sampling rate is a whole number of values per second, so write 500.0 as 500. A rate that is not whole has no single reading: 29.97 fps is 2997/100 by its spelling and 30000/1001 by its intent. Those two drift 3.6 ms apart over an hour. State which one with a :class:~fractions.Fraction, and build the Fraction from a string, not a float. Fraction(29.97) is the binary expansion (1054475631502295/35184372088832), and Fraction("29.97") is 2997/100.

Parameters:

Name Type Description Default
rate_hz int | Fraction

Values per second.

required

Returns:

Type Description
Self

The axis whose period is one sampling period of rate_hz.

Raises:

Type Description
TimeFValidationError

If rate_hz is not a positive integer or Fraction.

index_at_or_after source

index_at_or_after(time_offset_us: int) -> int

Return the first value at or after a time offset.

Parameters:

Name Type Description Default
time_offset_us int

The time offset, in microseconds from the record's relative zero.

required

Returns:

Type Description
int

The index within this series, which is negative if the time offset precedes its first value.

time_offset_us source

time_offset_us(index: int) -> int

Return the time offset of one value, floored to whole microseconds.

This floor pairs with the ceiling in :meth:index_at_or_after. The two are exactly inverse at every period of one microsecond or coarser, including the non-integral ones.

Parameters:

Name Type Description Default
index int

The value's index within this series.

required

Returns:

Type Description
int

Microseconds from the record's relative zero.

TimeFDataset source

Holds records and their tasks as Python objects. It does not do I/O. The writer handles persistence.

has_task_stream property source

has_task_stream: bool

Whether tasks stream from a source (see :meth:set_task_stream) rather than the _tasks list.

metadata property source

metadata: DatasetMetadata

The dataset's descriptive identity.

records property source

records: tuple[Record, ...]

All records in insertion order.

registered_annotations property source

registered_annotations: tuple[Annotation, ...]

Annotations registered for tasks to reference, which no record carries (registration order).

schema property source

schema: DatasetSchema | None

The derived schema, or None until :meth:derive_schema is called.

tasks property source

tasks: tuple[Task, ...]

All tasks in insertion order.

add_record source

add_record(
    *,
    time_series: tuple[TimeSeries, ...],
    subject_ids: tuple[str, ...] = (),
    record_id: str | None = None,
    start_time: datetime | int | None = None,
    time_span: TimeInterval | None = None,
) -> Record

Create a record, register it, and return it.

Parameters:

Name Type Description Default
time_series tuple[TimeSeries, ...]

The logical :class:TimeSeries streams that the record uses.

required
subject_ids tuple[str, ...]

The subjects that this record belongs to. The tuple is empty for domains that have no subjects.

()
record_id str | None

An explicit ID. The default is an automatically generated uuid4 value. Pass an explicit ID for deterministic output, for example for golden test fixtures.

None
start_time datetime | int | None

The wall-clock timestamp for the record's relative zero point. This value can be a timezone-aware datetime or a whole number of Unix microseconds. Use None when no wall-clock reference exists.

None
time_span TimeInterval | None

The overall span of the session. Use this if the series have gaps that an unscoped span can fall into (see :attr:Record.time_span). The value must be a whole-record :class:~timenet.types.TimeInterval object that contains every series window.

None

Returns:

Type Description
Record

The newly created :class:Record.

Raises:

Type Description
TimeFValidationError

This error occurs if time_series is empty, or if two series share the same time_series_id value. The ids must be distinct. The writer uses the ids as shard keys, and :meth:Record.add_annotation and :meth:add_task both resolve references by these ids. So a repeated id silently merges two signals into one.

add_task source

add_task(
    records: Record | Iterable[Record], task: Task
) -> Task

Register a task and link it to its records.

This method checks every span that the task carries against the records given here. This includes the task's scope and, for a :class:~timenet.types.TemporalLocalizationTask, its target regions. The records are available here, so the method checks them at this point. The method also checks the record and annotation ids that the task references, and the source tasks listed in its from_tasks.

Set scope and from_tasks on the task itself. These fields describe that one task, not the call to this method.

Parameters:

Name Type Description Default
records Record | Iterable[Record]

The record, or records, that the task attaches to.

required
task Task

The task instance. The caller must already set its payload, scope, and from_tasks fields.

required

Returns:

Type Description
Task

The registered task. This is the same instance, with the record_ids field

Task

populated.

Raises:

Type Description
TimeFValidationError

This error occurs if records is empty. It also occurs if the task's id is already registered, or if a task in from_tasks is neither registered nor the task itself. It also occurs if a scope-dependent payload rule fails. Examples include a :class:~timenet.types.ForecastingTask target_span with no scope, a frame mismatch, or a context that leaks the target. It also occurs if the task sets both target and target_annotation_ids, or if its answer is not a produced series and it sets neither field. It also occurs if a span's time_series_ids does not resolve to a series on every target record. It also occurs if the span falls outside a record's covered span. It also occurs if a referenced record or annotation is not registered in this dataset.

add_tasks source

add_tasks(
    records: Record | Iterable[Record],
    tasks: Iterable[Task],
) -> tuple[Task, ...]

Register several tasks against the same records, all together or not at all.

The method validates the whole batch before it attaches any task. If one task fails a check, the call raises an error, and the dataset and every task in the batch stay unchanged. To keep the tasks that passed before a failure, call :meth:add_task in a loop instead.

A task can derive from another task in the same batch. To do this, list the parent task in the deriving task's from_tasks field. The batch is checked as a unit, so the order of tasks within tasks does not matter.

Parameters:

Name Type Description Default
records Record | Iterable[Record]

The record, or records, that the tasks attach to.

required
tasks Iterable[Task]

The task instances to register. For a single task, use :meth:add_task instead.

required

Returns:

Type Description
Task

The registered tasks, in the order given. These are the same instances, with the

...

record_ids field populated.

Raises:

Type Description
TimeFValidationError

This error occurs if records is empty. It also occurs if two tasks in the batch share an id, or if one task reuses an id that is already registered. It also occurs if a from_tasks parent is neither registered nor in the batch, or if the derivation forms a cycle. It also occurs if any task fails a check that :meth:add_task documents.

derive_schema source

derive_schema() -> DatasetSchema

Walk the dataset's instances and build its :class:DatasetSchema.

This method collects the distinct spec, annotation, and task types. It stores the result on the dataset, and it returns the result.

Returns:

Type Description
DatasetSchema

The derived :class:DatasetSchema.

Raises:

Type Description
TimeFValidationError

If one spec type or annotation key yields conflicting descriptors across records.

describe source

describe(
    *, rows: int = 5, file: TextIO | None = None
) -> None

Print a plain-text summary of the dataset: its identity, counts, specs and columns, and a record preview.

This method works like pandas' describe and info methods. The preview reads only span metadata, not series values. The method checks value dtypes from one series per spec. This method works even before :meth:derive_schema runs, because it computes everything from the records.

Parameters:

Name Type Description Default
rows int

The number of records to show in the preview.

5
file TextIO | None

Where to write the output. The default is sys.stdout.

None

from_parts classmethod source

from_parts(
    *,
    metadata: DatasetMetadata,
    records: Iterable[Record],
    tasks: Iterable[Task],
    schema: DatasetSchema,
    registered_annotations: Iterable[Annotation] = (),
) -> TimeFDataset

Build a dataset from parts that are already constructed.

The reader uses this method when it reads a dataset back from disk.

Parameters:

Name Type Description Default
metadata DatasetMetadata

The dataset's descriptive identity.

required
records Iterable[Record]

Fully built records. Their loaders pull data from disk.

required
tasks Iterable[Task]

Fully built tasks, with their from_tasks references resolved.

required
schema DatasetSchema

The schema reconstructed from the manifest.

required
registered_annotations Iterable[Annotation]

Annotations that tasks reference but no record carries (see :meth:register_annotations).

()

Returns:

Type Description
TimeFDataset

The dataset, built from these parts.

iter_streamed_tasks_validated source

iter_streamed_tasks_validated() -> Iterator[Task]

Yield the streamed tasks, validating each against the dataset before it is written.

Streamed tasks skip :meth:add_task's checks, so validate each here as it passes through: an undeclared type, an attachment to an unknown record, a dangling reference, a bad answer, or an out-of-window span raises before the task reaches disk. The dataset holds no task list, so the cross-task checks (duplicate ids, from_tasks derivations) that need every task at once do not run for a stream.

Yields:

Type Description
Task

Each validated task, in the source's order.

Raises:

Type Description
TimeFValidationError

If a streamed task fails one of the per-task checks.

iter_tasks source

iter_tasks() -> Iterator[Task]

Yield the dataset's tasks, from the stream when one is set, else the materialized list.

Yields:

Type Description
Task

Each task. A streamed dataset re-reads its source on every call.

register_annotations source

register_annotations(
    annotations: Iterable[Annotation],
) -> None

Register annotations that tasks reference but no record carries.

Deduped by id, so many tasks can share one annotation without copying it. The writer persists these alongside the record annotations, so a task's input_annotation_ids / target_annotation_ids resolve without the annotation being attached to a record. Register an annotation before the task that references it (:meth:add_task checks the reference).

Parameters:

Name Type Description Default
annotations Iterable[Annotation]

The annotations to register. A repeated id must map to an equal annotation.

required

Raises:

Type Description
TimeFValidationError

If two annotations share an id but are not equal.

set_task_stream source

set_task_stream(
    task_types: Sequence[type[Task]],
    source: Callable[[], Iterator[Task]],
) -> None

Provide tasks as a re-iterable stream instead of materializing them in the dataset.

For a dataset with far more tasks than records (many questions over few recordings), holding every task in memory is the scaling wall. A streaming connector builds the bounded records and registered annotations, then hands the tasks over through source; the writer streams them to disk without a list. Streamed tasks are trusted, not validated the way :meth:add_task validates them: each must already have its record_ids set and reference only registered annotations and existing records. Streamed tasks do not populate Record.task_ids.

Parameters:

Name Type Description Default
task_types Sequence[type[Task]]

The task classes the stream yields, so :meth:derive_schema records them. Every yielded task must be one of these types.

required
source Callable[[], Iterator[Task]]

A callable returning a fresh iterator over the tasks each time it is called. The writer calls it more than once (a peek for id storage, then the write), so it must re-read its source rather than exhaust a one-shot generator.

required

Raises:

Type Description
TimeFValidationError

If tasks were already added with :meth:add_task. A dataset either streams its tasks or materializes them, never both, or the writer would drop one set.

tasks_for source

tasks_for(record: Record) -> tuple[Task, ...]
tasks_for(
    record: Record, task_type: type[TTask]
) -> tuple[TTask, ...]
tasks_for(
    record: Record, task_type: type[Task] = Task
) -> tuple[Task, ...]

Return the tasks attached to a record, optionally filtered by type.

This method reverses the stored direction. Tasks reference their records, so this method resolves a record's task_ids back to the task objects.

Parameters:

Name Type Description Default
record Record

The record whose tasks to resolve.

required
task_type type[Task]

Keep only tasks of this subclass. The default keeps every task on the record.

Task

Returns:

Type Description
tuple[Task, ...]

The record's tasks of task_type, in the record's task order.

Raises:

Type Description
TimeFValidationError

This error occurs if record is not registered in this dataset. It also occurs if one of the record's task_ids does not resolve to a registered task that links back to the record.

tasks_of source

tasks_of(task_type: type[TTask]) -> tuple[TTask, ...]

Return every task of a given type, in insertion order.

Parameters:

Name Type Description Default
task_type type[TTask]

The task subclass to keep, for example :class:~timenet.types.ClassificationTask.

required

Returns:

Type Description
tuple[TTask, ...]

The matching tasks.

to_features_and_targets source

to_features_and_targets(
    *,
    task: type[Task] | None = ...,
    output: Literal["arrow"] = ...,
    features: Literal["timestep", "series"] = ...,
) -> tuple[Array, Array]
to_features_and_targets(
    *,
    task: type[Task] | None = ...,
    output: Literal["numpy"],
    features: Literal["timestep", "series"] = ...,
) -> tuple[ndarray, ndarray]
to_features_and_targets(
    *,
    task: type[Task] | None = None,
    output: Literal["arrow", "numpy"] = "arrow",
    features: Literal["timestep", "series"] = "timestep",
) -> tuple[Array, Array] | tuple[ndarray, ndarray]

Build an (X, y) training pair. By default, this method defers materialization.

This method requires every record to carry exactly one task of task. It pairs the values of that task's sole signal with the task's target. The features argument chooses the shape of X:

  • "timestep" (the default): one feature per point, in a rectangular matrix. This needs equal-length records. The result is an Arrow FixedSizeListArray[T], or a NumPy (n, T) array of float32 values.
  • "series": one sequence feature per record, so variable-length series work too. The result is an Arrow ListArray, or a NumPy (n,) object array of 1-D arrays.

With output="arrow" (the default), the method builds these arrays straight from the series loaders, with no NumPy copy in between. With output="numpy", the method materializes them. y is always the targets, as an Arrow string array or a 1-D NumPy array.

Parameters:

Name Type Description Default
task type[Task] | None

The task type to read targets from, for example :class:~timenet.types.ClassificationTask. Omit this argument to infer the type when the dataset has exactly one task type that carries an inline target.

None
output Literal['arrow', 'numpy']

Use "arrow" to keep the deferred Arrow arrays, or "numpy" to materialize them.

'arrow'
features Literal['timestep', 'series']

Use "timestep" for a rectangular per-point matrix, or "series" for one variable-length sequence per record.

'timestep'

Returns:

Type Description
tuple[Array, Array] | tuple[ndarray, ndarray]

(X, y) as two Arrow arrays when output="arrow", or two NumPy arrays when

tuple[Array, Array] | tuple[ndarray, ndarray]

output="numpy".

Raises:

Type Description
TimeFValidationError

This error occurs if output or features is invalid. It also occurs if task is omitted and the dataset has zero or several task types with inline targets. It also occurs if a matched task carries no inline target, for example if its answer is a produced series or is stored as target_annotation_ids. It also occurs if a matched record is not single signal, or if features="timestep" is asked of records that are not all the same length. It also occurs if the dataset has no records, or if any record does not carry exactly one task of task.

TimeSeries dataclass source

Reference to one logical stream of time-series data, with optional windowing and a lazy loader.

The writer dedupes by time_series_id, not by value (eq=False). If you reuse one instance across records, or give two instances the same explicit id, they share one chunk on disk. Consumers read values through :meth:to_arrow or :meth:to_numpy. The connector supplies loader at build, or :class:~timenet.reader.TimeFReader supplies it on read-back.

loader instance-attribute source

loader: Callable[[], Array]

Lazy callable returning the series' values as an Arrow array.

n_values instance-attribute source

n_values: int

The number of values the series holds, one per timestep. If spec gives each timestep a shape, the series counts one value per timestep, not one per scalar. This matches the count a chunk's n_values reports.

signal instance-attribute source

signal: str

Name of this signal within the modality. The signal must be non-empty.

source_id class-attribute instance-attribute source

source_id: str | None = None

Optional identifier of the raw source recording.

span_us property source

span_us: tuple[int, int] | None

The half-open microsecond window this series covers, or None if it has no timeline.

The axis derives this window, so the window and the axis cannot disagree. The dispatch ends in :func:~typing.assert_never, so a new axis shape breaks this method at type-check time.

Returns:

Type Description
tuple[int, int] | None

(first time_offset, one past the last) in microseconds, or None for an ordinal series.

spec instance-attribute source

Measurement-modality contract: type tag, units, dtype, and per-timestep shape.

time_axis instance-attribute source

time_axis: TimeAxis

Where this series' values sit in time. A :class:~timenet.dataset.axis.RegularAxis gives a cadence, an :class:~timenet.dataset.axis.IrregularAxis stores per-value time offsets, and an :class:~timenet.dataset.axis.OrdinalAxis marks a sequence with no time at all.

time_offsets_loader class-attribute instance-attribute source

time_offsets_loader: Callable[[], Array] | None = None

Lazy callable returning one int64 microsecond time offset per value, for an irregular series only.

An :class:~timenet.dataset.axis.IrregularAxis requires this callable, and any other axis rejects it. A regular axis computes its time offsets and an ordinal one has none. A stream attached to either gives a second, conflicting answer to the same question.

time_series_id class-attribute instance-attribute source

time_series_id: str = field(default_factory=new_id)

Stable identity used to dedupe and share chunks. Defaults to a UUIDv7.

from_irregular classmethod source

from_irregular(
    values: ndarray
    | Sequence[bool | int | float | str | None],
    *,
    time_offsets_us: ndarray | Sequence[int],
    spec: TimeSeriesSpec,
    signal: str,
    source_id: str | None = None,
    time_series_id: str | None = None,
) -> TimeSeries

Build an irregular series from materialized values and their time offsets.

The axis endpoints come from the stream itself, so the two cannot disagree. You cannot state a first or last time offset that the time offsets do not have. To convert wall-clock moments, use :func:~timenet.dataset.axis.time_offsets_from_datetimes before you call. Conversion happens once. Repeated reads return the retained Arrow arrays for values and offsets.

Parameters:

Name Type Description Default
values ndarray | Sequence[bool | int | float | str | None]

The signal values to convert to the spec's dtype. Text and enum specs take strings. Python None marks a missing timestep when spec.nullable is true.

required
time_offsets_us ndarray | Sequence[int]

One time offset per value, in microseconds from the record's relative zero.

required
spec TimeSeriesSpec

The series' measurement-modality spec.

required
signal str

The signal name.

required
source_id str | None

Optional id of the raw source recording.

None
time_series_id str | None

Explicit id, or None for an auto-generated UUIDv7.

None

Returns:

Type Description
TimeSeries

The constructed :class:TimeSeries.

Raises:

Type Description
TimeFValidationError

If the time offsets are unusable, or there is not exactly one per value.

from_values classmethod source

from_values(
    values: ndarray
    | Sequence[bool | int | float | str | None],
    *,
    spec: TimeSeriesSpec,
    signal: str,
    time_axis: TimeAxis,
    source_id: str | None = None,
    time_series_id: str | None = None,
) -> TimeSeries

Build a series from already-materialized values and wrap them in a loader for the spec's dtype.

Use this constructor for values already in memory. It converts them once to an Arrow array with the spec's dtype. Repeated reads return that same array. The array length sets n_values.

For files or remote sources, use the loader= constructor and supply the length. That constructor does not read the values immediately.

Parameters:

Name Type Description Default
values ndarray | Sequence[bool | int | float | str | None]

The signal values to convert to the spec's dtype. Text and enum specs take strings. Python None marks a missing timestep when spec.nullable is true.

required
spec TimeSeriesSpec

The series' measurement-modality spec.

required
signal str

The signal name.

required
time_axis TimeAxis

Where the values sit in time.

required
source_id str | None

Optional id of the raw source recording.

None
time_series_id str | None

Explicit id, or None for an auto-generated UUIDv7.

None

Returns:

Type Description
TimeSeries

The constructed :class:TimeSeries.

read_steps source

read_steps(start: int, stop: int) -> Array

Read a half-open temporal step range as Arrow without forcing a NumPy conversion.

Range-aware storage loaders read only the intersecting chunks. A connector loader that implements only the no-argument callable stays compatible through a full-read slice.

Parameters:

Name Type Description Default
start int

First temporal step, inclusive.

required
stop int

Last temporal step, exclusive.

required

Returns:

Type Description
Array

A primitive Arrow array for scalar series or a fixed-shape tensor array for N-D series.

Raises:

Type Description
TimeFValidationError

If the range is negative or reversed.

step_range source

step_range(span: Span) -> tuple[int, int]

Return the half-open step range (start, stop) of this series that span covers.

The bridge to step-based forecasting libraries: stop - start is the horizon h that GluonTS, Nixtla, and fev speak in. The pair feeds :meth:read_steps to read the ground truth. A step span already counts in this series' own steps, so it is the range, bounded by the series' length. The axis locates a time span instead: the steps whose time offsets fall in [start_us, end_us), each rounded up to the next step. An ordinal series has no timeline, so a time span has no answer on it.

Parameters:

Name Type Description Default
span Span

The interval to locate. A step span must name this series.

required

Returns:

Type Description
tuple[int, int]

(start, stop) step indices, half-open, from this series' first step.

Raises:

Type Description
TimeFValidationError

If span is a point. If a step span does not name this series or runs past its length. If an ordinal series receives a time span, or the span resolves past the series' steps. If the located range is empty.

time_offsets_us source

time_offsets_us() -> ndarray

Read this series' per-value time offsets.

Only an irregular series has this. A regular axis computes its time offsets without a read, and an ordinal one has none. So neither has a stream to return.

Returns:

Type Description
ndarray

One int64 microsecond time offset per value.

Raises:

Type Description
TimeFValidationError

If this series' axis is not an :class:~timenet.dataset.axis.IrregularAxis.

to_arrow source

to_arrow() -> Array

Read the series' values as an Arrow array.

Returns:

Type Description
Array

A primitive array for scalar values or a fixed-shape tensor array for N-D values.

to_numpy source

to_numpy() -> Shaped[ndarray, ' time *value']

Read the series' values as a NumPy array.

Returns:

Type Description
Shaped[ndarray, ' time *value']

The series' values with shape (n_steps, *spec.value_shape).

Raises:

Type Description
TimeFValidationError

If the loaded values contain nulls. Use :meth:to_numpy_and_mask or :meth:to_arrow to preserve missingness. A nullable spec without actual nulls remains supported, as do NaN and infinity values.

to_numpy_and_mask source

to_numpy_and_mask() -> tuple[ndarray, ndarray]

Read values and an aligned boolean mask of observed timesteps.

Missing positions contain zero, false, or empty strings. Use the mask to identify missing timesteps. These fill values are not observations. The method keeps numeric and boolean dtypes, even when every timestep is missing.

Returns:

Type Description
tuple[ndarray, ndarray]

Values shaped (n_steps, *spec.value_shape) and validity shaped (n_steps,).