Skip to content

objects

objects ¤

Object detection — turn identifier-bearing timeseries signals into OCEL 2.0 objects, relations, and time-varying attributes.

The event packs answer "what happened?". This module answers "to which things?" — the batches, serials, coils, recipes, tools, materials, drums, bundles, families, customer part-numbers (and any other object out there) that events refer to.

It is deliberately one generic, declarative layer, not another family of detector classes: you describe which signals carry which object type with an :class:ObjectSpec (or discover them from metadata), and a run-length kernel (the same value-change segmentation that powers batch/traceability detection) extracts every object's presence interval. New object types are added by data / config, never by code.

Three steps, all feeding the OCEL 2.0 tables on :class:~ts_shape.eventlog.model.EventLog:

  • :func:object_intervals — segment id signals into (oid, type, start, end) presence intervals (an id is present until the next id on the same signal).
  • :func:detect_objects — build the objects / o2o / object_changes tables. part_of is asserted only along a declared hierarchy (it cannot be inferred from time alone); other overlaps are reported as co_occurs.
  • :func:attach_objects — link an existing event log's events to the detected objects by temporal containment, so the output of every event detector gains rich object references with no per-detector changes.

When you already hold the two lists — an event list and an object list (intervals) — the relationship derivation is automated and needs no raw signals:

  • :func:link_events_to_objects — all event-to-object (E2O) relations, by temporal containment and/or direct key_columns matching.
  • :func:derive_o2o — all object-to-object (O2O) relations (declared part_of hierarchy or symmetric co_occurs).
  • :func:relate — assemble both, plus objects and object_changes, into one validated :class:EventLog ready to ingest into an OCEL 2.0 database.

ObjectSpec dataclass ¤

ObjectSpec(
    uuid: str,
    object_type: str,
    value_column: str = "value_string",
    min_duration: str | None = None,
    id_template: str = "{value}",
    attributes: Mapping[str, str] = dict(),
)

Declares that one signal carries the identity of one object type.

Attributes:

Name Type Description
uuid str

The signal (uuid value) whose value column is the object id.

object_type str

OCEL object type, e.g. "batch", "coil", "serial", "recipe". Auto-registered via :func:~ts_shape.eventlog.schema.register_object_type if unknown.

value_column str

Column carrying the id ("value_string" or "value_integer").

min_duration str | None

Optional minimum presence (e.g. "1min"); shorter blips are dropped.

id_template str

How to render the object id from the raw value. Supports {value} and {type} — default "{value}"; use "{type}:{value}" to namespace ids across types.

attributes Mapping[str, str]

{attribute_name: signal_uuid} — values captured at each presence start (backward merge_asof) and emitted as object_changes.

object_intervals ¤

object_intervals(
    df: DataFrame,
    specs: Sequence[ObjectSpec],
    *,
    uuid_column: str = "uuid",
    time_column: str = "systime"
) -> pd.DataFrame

Extract one presence interval per object instance from id signals.

Returns a tidy frame with columns oid, type, start, end plus one column per captured attribute. Each id signal is run-length encoded into contiguous constant-value segments.

detect_objects ¤

detect_objects(
    df: DataFrame,
    specs: Sequence[ObjectSpec],
    *,
    uuid_column: str = "uuid",
    time_column: str = "systime",
    hierarchy: Mapping[str, str] | None = None,
    infer_o2o: bool = True,
    validate: bool = True
) -> EventLog

Detect object instances and build their OCEL 2.0 tables.

Produces an :class:EventLog with no events — only objects, o2o (object-to-object relations), and object_changes (presence lifecycle + captured attributes). Compose it with event-detector logs via :func:~ts_shape.eventlog.concat.concat.

hierarchy maps a child object type to its parent type (e.g. {"serial": "batch", "batch": "work_order"}). A compositional part_of relation is asserted only along these declared edges, when a child's presence overlaps a parent's — because part_of cannot be inferred from time alone (a long-running tool temporally contains every batch without owning it). Without a declared hierarchy, overlapping objects of different types get the honest, symmetric co_occurs qualifier instead. Set infer_o2o=False to skip object-to-object relations entirely.

