Skip to content

Index

eventlog ¤

ts-shape canonical event-log package.

A lightweight, pm4py-compatible (XES + OCEL 2.0) representation of detector output. ts-shape itself adds no process-mining dependencies — the columns match the specs verbatim so users can hand the resulting DataFrames to pm4py / Disco / Celonis / OCEL viewers directly.

Typical use::

from ts_shape.eventlog import (
    to_event_log, concat, to_event_log_xes, to_event_log_ocel,
)
from ts_shape.events.production.machine_state import MachineStateEvents
from ts_shape.events.quality.outlier_detection import OutlierDetectionEvents

state_log = to_event_log(
    MachineStateEvents(df, run_state_uuid="m").detect_run_idle(),
    detector="MachineStateEvents.detect_run_idle",
)
outlier_log = to_event_log(
    OutlierDetectionEvents(df, value_column="value_double").detect_outliers_zscore(),
    detector="OutlierDetectionEvents.detect_outliers_zscore",
)
log = concat(state_log, outlier_log)
xes_df = to_event_log_xes(log, case_object_type="asset")
tables = to_event_log_ocel(log)  # OCEL2Tables(events, objects, relations, ...)

LambdaDetector ¤

LambdaDetector(spec: RuleSpec)

Runnable form of a :class:RuleSpec.

evaluate ¤

evaluate(dataframe: DataFrame) -> pd.DataFrame

Apply the rule to dataframe and return a detector frame.

Parameters:

Name Type Description Default
dataframe DataFrame

Input DataFrame the rule's trigger expression is evaluated on.

required

Returns:

Type Description
DataFrame

Detector frame whose layout depends on the rule's shape

DataFrame

(see :func:~ts_shape.eventlog.lambda_rules.evaluator.evaluate).

to_event_log ¤

to_event_log(
    dataframe: DataFrame,
    *,
    objects: Mapping[str, object] | None = None,
    qualifiers: Mapping[str, str] | None = None
) -> EventLog

Evaluate the rule on dataframe and normalize the result to an event log.

Parameters:

Name Type Description Default
dataframe DataFrame

Input DataFrame the rule is evaluated on.

required
objects Mapping[str, object] | None

Optional OCEL object bindings, mapping an object type to a column name, callable, Series or scalar; passed through to :func:~ts_shape.eventlog.to_event_log. None uses only the auto-extracted objects.

None
qualifiers Mapping[str, str] | None

Optional mapping of object type to the relation qualifier written for that type. None leaves qualifiers empty.

None

Returns:

Type Description
EventLog

The canonical :class:~ts_shape.eventlog.model.EventLog.

RuleSpec dataclass ¤

RuleSpec(
    id: str,
    class_name: str,
    method_name: str,
    pack: str,
    shape: str,
    archetype: str,
    template: str,
    trigger: TriggerSpec,
    produces_objects: tuple[str, ...] = ("asset",),
    severity_field: str | None = None,
    value_field: str | None = None,
    standard_attrs: Mapping[str, object] = dict(),
)

A complete lambda-rule definition.

Required fields mirror the built-in REGISTRY contract — class_name is synthesized (must start with Lambda) so the rule slots into the same (class, method) -> LabelRule table as the 290 built-ins.

TriggerSpec dataclass ¤

TriggerSpec(
    expression: str,
    min_duration_s: float | None = None,
    group_by: tuple[str, ...] = (),
)

When does the rule fire?

expression is evaluated against the input DataFrame by the AST-restricted compiler in :mod:.expression. It must produce a boolean Series aligned with the input rows.

min_duration_s and group_by are only meaningful for shape="interval" rules: consecutive True rows are coalesced (per group) and the resulting interval is dropped if shorter than min_duration_s seconds.

UnsafeExpression ¤

Bases: ValueError

Raised when a trigger expression contains a disallowed AST node.

EventLog dataclass ¤

EventLog(
    events: DataFrame = schema.empty_events(),
    objects: DataFrame = schema.empty_objects(),
    relations: DataFrame = schema.empty_relations(),
    o2o: DataFrame = schema.empty_o2o(),
    object_changes: DataFrame = schema.empty_object_changes(),
)

