Why a registry
Event logs are large. Converting the 561,470 events of the Road Traffic Fine log into Python objects and back for every function call would take longer than most analyses. Instead, r4pm keeps logs, OCELs and activity projections in Rust and gives Python a string ID. Functions in r4pm.bindings take these IDs as arguments, and functions that produce large data store the result and return a new ID.
Small results come back as plain Python data: a directly-follows graph or a Petri net is a dict, a list of OC-DECLARE constraints is a list of dicts.
Working with items
import r4pm
# import_item returns the ID of the new registry item.
log = r4pm.import_item("EventLog", "data/road-traffic-fines.xes.gz")
print("log id:", log)
# Functions that produce large data store it and return another ID.
projection = r4pm.bindings.log_to_activity_projection(log)
for item in r4pm.list_items():
print(item["type"], item["id"])
# Pick the ID yourself to make scripts easier to read.
r4pm.import_item("SlimLinkedOCEL", "data/order-management.xml.gz", item_id="orders")
print(r4pm.bindings.locel_event_object_type_counts("orders")[0])
# Write items back to files. The extension selects the format.
r4pm.export_item(log, "fines.xes")
r4pm.export_item("orders", "orders.json")
# Registry items live until the Python process ends, or until you remove them.
r4pm.remove_item(log)
print(len(r4pm.list_items()), "items left")| Function | What it does |
|---|---|
import_item(kind, path, item_id=None) | Read a file into a new item of the given kind (EventLog, OCEL, SlimLinkedOCEL, IndexLinkedOCEL, EventLogActivityProjection) |
export_item(id, path) | Write an item to a file, in the format of the extension |
convert_item(id, kind, new_item_id=None) | Convert an item into another kind, e.g., OCEL into SlimLinkedOCEL |
list_items() | All items with their ID and kind |
remove_item(id) | Free an item's memory |
item_to_df(id) | An item as polars DataFrames (one for event logs, a dict of tables for OCELs) |
import_item_from_df(kind, data, item_id=None) | The reverse: build an item from DataFrames |
IDs you choose
Every function that creates an item has an optional output_id argument, and import_item has item_id. With a readable ID such as "orders", scripts are easier to follow and you do not have to pass variables around.
Lifetime
Items live until the Python process ends. In long notebooks with large logs, call remove_item on intermediate results you no longer need.
Not in Python
In Rust there is no registry. Functions take and return the data types directly, and the borrow checker decides how long data lives. The Rust quickstart shows the same analysis without IDs.