Case-centric and object-centric data
A case-centric event log groups events into traces, one trace per case (a fine, a patient, an order). This is the classic XES model, and most process discovery and conformance techniques work on it.
An object-centric event log (OCEL 2.0) drops the single case notion. Events relate to any number of objects of different types, and objects relate to each other. In the Order Management log, a pick item event relates to an item, a product and an employee at the same time.
The five types
| Type | What it is | Typical use | How to get one |
|---|---|---|---|
EventLog | Traces of events with attributes, as in XES | Directly-follows graphs, exporting XES | Import .xes, .xes.gz or .json; flatten an OCEL |
EventLogActivityProjection | Each trace variant once (as activity indices) with its frequency | Alpha+++, alignments, fitness, variants | log_to_activity_projection / EventLogActivityProjection::from(&log) |
OCEL | OCEL 2.0 data as stored in a file: events, objects, relationships, attribute values | Reading and writing OCEL files, DataFrames | Import .xml, .json, .sqlite, .ocel.csv (and .gz variants) |
SlimLinkedOCEL | A compact OCEL with indexed event-to-object and object-to-object relations in both directions | Almost all object-centric functions: OC-DECLARE, flattening, statistics, building logs from scratch | Import the same files as OCEL, or convert an OCEL |
IndexLinkedOCEL | The full OCEL plus index-based lookups | Custom Rust code that needs both the original data and fast lookups | Import the same files as OCEL, or index_link_ocel |
For object-centric work, start with SlimLinkedOCEL. Every function page in the API reference shows the type each argument expects.
In code
import r4pm
# Case-centric: one trace per case, events ordered by time.
log = r4pm.import_item("EventLog", "data/road-traffic-fines.xes.gz")
projection = r4pm.bindings.log_to_activity_projection(log)
print(
r4pm.bindings.get_num_cases(projection), "cases,",
r4pm.bindings.get_num_variants(projection), "variants",
)
# Object-centric: the OCEL 2.0 data as it is stored in the file.
ocel = r4pm.import_item("OCEL", "data/order-management.xml.gz")
tables = r4pm.item_to_df(ocel)
print(tables["events"].height, "events,", tables["objects"].height, "objects")
# Most analysis functions take the linked form, which indexes all relationships.
locel = r4pm.convert_item(ocel, "SlimLinkedOCEL")
for event_type, object_type, count in r4pm.bindings.locel_event_object_type_counts(locel)[:3]:
print(f"{event_type} -> {object_type}: {count}")In Python, import_item builds any of these types directly from a file, so you rarely convert by hand. r4pm.convert_item(id, "SlimLinkedOCEL") turns one registry item into another when you need to.
In Rust, SlimLinkedOCEL::from_ocel(ocel) and IndexLinkedOCEL::from_ocel(ocel) build the linked forms, and both implement Importable themselves. The LinkedOCELAccess trait gives the same read access on both: all events and objects, their types, and relationships in both directions.
From object-centric to case-centric
Classic techniques need a single case notion. flatten_ocel_on(ocel, object_type) picks one object type and turns every object of that type into a trace with all events related to it. The filter and flatten how-to shows it on the Order Management log.