Canonical ts-shape event log in OCEL 2.0 shape.

Five relational tables, mirroring the OCEL 2.0 standard:

  • events — one row per detected event (+ event attributes),
  • objects — the objects events refer to (ocel:oid / ocel:type),
  • relations — event-to-object (E2O) relations with qualifiers,
  • o2o — object-to-object relations with qualifiers,
  • object_changes — time-varying object attribute values.

objects / relations are empty when the source detector has no natural object association (e.g. a global cross-signal correlation statistic); o2o / object_changes are empty unless supplied. Use :attr:has_objects to check before calling :func:to_event_log_xes.

Example

Built by :func:~ts_shape.to_event_log from a detector's output:

import ts_shape df = ts_shape.make_timeseries(["temp"], n_outliers=3) out = ts_shape.OutlierDetectionEvents(df, "value_double").detect_outliers_zscore() log = ts_shape.to_event_log( ... out, detector="OutlierDetectionEvents.detect_outliers_zscore") len(log.events), log.has_objects (4, True) log.events[["ocel:activity", "ocel:timestamp"]].head(2) ocel:activity ocel:timestamp 0 quality.outlier.zscore 2025-01-01 01:34:30+00:00 1 quality.outlier.zscore 2025-01-01 03:21:00+00:00

has_objects property ¤

has_objects: bool

Whether the log contains at least one object in objects.

filter_by_pack ¤

filter_by_pack(pack: str) -> EventLog

Return a sub-log containing only the events emitted by one pack.

Objects, relations, object-to-object links, and object changes are restricted to those referenced by the kept events.

Parameters:

Name Type Description Default
pack str

Pack name to match against the ts_shape:pack column.

required

Returns:

Type Description
EventLog

A new EventLog restricted to the matching events.

filter_by_object ¤

filter_by_object(
    oid: str, type_: str | None = None
) -> EventLog

Return a sub-log containing only the events related to one object.

Events are selected through the event-to-object relations table; the other tables are restricted to the objects those events use.

Parameters:

Name Type Description Default
oid str

Object identifier (ocel:oid) to match.

required
type_ str | None

Optional object type (ocel:type) to match as well.

None

Returns:

Type Description
EventLog

A new EventLog restricted to the events related to the object.

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"). Empty strings and missing values mean "no object present".

min_duration str | None

Optional minimum presence (e.g. "1min", measured start to next value); shorter blips are dropped and the presence around them closes over the gap. The last id on a signal is open-ended and never 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} — the value in effect at each presence start (last non-missing sample at or before it) plus every change of that signal during the presence, emitted as object_changes.

OCEL2Tables dataclass ¤

OCEL2Tables(
    events: DataFrame,
    objects: DataFrame,
    relations: DataFrame,
    o2o: DataFrame,
    object_changes: DataFrame,
)

The five OCEL 2.0 relational tables produced from an :class:EventLog.

LabelRule dataclass ¤

LabelRule(
    template: str,
    pack: str,
    shape: str = "point",
    produces_objects: tuple[str, ...] = ("asset",),
    severity_field: str | None = None,
    value_field: str | None = None,
    drop_fields: tuple[str, ...] = (),
    standard_attrs: Mapping[str, object] = dict(),
)

How one detector method's output maps onto OCEL events.

See the module docstring for the meaning of template, pack, shape, and produces_objects. severity_field and value_field name legacy columns read into the severity and value attributes, drop_fields lists legacy columns to discard, and standard_attrs maps standard attribute keys to a legacy column name or a literal value.

clear_overrides ¤

clear_overrides(
    class_name: str | None = None,
    method_name: str | None = None,
) -> None

Remove registered override(s).

  • No arguments: clear every registration (typical test teardown).
  • Both arguments: clear that specific (class, method) only.
  • Class name only: clear every override on that class.

Deprecated class and method names (e.g. CycleTimeTracking) resolve to their current names, like :func:register_adapter. Has no effect if the targeted entries are already absent.

Parameters:

Name Type Description Default
class_name str | None

Detector class name; None clears every registration.

None
method_name str | None

Method name; None clears every method of class_name.

None

Raises:

Type Description
ValueError

If method_name is given without class_name.

