spec
spec ¤
Signal semantics: what a uuid's values mean.
Every analysis on industrial time series silently depends on a few facts about each signal:
- kind -- is it a continuous measurement, a cumulative counter, a state (run/idle, recipe, alarm), or a sparse event stream?
- encoding -- is it sampled periodically, or change-encoded (one row per
change, the
is_deltaconvention)? - timing -- its nominal sampling period and the gap after which the signal must be treated as "no data" rather than "value held".
Getting any of these wrong produces silently wrong results: counters that
lose production at a reset, intervals that collapse to zero length on
change-encoded data, averages biased by sampling density, durations that
bridge logging outages. :class:SignalSpec records these facts once per
uuid so that the primitives in :mod:ts_shape.signals.primitives (and the
detectors built on them) get them right by construction.
SignalSpec
dataclass
¤
SignalSpec(
uuid: str,
kind: Kind = "continuous",
encoding: Encoding = "sampled",
value_column: str = "value_double",
unit: str | None = None,
nominal_period: Timedelta | None = None,
max_gap: Timedelta | None = None,
tz: str | None = None,
reset_policy: Literal[
"reset_to_zero", "none"
] = "reset_to_zero",
notes: dict[str, str] = dict(),
)
Semantics of one signal (one uuid).
Attributes:
| Name | Type | Description |
|---|---|---|
uuid |
str
|
The signal identifier. |
kind |
Kind
|
|
encoding |
Encoding
|
|
value_column |
str
|
The |
unit |
str | None
|
Engineering unit, informational (e.g. |
nominal_period |
Timedelta | None
|
Typical time between samples. |
max_gap |
Timedelta | None
|
Longest interval across which a value may be held. Longer
spans are "no data". |
tz |
str | None
|
Timezone name of |
reset_policy |
Literal['reset_to_zero', 'none']
|
For counters: |
notes |
dict[str, str]
|
Free-form provenance, e.g. why a field was inferred. |
Example
from ts_shape import SignalSpec spec = SignalSpec("line1:parts", kind="counter", ... value_column="value_integer", unit="pcs") spec.kind, spec.reset_policy ('counter', 'reset_to_zero') spec.with_(unit="parts").unit 'parts' SignalSpec.from_dict(spec.to_dict()) == spec True
is_change_encoded
property
¤
is_change_encoded: bool
Whether rows are emitted only when the value changes.
holds_value
property
¤
holds_value: bool
Whether a value persists until the next row (state / counter / level).
interval_end
property
¤
interval_end: str
interval_end mode for :func:ts_shape.utils.intervals.state_intervals.
A held value lasts until the next change, for both encodings: on sampled data closing at the last sample undercounts every interval by one period, on change-encoded data it collapses them to zero.
with_ ¤
with_(**changes: Any) -> SignalSpec
Return a copy with changes applied (validated).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**changes
|
Any
|
New field values. |
{}
|
Returns:
| Type | Description |
|---|---|
SignalSpec
|
The new spec. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For unknown field names or invalid values. |
from_dict
classmethod
¤
from_dict(data: Mapping[str, Any]) -> SignalSpec
Build a spec from a plain mapping of field names to values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Mapping[str, Any]
|
Mapping of |
required |
Returns:
| Type | Description |
|---|---|
SignalSpec
|
The validated SignalSpec. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
SignalSpecs ¤
SignalSpecs(specs: Mapping[str, SignalSpec] | None = None)
Bases: Mapping[str, SignalSpec]
A read-only collection of :class:SignalSpec, keyed by uuid.
Build it with :func:ts_shape.signals.infer_specs, then override what the
data cannot tell::
specs = infer_specs(df).override({
"line1:parts": {"kind": "counter", "unit": "pcs"},
"line1:state": {"max_gap": "15min"},
})
override ¤
override(
changes: Mapping[str, Mapping[str, Any]],
) -> SignalSpecs
Return new specs with per-uuid field overrides applied.
A uuid not yet present is added with the given fields.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
changes
|
Mapping[str, Mapping[str, Any]]
|
Mapping of uuid to |
required |
Returns:
| Name | Type | Description |
|---|---|---|
New |
SignalSpecs
|
class: |
Raises:
| Type | Description |
|---|---|
ValueError
|
For unknown field names or invalid values. |
to_dict ¤
to_dict() -> dict[str, dict[str, Any]]
Serialize to {uuid: {field: value}} (the YAML/JSON layout).
The uuid key is omitted from each inner mapping and notes are
excluded.
Returns:
| Type | Description |
|---|---|
dict[str, dict[str, Any]]
|
Mapping of uuid to that signal's spec fields. |
from_dict
classmethod
¤
from_dict(
data: Mapping[str, Mapping[str, Any]],
) -> SignalSpecs
Build from {uuid: {field: value}} (the YAML/JSON layout).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Mapping[str, Mapping[str, Any]]
|
Mapping of uuid to |
required |
from_yaml
classmethod
¤
from_yaml(path: str | Path) -> SignalSpecs
Load {uuid: {field: value}} from a YAML file (needs PyYAML).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Path to a YAML file with the |
required |