attach_objects ¤

attach_objects(
    event_log: EventLog,
    df: DataFrame,
    specs: Sequence[ObjectSpec],
    *,
    qualifiers: Mapping[str, str] | None = None,
    uuid_column: str = "uuid",
    time_column: str = "systime",
    hierarchy: Mapping[str, str] | None = None,
    infer_o2o: bool = True,
    validate: bool = True
) -> EventLog

Link an existing event log to detected objects by temporal containment.

For every event, any object whose presence interval contains the event timestamp is linked with an event-to-object (E2O) relation. The detected objects, o2o and object_changes are merged in. This enriches the output of any event detector — no per-detector changes required. See :func:detect_objects for hierarchy (declared part_of edges).

This is a convenience over :func:relate: it first segments df into an object-interval table via :func:object_intervals, then relates the events to it. When you already hold an interval/object table, call :func:relate directly and skip the raw signals.

relate ¤

relate(
    event_log: EventLog,
    intervals: DataFrame,
    *,
    hierarchy: Mapping[str, str] | None = None,
    qualifiers: Mapping[str, str] | None = None,
    key_columns: Sequence[str] | None = None,
    infer_o2o: bool = True,
    validate: bool = True
) -> EventLog

Assemble a full OCEL 2.0 log from an event list and an object list.

This is the automated relationship step: given an existing event_log (with events) and an intervals table describing objects -- columns oid, type and, for temporal linking, start / end (plus any attribute columns) -- it derives all relations and returns a validated :class:EventLog whose five tables are ready to ingest into an OCEL 2.0 database:

  • objects from the distinct (oid, type) pairs,
  • relations (E2O) via :func:link_events_to_objects (temporal containment, plus optional key_columns matching),
  • o2o via :func:derive_o2o (declared part_of hierarchy or symmetric co_occurs),
  • object_changes (lifecycle + captured attributes).

Unlike :func:attach_objects, no raw signals or :class:ObjectSpec are needed -- you bring the two lists, ts-shape derives the relationships.

derive_o2o ¤

derive_o2o(
    intervals: DataFrame,
    *,
    hierarchy: Mapping[str, str] | None = None
) -> pd.DataFrame

Derive object-to-object (O2O) relations from an interval table.

Public entry point over the overlap kernel: a declared hierarchy (child type -> parent type) yields part_of along overlapping presence intervals; without one, overlapping objects of different types get the symmetric co_occurs qualifier. Returns the OCEL 2.0 o2o table.

object_specs_from_metadata ¤

object_specs_from_metadata(
    metadata: DataFrame,
    *,
    type_field: str = "object_type",
    value_column_field: str = "object_value_column",
    uuid_column: str = "uuid"
) -> list[ObjectSpec]

Build :class:ObjectSpec list from per-signal metadata tags.

Scans a metadata table (e.g. MetadataJsonLoader.to_df()) for a column naming the object type per signal — so new object types are onboarded by tagging metadata, not by writing code. The uuid may be the index or a column; the type/value-column fields are matched directly or with a config_ / config. prefix (mkdocs metadata flattening).

link_events_to_objects(
    events: DataFrame,
    intervals: DataFrame,
    *,
    qualifiers: Mapping[str, str] | None = None,
    key_columns: Sequence[str] | None = None,
    contain: bool = True
) -> pd.DataFrame

Derive all event-to-object (E2O) relations from two lists.

Takes an OCEL events table and an object/interval table (oid, type and, for temporal linking, start / end) and returns the OCEL 2.0 relations table -- with no access to the raw signals. Two complementary strategies are unioned and de-duplicated:

  • temporal containment (contain, default on): link an event to every object whose presence interval [start, end] contains the event timestamp. Requires start / end columns.
  • key match (opt-in via key_columns): link an event to the object whose oid equals the value in one of the event's key_columns -- for events that already name their object (e.g. a batch attribute).

qualifiers maps an object type to the relation qualifier to stamp (e.g. {"batch": "processed_in"}); unmapped types get <NA>.