has_override ¤

has_override(class_name: str, method_name: str) -> bool

Return True iff a custom adapter is registered for (class, method).

register_adapter ¤

register_adapter(
    class_name: str, method_name: str
) -> Callable[[AdapterFn], AdapterFn]

Register a custom adapter for a specific (class, method).

The function will be called with (detector_frame, *, rule, detector, objects, qualifiers).

align_columns ¤

align_columns(*logs: EventLog) -> list[EventLog]

Reindex every log's events table to the union of their columns.

Each returned :class:EventLog exposes an identical, identically-ordered set of event columns — the canonical core first (in emit order), then the remaining attribute columns sorted for determinism. Columns absent from a given log are added and filled with NA, so the frames can be appended or stacked directly without a column mismatch. objects and relations already have fixed schemas and are returned unchanged.

Returns a list parallel to the inputs. An empty call returns [].

to_event_log_xes ¤

to_event_log_xes(
    eventlog: EventLog,
    *,
    case_object_type: str = "asset",
    lifecycle: str = "single"
) -> pd.DataFrame

Flatten the event log into an XES-style DataFrame.

A trace is built per object of case_object_type — each event linked to that object becomes one (or two) rows in the trace.

lifecycle="single" — interval events become one row with lifecycle:transition="complete". time:timestamp is the interval-end; start_timestamp is exposed verbatim.

lifecycle="two_row" — interval events expand into a start row (using ts_shape:start_timestamp) and a complete row, paired by concept:instance.

Parameters:

Name Type Description Default
eventlog EventLog

The event log to flatten.

required
case_object_type str

Object type whose objects become the traces.

'asset'
lifecycle str

"single" or "two_row".

'single'

Returns:

Type Description
DataFrame

DataFrame with case:concept:name, concept:name,

DataFrame

time:timestamp, lifecycle:transition (and org:resource

DataFrame

when the log links operators), sorted by case and time.

Raises:

Type Description
ValueError

When the log has no objects, no object of case_object_type, or lifecycle is unknown.

describe ¤

describe(detector: str) -> dict[str, Any]

Return everything ts-shape knows about a detector method.

Parameters:

Name Type Description Default
detector str

"ClassName.method_name" -- the key accepted by :func:~ts_shape.eventlog.to_event_log.

required

Returns:

Type Description
dict[str, Any]

A dict with class_name, method_name, the registered

dict[str, Any]

class:~ts_shape.eventlog.taxonomy.LabelRule fields (pack,

dict[str, Any]

shape, activity_template, produces_objects,

dict[str, Any]

severity_field, value_field, drop_fields,

dict[str, Any]

standard_attrs), the conceptual archetype and

dict[str, Any]

has_override.

Raises:

Type Description
ValueError

If detector is not "ClassName.method_name".

KeyError

If the method has no taxonomy entry.

compile_expression ¤

compile_expression(
    expression: str,
) -> Callable[[pd.DataFrame], pd.Series]

Compile a trigger expression to a vectorized boolean mask function.

Python's and / or / not / in, chained comparisons and min / max over columns are rejected here, with a hint to the element-wise &, |, ~ and column.isin([...]).

Parameters:

Name Type Description Default
expression str

The trigger, e.g. "(torque > 75) & (state == 'run')".

required

Returns:

Type Description
Callable[[DataFrame], Series]

A callable that takes a DataFrame and returns a boolean Series

Callable[[DataFrame], Series]

aligned with its rows (missing -> False).

Raises:

Type Description
UnsafeExpression

For disallowed or non-element-wise syntax. The callable raises :class:~ts_shape.errors.ColumnNotFoundError for a name that is not a column and ValueError when the result is not boolean.

load_dicts ¤

load_dicts(
    entries: Iterable[Mapping[str, Any]],
) -> list[LambdaDetector]

Compile and register an iterable of dict rules, all or nothing.

Every rule is validated and its trigger compiled before any is registered; if one fails, the rules registered by this call are removed again and the error is raised.

Parameters:

Name Type Description Default
entries Iterable[Mapping[str, Any]]

Rule mappings with the :class:RuleSpec fields; trigger is a mapping with expression (and optional min_duration_s, group_by -- a column name or a list of them).

