Annotations¶
An annotation is side-information attached to a record. Every annotation has two parts.
The scope says which signals and which point or window in time the annotation refers to. The
free-text content can be as short as a tag or as long as a paragraph of reasoning. One
Annotation class covers every case. The optional span says how the annotation sits in time.
A span is a TimePoint or a TimeInterval. Both read as microseconds on the source recording
timeline. The step frame (StepPoint and StepInterval, counted in a series' own ordinals) is for
tasks on an ordinal series. Annotations do not use it. The Annotation class is a
keyword-only frozen dataclass with key, value, unit and description fields. A connector
authors it directly, or subclasses it with field defaults for reuse.
Record-wide facts¶
An Annotation with no span has no time reference. It describes the whole recording. It requires a
value. Record-level facts live here: a subject's attributes, the machine id, its firmware version,
an operating mode, or a single condition label. A record has no metadata field, so this is where such
context belongs. The fact travels with every window drawn later from the record.
from timenet.types import Annotation
Annotation(key="age", value=54, unit="years")
Annotation(key="sex", value="F")
Annotation(key="condition", value="healthy")
Annotation(key="operating_hours", value=1200, unit="hours")
One time offset¶
An Annotation whose span is a TimePoint marks one time offset on one or more signals. This
shape fits discrete events: a shock, a valve actuation, a detected spike. Points are cheap to store.
Detectors often produce them in bulk. Points then serve as anchors for downstream windowing. To
target specific signals, pass time_series_ids. To target the whole record, leave it None. A
TimePoint reads its offset as microseconds on the source recording timeline. seconds() converts
from recording seconds for you.
from timenet.types import Annotation, TimePoint
Annotation(key="impact", span=TimePoint.seconds(4.2, time_series_ids=("vibration",)))
A bounded window¶
An Annotation whose span is a TimeInterval covers a start-to-end window on one or more
signals. This span is the most common one. The half-open range [start, end) must have an end that
exceeds its start. The full scoping grammar lives here: which signals by which time range. The
value and free-text description can carry the full reading of what happens in that window.
from timenet.types import Annotation, TimeInterval
Annotation(
key="fault",
value="bearing fault",
span=TimeInterval.seconds(5.0, 8.0, time_series_ids=("vibration",)),
)
Across signals¶
Annotations are not tied to one signal. A single event can span a vibration sensor, a temperature probe, and a current sensor together. The shared timing indicates one physical process, not a per-signal artifact.
A record holds a list of annotations. Several spans can sit on one signal. Windows can overlap or nest. All three ways of sitting in time can coexist on one signal. The text field is free-form, so an annotation can carry a multi-sentence reading rather than a label. This reading lets the annotation become a reasoning target. That is the bridge to tasks.