Labeling and taxonomy¤
The rules that fill ocel:activity and the standard attributes of every event.
They keep the events of any two detectors comparable without per-detector
code downstream. Each detector method has one LabelRule in
ts_shape.eventlog.taxonomy.REGISTRY; tests/eventlog/test_adapter_coverage.py
fails when a public detector method has no rule.
Activity names¤
<pack>.<family>.<specifier>[.<subtype>]
- All segments are lowercase snake_case, separated by dots.
packis one ofquality,production,engineering,maintenance,energy,correlation,supplychain,development.familyis the topic within the pack (outlier,machine_state,setpoint, ...). Reuse an existing family where one fits.specifierdistinguishes methods within the family (zscore,iqr,madforquality.outlier).- A segment may be a
{column}placeholder, filled from the detector's output row. A missing value renders asunknown.
| Detector method | ocel:activity |
|---|---|
OutlierDetectionEvents.detect_outliers_zscore |
quality.outlier.zscore |
StatisticalProcessControlEvents.detect_violations |
quality.spc.rule_violation |
MachineStateEvents.detect_run_idle |
production.machine_state.{state} (...run, ...idle) |
MachineStateEvents.transition_events |
production.machine_state.transition_{transition} |
SetpointChangeEvents.detect_setpoint_steps |
engineering.setpoint.step_{change_type} |
DegradationDetectionEvents.detect_trend_degradation |
maintenance.degradation.trend |
The families in use can be listed from the registry:
from ts_shape.eventlog.taxonomy import REGISTRY
families = sorted({rule.template.split(".")[1] for rule in REGISTRY.values() if rule.pack == "quality"})
Severity is never part of the activity name; it goes into ts_shape:severity.
Attribute prefixes¤
| Prefix | Source | Examples |
|---|---|---|
ocel: |
OCEL 2.0 | ocel:eid, ocel:activity, ocel:timestamp, ocel:oid, ocel:type, ocel:qualifier |
ts_shape: |
ts-shape fields without an OCEL counterpart | ts_shape:start_timestamp, ts_shape:duration_s, ts_shape:detector, ts_shape:severity |
<pack>: |
the detector's own output columns | production:state, quality:triggered_rule, engineering:magnitude |
concept:, time:, case:, lifecycle:, org: |
XES, added by to_event_log_xes only |
concept:name, time:timestamp, case:concept:name |
The pack prefix keeps columns of different packs apart: two packs that both
output a state column produce production:state and quality:state.
Severity¤
When a rule names a numeric severity_field, its value is bucketed:
| Value | ts_shape:severity |
|---|---|
< 3.0 |
info |
3.0 to < 4.5 |
warn |
>= 4.5 |
critical |
| missing or not numeric | <NA> |
The thresholds match the z-score style severity column of
OutlierDetectionEvents. A text column that already holds info, warn or
critical is passed through.
Object types and qualifiers¤
asset is created automatically from source_uuid. The other standard object
types are tool, sensor, signal, station (equipment), cycle, batch,
lot, recipe, work_order, shift (process context), material, part,
serial, article (product) and operator. Add your own with
register_object_type("name").
Recommended values for ocel:qualifier (free text is allowed):
| Qualifier | Object type | Meaning |
|---|---|---|
produced_on |
asset |
the event happened on this asset |
during_batch |
batch |
the event happened while this batch ran |
during_cycle |
cycle |
the event happened during this cycle |
during_shift |
shift |
the event happened during this shift |
made_of |
material |
the product contained this material |
identified_by |
serial |
the product carries this serial number |
operated_by |
operator |
the operator on duty |
measured_by |
sensor |
the sensor that produced the reading |
governed_by |
recipe |
the recipe in effect |
Standard attribute extension¤
Besides the core columns, each LabelRule maps detector output to a fixed set
of attribute keys (ts_shape.eventlog.schema.STANDARD_ATTR_KEYS). Two
detectors that report the same kind of thing then use the same column.
| Key | Type | Content |
|---|---|---|
ts_shape:method |
string | algorithm, for example zscore, western_electric, cusum |
ts_shape:baseline |
float | expected value (center line, setpoint, baseline mean) |
ts_shape:threshold_low |
float | lower bound; NaN if one-sided |
ts_shape:threshold_high |
float | upper bound; NaN if one-sided |
ts_shape:deviation |
float | value - baseline |
ts_shape:deviation_pct |
float | (value - baseline) / baseline |
ts_shape:direction |
string | above, below, up, down, outside, inside, lead, lag, shift |
ts_shape:confidence |
float | 0 to 1, for probabilistic detectors |
ts_shape:sample_count |
int | number of observations behind the row |
ts_shape:outcome |
string | ok, nok, rework, scrap, pass, fail or a reason code |
ts_shape:lifecycle_state |
string | raised, cleared, predicted or a state name (run, idle) |
ts_shape:lifecycle_pair_id |
string | pairs a raise and a clear into one occurrence |
Each entry in standard_attrs is either the name of a detector output column
(renamed and converted to the declared type), a literal broadcast to every row,
or None (not applicable). Two registry entries as examples:
from ts_shape.eventlog.taxonomy import LabelRule
LabelRule(
template="quality.outlier.zscore",
pack="quality",
shape="point",
severity_field="severity",
standard_attrs={"ts_shape:method": "zscore", "ts_shape:direction": "outside"}, # literals
)
LabelRule(
template="production.cycle_time.statistics",
pack="production",
shape="static",
standard_attrs={"ts_shape:sample_count": "count"}, # taken from the "count" column
)
With these keys, questions across detectors need no per-detector code:
import ts_shape
from ts_shape.eventlog import concat, to_event_log
df = ts_shape.make_timeseries(["sensor:temp"], n_points=1000, n_outliers=5)
detector = ts_shape.OutlierDetectionEvents(df, value_column="value_double")
log = concat(
to_event_log(detector.detect_outliers_zscore(), detector="OutlierDetectionEvents.detect_outliers_zscore"),
to_event_log(detector.detect_outliers_mad(), detector="OutlierDetectionEvents.detect_outliers_mad"),
)
log.events.groupby(["ts_shape:method", "ts_shape:direction"])["ocel:eid"].count()
Required keys per archetype¤
Every method is assigned one archetype in
ts_shape.eventlog.archetypes.ARCHETYPE_BY_METHOD, and must fill that
archetype's required keys (checked by test_required_standard_attrs_per_archetype):
| Archetype | Required | Typical optional |
|---|---|---|
threshold |
method, direction |
baseline, threshold_low, threshold_high, deviation, deviation_pct, confidence |
interval |
lifecycle_state |
lifecycle_pair_id, sample_count, direction |
aggregate |
sample_count |
baseline, threshold_low, threshold_high, method |
outcome |
outcome |
sample_count, method |
static |
method |
sample_count, baseline, threshold_low, threshold_high |
trace |
lifecycle_state, direction |
sample_count |
forecast |
method, confidence |
baseline, threshold_low, threshold_high |
correlation |
method |
direction, confidence, sample_count |
Adding a new detector method¤
- Add a
LabelRuletoREGISTRYinsrc/ts_shape/eventlog/taxonomy.py, following the naming rules above. Setshapeto what the method returns (point,interval,summaryorstatic). - For several activities from one method, use a
{column}placeholder and make sure the column is in the output. - If severity or the main value are in non-standard columns, set
severity_fieldorvalue_field. - Add the method to
ARCHETYPE_BY_METHODinsrc/ts_shape/eventlog/archetypes.pyand fill the archetype's requiredstandard_attrs. - Run
pytest tests/eventlog/test_adapter_coverage.py -q. It checks that every method has a rule and an archetype, that no rule is orphaned, that everystandard_attrskey is in the vocabulary and that the required keys are filled. - If the output does not fit the generic adapter (several events per row, nested data), register a custom adapter.