Object Detection: batches, serials, coils, recipes…¤
The event packs answer "what happened?". This layer answers "to which things?" —
the batch, product, material, coil, serial, bundle, customer part-number, family,
drum, tool, and recipe objects that events refer to. It is one generic, declarative
layer, not another family of detectors: you describe which signals carry which
object type, and the same run-length kernel that powers batch/traceability detection
(SegmentExtractor) extracts every object's presence
interval into the OCEL 2.0 objects / o2o / object_changes
tables. New object types are added by data/config — never by writing code.
flowchart LR
SIG["id signals<br/><i>value_string per uuid</i>"]
OI["object_intervals()<br/><i>run-length segmentation</i>"]
DO["detect_objects()<br/><i>objects + o2o + changes</i>"]
AO["attach_objects()<br/><i>link events by time</i>"]
LOG["EventLog<br/><i>OCEL 2.0</i>"]
SIG --> OI --> DO --> LOG
OI --> AO --> LOG
style DO fill:#0f2a3d,stroke:#38bdf8,color:#e0f2fe
style AO fill:#3d2a0f,stroke:#fbbf24,color:#fef3c7
Declare the objects¤
An ObjectSpec maps one signal to one object type. The object_type is
auto-registered if it is not one of the standard 16, so coil, drum, bundle,
family, customer_partnumber, … work out of the box.
from ts_shape.eventlog import ObjectSpec, detect_objects
specs = [
ObjectSpec("sig:batch", "batch", id_template="{type}:{value}"),
ObjectSpec("sig:serial", "serial", id_template="{type}:{value}"),
ObjectSpec("sig:coil", "coil", id_template="{type}:{value}"), # auto-registered
]
| Field | Purpose |
|---|---|
uuid |
The signal whose value column carries the object id. |
object_type |
OCEL type; auto-registered if unknown. |
value_column |
value_string (default) or value_integer. |
min_duration |
Drop presence blips shorter than this. |
id_template |
Render the oid — "{value}" or "{type}:{value}" to namespace. |
attributes |
{attr_name: signal_uuid} captured into object_changes. |
At scale, skip the hand-written list entirely — tag object types in your signal metadata and derive the specs:
from ts_shape.eventlog import object_specs_from_metadata
specs = object_specs_from_metadata(metadata_loader.to_df()) # reads `object_type` tags
Detect objects and their relations¤
log = detect_objects(df, specs, hierarchy={"serial": "batch", "batch": "work_order"})
log.objects # one row per (oid, type)
log.o2o # serial part_of batch (declared hierarchy), else co_occurs
log.object_changes # presence lifecycle (active/released) + captured attributes
part_of is declared, not guessed
A compositional part_of relation cannot be inferred from time alone — a
long-running tool or shift temporally contains every batch without owning
it. So ts-shape asserts part_of (child → parent) only along a declared
hierarchy (child type → parent type), when the child's presence overlaps
the parent's. Without a hierarchy, overlapping objects of different types get
the honest, symmetric co_occurs qualifier. Pass infer_o2o=False to skip
object-to-object relations entirely.
An id is treated as present from when it appears until the next id on the same signal — so sparse sampling does not leave gaps that orphan events.
Attach objects to any event log¤
This is the end-to-end win. attach_objects links the output of any event
detector to the detected objects by temporal containment — every event gets E2O
relations to whatever batch / serial / coil was active at its timestamp, with zero
per-detector changes.
from ts_shape.eventlog import attach_objects, to_event_log
ev_log = to_event_log(intervals, detector="MachineStateEvents.detect_run_idle")
enriched = attach_objects(
ev_log, df, specs,
qualifiers={"batch": "during_batch", "serial": "identified_by", "coil": "made_of"},
)
# enriched.relations now links each run/idle event to its batch, serial and coil.
Because detect_objects returns a normal EventLog, you can also
concat an objects-only log with any event log instead of attaching by
time. Either way the result exports through to_event_log_ocel /
to_event_log_xes like any other log.
A full runnable walkthrough is in
examples/object_detection_demo.py.
See also¤
- Event Log overview — the five OCEL 2.0 tables.
- Labelling standard & taxonomy — the object-type vocabulary.