required

Returns:

Name Type Description
One list[LambdaDetector]

class:LambdaDetector per rule, in input order.

Raises:

Type Description
ValueError

For a malformed rule, a duplicate registration or an expression that does not compile (:class:UnsafeExpression).

load_yaml ¤

load_yaml(path: str | Path) -> list[LambdaDetector]

Load and register every rule under a YAML file's rules: key.

YAML is imported lazily so :mod:ts_shape.eventlog does not gain a hard runtime dependency on PyYAML for users who only use built-in detectors. Install ts-shape[yaml] to enable this loader, or use :func:load_dicts with a hand-built list.

Parameters:

Name Type Description Default
path str | Path

Path to a YAML file with a top-level rules: list.

required

Returns:

Name Type Description
One list[LambdaDetector]

class:LambdaDetector per rule (see :func:load_dicts).

Raises:

Type Description
ImportError

When PyYAML is not installed.

ValueError

When the file has no rules: list or a rule is invalid.

register_lambda_rule ¤

register_lambda_rule(spec: RuleSpec) -> LambdaDetector

Register spec in REGISTRY + ARCHETYPE_BY_METHOD; return runnable detector.

The trigger expression is compiled here, so a bad expression fails at registration rather than at the first evaluation.

Parameters:

Name Type Description Default
spec RuleSpec

The rule to register.

required

Returns:

Type Description
LambdaDetector

The runnable :class:LambdaDetector.

Raises:

Type Description
ValueError

If the spec is malformed, the expression does not compile (:class:UnsafeExpression), the standard_attrs mapping uses unknown keys, the archetype's required keys are missing, or a rule with the same (class_name, method_name) is already registered.

run_backtest ¤

run_backtest(
    detector: LambdaDetector,
    dataframe: DataFrame,
    *,
    objects: Mapping[str, object] | None = None,
    qualifiers: Mapping[str, str] | None = None
) -> BacktestResult

Run detector over dataframe and summarize hit counts.

Parameters:

Name Type Description Default
detector LambdaDetector

The registered lambda rule.

required
dataframe DataFrame

Input frame the rule is evaluated on.

required
objects Mapping[str, object] | None

Object bindings passed to the event-log adapter.

None
qualifiers Mapping[str, str] | None

Relation qualifier per object type.

None

Returns:

Name Type Description
A BacktestResult

class:BacktestResult with the hit count, hits per severity and

BacktestResult

per asset, and the full event log.

unregister_lambda_rule ¤

unregister_lambda_rule(
    class_name: str, method_name: str
) -> None

Remove a rule previously installed by :func:register_lambda_rule.

Used by tests to keep the global REGISTRY clean between cases. No-op if the rule was not registered. Deprecated class / method names resolve to their current names, as in the registry lookup.

Parameters:

Name Type Description Default
class_name str

The rule's class name.

required
method_name str

The rule's method name.

required

to_event_log ¤

to_event_log(
    dataframe: DataFrame,
    *,
    detector: str,
    objects: Mapping[str, object] | None = None,
    qualifiers: Mapping[str, str] | None = None,
    o2o: TableInput = None,
    object_changes: TableInput = None,
    validate: bool = True,
    timestamp: object | None = None
) -> EventLog

