Source code for pm4py.objects.conversion.ocel.variants.ocel_to_nx
'''
PM4Py – A Process Mining Library for Python
Copyright (C) 2024 Process Intelligence Solutions UG (haftungsbeschränkt)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see this software project's root or
visit <https://www.gnu.org/licenses/>.
Website: https://processintelligence.solutions
Contact: info@processintelligence.solutions
'''
from enum import Enum
from pm4py.objects.ocel.obj import OCEL
from typing import Optional, Dict, Any
from pm4py.util import exec_utils, nx_utils
from pm4py.objects.conversion.log.variants import to_event_stream
from copy import copy
[docs]
class Parameters(Enum):
INCLUDE_DF = "include_df"
INCLUDE_OBJECT_CHANGES = "include_object_changes"
[docs]
def apply(ocel: OCEL, parameters: Optional[Dict[Any, Any]] = None):
"""
Converts an OCEL to a NetworkX DiGraph object.
The nodes are the events and objects of the OCEL.
The edges are of two different types:
- relation edges, connecting an event to its related objects
- directly-follows edges, connecting an event to a following event (in the lifecycle of one of the related objects)
Parameters
---------------
ocel
Object-centric event log
parameters
Parameters of the algorithm, including:
- Parameters.INCLUDE_DF => include the directly-follows relationship
Returns
---------------
G
NetworkX DiGraph
"""
if parameters is None:
parameters = {}
include_df = exec_utils.get_param_value(
Parameters.INCLUDE_DF, parameters, True
)
include_object_changes = exec_utils.get_param_value(
Parameters.INCLUDE_OBJECT_CHANGES, parameters, True
)
G = nx_utils.DiGraph()
stream = ocel.events.to_dict("records")
stream = to_event_stream.__postprocess_stream(stream)
for ev in stream:
ev["type"] = "EVENT"
G.add_node(ev[ocel.event_id_column], attr=ev)
stream = ocel.objects.to_dict("records")
stream = to_event_stream.__postprocess_stream(stream)
for obj in stream:
obj["type"] = "OBJECT"
G.add_node(obj[ocel.object_id_column], attr=obj)
rel_cols = {ocel.event_id_column, ocel.object_id_column, ocel.qualifier}
relations = ocel.relations[list(rel_cols)]
stream = relations.to_dict("records")
for rel in stream:
qualifier = rel[ocel.qualifier]
if qualifier is None:
qualifier = ""
G.add_edge(
rel[ocel.event_id_column],
rel[ocel.object_id_column],
attr={"type": "E2O", "qualifier": qualifier},
)
obj_relations = ocel.o2o[
[ocel.object_id_column, ocel.object_id_column + "_2", ocel.qualifier]
]
stream = obj_relations.to_dict("records")
for rel in stream:
qualifier = rel[ocel.qualifier]
if qualifier is None:
qualifier = ""
G.add_edge(
rel[ocel.object_id_column],
rel[ocel.object_id_column + "_2"],
attr={"type": "O2O", "qualifier": qualifier},
)
if include_df:
lifecycle = (
relations.groupby(ocel.object_id_column)
.agg(list)
.to_dict()[ocel.event_id_column]
)
for obj in lifecycle:
lif = lifecycle[obj]
for i in range(len(lif) - 1):
G.add_edge(
lif[i], lif[i + 1], attr={"type": "DF", "object": obj}
)
if include_object_changes:
object_changes = ocel.object_changes.to_dict("records")
for i in range(len(object_changes)):
change_id = "@@change##%d" % i
change_dict = copy(object_changes[i])
change_dict["type"] = "CHANGE"
G.add_node(change_id, attr=change_dict)
G.add_edge(
change_id,
object_changes[i][ocel.object_id_column],
attr={"type": "CHANGE"},
)
return G