Normalize a detector frame (a detector's output DataFrame) into the canonical event log.

Timestamps: tz-aware times are converted to UTC; naive times are taken to be UTC (localize them first if they are local wall-clock times). Interval rows that end before they start are dropped with a :class:~ts_shape.errors.DataQualityWarning.

Event ids (ocel:eid) are content-based: a hash of the detector, the row's source_uuid, its timestamps, the activity and every other column of the row. They are reproducible across runs, independent of row order, and the same detector on two machines does not collide.

Parameters:

Name Type Description Default
dataframe DataFrame

The detector frame.

required
detector str

"ClassName.method_name" -- the key used in the taxonomy registry and the value written to ts_shape:detector. Deprecated names resolve to the current ones.

required
objects Mapping[str, object] | None

Binds OCEL object types to, per type:

  • a column name of dataframe (e.g. {"asset": "source_uuid"});
  • any other string or scalar -- a literal broadcast to every row ({"line": "L1"}); None binds no object;
  • a callable taking a row dict and returning an oid;
  • a pd.Series aligned with dataframe by index label (a length or label mismatch raises ValueError).

Types listed in the rule's produces_objects are also auto-extracted from standard columns (source_uuid -> asset) when not bound explicitly.

None
qualifiers Mapping[str, str] | None

Relation qualifier per object type.

None
o2o TableInput

OCEL 2.0 object-to-object relations, a DataFrame or iterable of dict rows with ocel:oid / ocel:oid2 (ocel:qualifier optional); see :func:~ts_shape.eventlog.schema.empty_o2o.

None
object_changes TableInput

Time-varying object attributes with ocel:oid, ocel:type, ocel:field, ocel:timestamp (ocel:value optional); see :func:~ts_shape.eventlog.schema.empty_object_changes. Naive timestamps are read as UTC.

None
validate bool

Check the result against the canonical schema.

True
timestamp object | None

Timestamp of the events of a static shape detector (which has no time column). Default: the Unix epoch (1970-01-01T00:00Z), so results are reproducible.

None

Returns:

Type Description
EventLog

The canonical :class:~ts_shape.eventlog.model.EventLog.

Raises:

Type Description
ValueError

For a malformed detector name, a misaligned Series binding or a table missing required columns.

KeyError

When the detector has no taxonomy entry, or the frame lacks the time columns of its shape.

attach_objects ¤

attach_objects(
    event_log: EventLog,
    dataframe: 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 dataframe 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.

Parameters:

Name Type Description Default
event_log EventLog

The events to link.

required
dataframe DataFrame

Long-format frame with the id signals.

required
specs Sequence[ObjectSpec]

Which signal carries which object type.

required
qualifiers Mapping[str, str] | None

Relation qualifier per object type.

None
uuid_column str

Signal identifier column.

'uuid'
time_column str

Timestamp column.

'systime'
hierarchy Mapping[str, str] | None

Child type -> parent type for part_of relations.

None
infer_o2o bool

Derive object-to-object relations.

True
validate bool

Check the result against the canonical schema.

True

Returns:

Type Description
EventLog

event_log merged with the detected objects and the new relations.

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 from each child presence to the parent presence it overlaps longest; without one, overlapping objects of different types get the symmetric co_occurs qualifier (one row per pair, the type that sorts first in ocel:oid).

Parameters:

Name Type Description Default
intervals DataFrame

Object table with oid, type, start, end.

required
hierarchy Mapping[str, str] | None

Child type -> parent type.

None

Returns:

Type Description
DataFrame

The OCEL 2.0 o2o table, de-duplicated.

detect_objects ¤

detect_objects(
    dataframe: 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: each child presence is part of the parent presence it overlaps longest — 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.

Parameters:

Name Type Description Default
dataframe DataFrame

Long-format frame with the id signals.

required
specs Sequence[ObjectSpec]

Which signal carries which object type.

required
uuid_column str

Signal identifier column.

'uuid'
time_column str

Timestamp column.

'systime'
hierarchy Mapping[str, str] | None

Child type -> parent type for part_of relations.

None
infer_o2o bool

Derive object-to-object relations.

True
validate bool

Check the result against the canonical schema.

True

Returns:

Name Type Description
An EventLog

class:EventLog with objects, o2o and object_changes

EventLog

and no events.

Raises:

Type Description
ColumnNotFoundError

When dataframe lacks the uuid, time or a spec's value column.

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 on (eid, oid, type):

  • temporal containment (contain, default on): link an event to every object whose presence contains the event timestamp. Presence is half-open [start, end) when another presence of the same signal (uuid column) or, without one, of the same type starts at end — so an event at a hand-over instant belongs to the incoming object only — and closed [start, end] otherwise (the last presence). NaT bounds are open. 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).

Parameters:

Name Type Description Default
events DataFrame

OCEL events table with ocel:eid (and ocel:timestamp for temporal linking).

required
intervals DataFrame

Object table with oid, type and, for temporal linking, start / end.

required
qualifiers Mapping[str, str] | None

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

None
key_columns Sequence[str] | None

Event columns whose value is an object id. Integral floats (an integer id column with gaps) match "1", not "1.0".

None
contain bool

Link by temporal containment.

True

Returns:

Type Description
DataFrame

The OCEL 2.0 relations table.

Raises:

Type Description
ColumnNotFoundError

When events lacks ocel:eid (or ocel:timestamp with contain) or intervals lacks oid / type.

object_intervals ¤

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

Extract one presence interval per object presence from id signals.

Returns a tidy frame with columns oid, type, start, end, duration_seconds, uuid (the id signal), source_uuid (of the id signal, when the frame has that column) plus one column per captured attribute (its value at the presence start). Times are UTC.

Each id signal is run-length encoded: an id is present from its first sample until the next value on the same signal (next id, or an empty / missing reading). A signal whose last reading is still an id keeps that object present until the last timestamp of the frame. An id that re-appears later yields another row with the same oid. An empty frame or no matching signal gives an empty frame with the full column set.

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).

Parameters:

Name Type Description Default
metadata DataFrame

Metadata table, one row per signal.

required
type_field str

Field naming the object type of a signal; rows without one are skipped.

'object_type'
value_column_field str

Field naming the signal's id column (default value_string); must be one of value_string, value_integer or value_double.

'object_value_column'
uuid_column str

Column (or index name) holding the signal uuid.

'uuid'

Returns:

Name Type Description
One list[ObjectSpec]

class:ObjectSpec per tagged signal; empty when the table has

list[ObjectSpec]

no uuid or type field.

Raises:

Type Description
ValueError

When a value-column entry is not an id column.

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).

A missing start / end (NaT) means the presence is unbounded on that side (e.g. an object that is still active). Every column other than oid / type / start / end / duration_seconds / uuid / source_uuid is an attribute, recorded at the presence start.

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

Parameters:

Name Type Description Default
event_log EventLog

The events to link.

required
intervals DataFrame

Object table with oid, type and optionally start / end and attribute columns.

required
hierarchy Mapping[str, str] | None

Child type -> parent type for part_of relations.

None
qualifiers Mapping[str, str] | None

Relation qualifier per object type.

None
key_columns Sequence[str] | None

Event columns that name an object id directly.

None
infer_o2o bool

Derive object-to-object relations.

True
validate bool

Check the result against the canonical schema.

True

Returns:

Type Description
EventLog

The validated five-table :class:EventLog.

Raises:

Type Description
ColumnNotFoundError

When intervals lacks oid or type.

to_event_log_ocel ¤

to_event_log_ocel(
    eventlog: EventLog, *, pm4py: bool = False
) -> OCEL2Tables

Return the OCEL 2.0 tables as an :class:OCEL2Tables bundle.

The canonical schema already uses OCEL 2.0 column names, so by default this is a pass-through with defensive copy() calls to prevent caller mutation.

With pm4py=True two tables are reshaped for pm4py's OCEL object: relations gains ocel:activity and ocel:timestamp of each event, and object_changes is widened — ocel:value is replaced by one column per distinct ocel:field holding the new value on that field's rows (a field named like a base column, e.g. ocel:type, gets the column <name>_field and a :class:~ts_shape.errors.DataQualityWarning). The other tables are unchanged.

Parameters:

Name Type Description Default
eventlog EventLog

The event log to export.

required
pm4py bool

Reshape relations and object_changes for pm4py.

False

Returns:

Type Description
OCEL2Tables

The five tables as :class:OCEL2Tables (copies).

empty_o2o ¤

empty_o2o() -> pd.DataFrame

Return an empty object-to-object relations table with the canonical columns and dtypes.

Returns:

Type Description
DataFrame

A zero-row DataFrame with the expected object-to-object relations schema.

empty_object_changes ¤

empty_object_changes() -> pd.DataFrame

Return an empty object changes table with the canonical columns and dtypes.

Returns:

Type Description
DataFrame

A zero-row DataFrame with the expected object changes schema.

register_object_type ¤

register_object_type(name: str) -> None

Register a custom OCEL object type so adapters can emit it.

validate ¤

validate(eventlog: EventLog) -> None

Raise ValueError if the event log violates the canonical schema.