diff --git a/CHANGELOG.md b/CHANGELOG.md index 74a19c33af..54b111b968 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,127 @@ # Changelog of pm4py +## pm4py 2.7.14 (2024.XX.YY) + +### Added + +### Changed + +### Deprecated + +### Fixed + +### Removed + +### Other + + +--- + + +## pm4py 2.7.13 (2024.07.11) + +### Added + +### Changed +* 75c893999e32b3a1a6081db854b9a3cd10eaaa44 + * Dockerfile refactoring +* 7b80dad1fcc6955730357f46754c4a511b83ee58 + * updating requirements list +* 52459f95d42e2fc39568b0d685bd794c319f205a + * refactoring dependencies generation script +* 928ecf5f8192ff1ef7944c5b0a7be7c530814ad9 + * refactoring OpenAI query interface +* 5fee9b4ac61c227ac77cbd28b888a7321ba401bf + * refactor OCEL object repr +* 6c1ea7a38b3ef18fe9d490164ecebd85c432915f + * possibility to set the title in pm4py visualizations + +### Deprecated + +### Fixed +* 954c2fbdc258b90bcba85001592a5d2950a79724 + * bug fix temporal profile conformance checking +* cd8f9fb9f49a5955f0a26f547770aefa8ff222b8 + * issue with closing in-memory files when parsing a XES from string +* d51bd9e24aed442f011917d6b77793b118bbf89f + * minor fix OCEL interleavings computation +* 39d769cad1327c47d5948b0c40be4a90e41fcce7 + * removed indeterminism in TBR with duplicate transitions +* 2fb8e6bffcac11b14fe13f152ac0b096cdc0d03a + * penwidth assignation in DFG edges +* c753425ffd63da55fc0d3e71ab5b5fb62a2bf359 + * OCEL exporters consistency check +* e98d795c977348dcbe7495d62c8975fae0b9499c + * LTL filters documentation in the simplified interface + +### Removed + +### Other +* ed708047252f96f3bf103ba81d6139b433c39b85 + * printing Python versions in tests and examples +* c48882b88326608dc5c6a4101b8123c71a852e18 + * parameter to enable/disable visualizations +* 607d2eb601299a23335f8c1976cc59f63bcae5f2 + * added example for Inductive Miner LC + +--- + + +## pm4py 2.7.12.1 (2024.04.17) + +### Added + +### Changed +* 5641f9f2c830b567c138524a0b5e705a3836102a + * minor change OCEL names_stripping +* c48882b88326608dc5c6a4101b8123c71a852e18 + * parameter to enable/disable visualizations' view + +### Deprecated + +### Fixed +* 1c6887fda74d2b0a36f483351a6978738517e61e + * bug fix inductive miner infreuent +* e7e8ebf6894d9bc85febc43c82613d185e16ee3b + * solving performance bottleneck in IM +* 4811bbde07f6639deb7c87bdda61504de0889873 + * resolved indeterminism in IM fallthroughs +* df1db968adf8bb5f483410be0ceef2928c2b3e6c + * missing tree sort in IM +* 4958e2b8407c01923e5b65c7e46a5f89d8f927dc + * missing fold of PT objects in IM +* 8e404c6c2b53cf14687d1f82d53a4a0fc4ac99d6 + * fixed folding of PTs +* a871cbc4bdeaa81013fbfa7a6ec5054d494cb329 + * fixed parsing of PTs +* a72cd92410653bf2f0b2ff35ab5d06bf73a95305 + eddae51259114caf4e1447beee4981f2e00bcb9c + 69dcf7a836a5af3ab5da64701f43e3d8cf6b07aa + * fixed WF-net-to-PT conversion +* 0980ac460d69beb9ede4403a3ed002c1cd30536e + * fixed docstring OCEL flattening +* 6f9c67f48ad89e88d58c2481e85c6f3de9e6e1a5 + * fix small issues with stochastic Petri net +* 6781a90df2ba8d90155e7b5135520076843cac33 + * bug fix DECLARE discovery and conformance checking +* 7330d5100c8b8916eba9e4b0f31e8fa1f9157c98 + * bug fix log skeleton discovery (always after, always before) +* ec2f0b5b43d83a7b9fa154456241e3c685fcb721 + * bug fix log-to-prefix-tree discovery (final nodes) +* d580fedd4b770ed85f6864cd6d435db2477684f2 + * bumping release number. forcing numpy<2 and pandas<3 +* 4f8bbf6d884e25c5474479fd86fe7331076ac2fe + * bug fixes for Pandas >= 3.0 future support + +### Removed + +### Other + + +--- + + ## pm4py 2.7.12 (2024.03.21) ### Added @@ -34,8 +155,10 @@ ### Other + --- + ## pm4py 2.7.11 (2024.03.01) ### Added diff --git a/pm4py/meta.py b/pm4py/meta.py index 619ff9f6ac..973a680238 100644 --- a/pm4py/meta.py +++ b/pm4py/meta.py @@ -21,7 +21,7 @@ ''' __name__ = 'pm4py' -VERSION = '2.7.12.4' +VERSION = '2.7.13' __version__ = VERSION __doc__ = 'Process mining for Python' __author__ = 'Process Intelligence Solutions (PIS)' diff --git a/pm4py/objects/bpmn/importer/variants/lxml.py b/pm4py/objects/bpmn/importer/variants/lxml.py index 002a465417..1460bead24 100644 --- a/pm4py/objects/bpmn/importer/variants/lxml.py +++ b/pm4py/objects/bpmn/importer/variants/lxml.py @@ -305,15 +305,13 @@ def parse_element(bpmn_graph, counts, curr_el, parents, incoming_dict, outgoing_ incoming_dict[seq_flow_id][3], incoming_dict[seq_flow_id][4]) for seq_flow_id in outgoing_dict: if outgoing_dict[seq_flow_id][0] in nodes_dict: - outgoing_dict[seq_flow_id] = ( - nodes_dict[outgoing_dict[seq_flow_id][0]], outgoing_dict[seq_flow_id][1], outgoing_dict[seq_flow_id][2], - outgoing_dict[seq_flow_id][3], outgoing_dict[seq_flow_id][4]) + outgoing_dict[seq_flow_id] = (nodes_dict[outgoing_dict[seq_flow_id][0]], outgoing_dict[seq_flow_id][1], outgoing_dict[seq_flow_id][2], outgoing_dict[seq_flow_id][3], outgoing_dict[seq_flow_id][4]) # also supports flows without waypoints flows_without_waypoints = set(flow_info).union(set(outgoing_dict).intersection(set(incoming_dict))) for flow_id in flows_without_waypoints: flow_info[flow_id] = [] - + for flow_id in flow_info: if flow_id in outgoing_dict and flow_id in incoming_dict: flow = None diff --git a/pm4py/objects/ocel/obj.py b/pm4py/objects/ocel/obj.py index 3078df8427..571241ed88 100644 --- a/pm4py/objects/ocel/obj.py +++ b/pm4py/objects/ocel/obj.py @@ -25,6 +25,7 @@ from pm4py.util import exec_utils, pandas_utils import pandas as pd import numpy as np +from collections import Counter from copy import copy, deepcopy @@ -39,7 +40,8 @@ class Parameters(Enum): class OCEL(object): - def __init__(self, events=None, objects=None, relations=None, globals=None, parameters=None, o2o=None, e2e=None, object_changes=None): + def __init__(self, events=None, objects=None, relations=None, globals=None, parameters=None, o2o=None, e2e=None, + object_changes=None): if parameters is None: parameters = {} @@ -54,10 +56,12 @@ def __init__(self, events=None, objects=None, relations=None, globals=None, para self.event_timestamp = exec_utils.get_param_value(Parameters.EVENT_TIMESTAMP, parameters, constants.DEFAULT_EVENT_TIMESTAMP) self.qualifier = exec_utils.get_param_value(Parameters.QUALIFIER, parameters, constants.DEFAULT_QUALIFIER) - self.changed_field = exec_utils.get_param_value(Parameters.CHANGED_FIELD, parameters, constants.DEFAULT_CHNGD_FIELD) + self.changed_field = exec_utils.get_param_value(Parameters.CHANGED_FIELD, parameters, + constants.DEFAULT_CHNGD_FIELD) if events is None: - events = pandas_utils.instantiate_dataframe({self.event_id_column: [], self.event_activity: [], self.event_timestamp: []}) + events = pandas_utils.instantiate_dataframe( + {self.event_id_column: [], self.event_activity: [], self.event_timestamp: []}) if objects is None: objects = pandas_utils.instantiate_dataframe({self.object_id_column: [], self.object_type_column: []}) if relations is None: @@ -67,11 +71,15 @@ def __init__(self, events=None, objects=None, relations=None, globals=None, para if globals is None: globals = {} if o2o is None: - o2o = pandas_utils.instantiate_dataframe({self.object_id_column: [], self.object_id_column+"_2": [], self.qualifier: []}) + o2o = pandas_utils.instantiate_dataframe( + {self.object_id_column: [], self.object_id_column + "_2": [], self.qualifier: []}) if e2e is None: - e2e = pandas_utils.instantiate_dataframe({self.event_id_column: [], self.event_id_column+"_2": [], self.qualifier: []}) + e2e = pandas_utils.instantiate_dataframe( + {self.event_id_column: [], self.event_id_column + "_2": [], self.qualifier: []}) if object_changes is None: - object_changes = pandas_utils.instantiate_dataframe({self.object_id_column: [], self.object_type_column: [], self.event_timestamp: [], self.changed_field: []}) + object_changes = pandas_utils.instantiate_dataframe( + {self.object_id_column: [], self.object_type_column: [], self.event_timestamp: [], + self.changed_field: []}) if self.qualifier not in relations: relations[self.qualifier] = [None] * len(relations) @@ -111,10 +119,13 @@ def get_summary(self) -> str: ret.append(", number of object types: %d" % (self.objects[self.object_type_column].nunique())) ret.append(", events-objects relationships: %d)" % (len(self.relations))) ret.append("\n") - ret.append("Activities occurrences: " + str(self.events[self.event_activity].value_counts().to_dict())) + ret.append("Activities occurrences: " + str(Counter(self.events[self.event_activity].value_counts().to_dict()))) ret.append("\n") ret.append("Object types occurrences (number of objects): " + str( - self.objects[self.object_type_column].value_counts().to_dict())) + Counter(self.objects[self.object_type_column].value_counts().to_dict()))) + ret.append("\n") + ret.append("Unique activities per object type: " + str( + Counter(self.relations.groupby(self.object_type_column)[self.event_activity].nunique().to_dict()))) ret.append("\n") ret.append( "Please use .get_extended_table() to get a dataframe representation of the events related to the objects.") @@ -123,7 +134,8 @@ def get_summary(self) -> str: def is_ocel20(self): unique_qualifiers = [] if self.qualifier in self.relations.columns: - unique_qualifiers = [x for x in pandas_utils.format_unique(self.relations[self.qualifier].unique()) if not self.__check_is_nan(x)] + unique_qualifiers = [x for x in pandas_utils.format_unique(self.relations[self.qualifier].unique()) if + not self.__check_is_nan(x)] return len(self.o2o) > 0 or len(self.object_changes) > 0 or len(unique_qualifiers) > 0 @@ -143,7 +155,8 @@ def __repr__(self): return str(self.get_summary()) def __copy__(self): - return OCEL(self.events, self.objects, self.relations, copy(self.globals), copy(self.parameters), copy(self.o2o), copy(self.e2e), copy(self.object_changes)) + return OCEL(self.events, self.objects, self.relations, copy(self.globals), copy(self.parameters), + copy(self.o2o), copy(self.e2e), copy(self.object_changes)) def __deepcopy__(self, memo): return OCEL(self.events.copy(), self.objects.copy(), self.relations.copy(), deepcopy(self.globals), diff --git a/pm4py/objects/ocel/util/ocel_consistency.py b/pm4py/objects/ocel/util/ocel_consistency.py index 600e91e783..89519c7c6a 100644 --- a/pm4py/objects/ocel/util/ocel_consistency.py +++ b/pm4py/objects/ocel/util/ocel_consistency.py @@ -22,6 +22,7 @@ from pm4py.objects.ocel.obj import OCEL from typing import Optional, Dict, Any +import warnings def apply(ocel: OCEL, parameters: Optional[Dict[Any, Any]] = None) -> OCEL: @@ -45,12 +46,12 @@ def apply(ocel: OCEL, parameters: Optional[Dict[Any, Any]] = None) -> OCEL: parameters = {} fields = { - "events": ["ocel:eid", "ocel:activity"], - "objects": ["ocel:oid", "ocel:type"], - "relations": ["ocel:eid", "ocel:oid", "ocel:activity", "ocel:type"], - "o2o": ["ocel:oid", "ocel:oid_2"], - "e2e": ["ocel:eid", "ocel:eid_2"], - "object_changes": ["ocel:oid"] + "events": [ocel.event_id_column, ocel.event_activity], + "objects": [ocel.object_id_column, ocel.object_type_column], + "relations": [ocel.event_id_column, ocel.object_id_column, ocel.event_activity, ocel.object_type_column], + "o2o": [ocel.object_id_column, ocel.object_id_column+"_2"], + "e2e": [ocel.event_id_column, ocel.event_id_column+"_2"], + "object_changes": [ocel.object_id_column] } for tab in fields: @@ -62,4 +63,14 @@ def apply(ocel: OCEL, parameters: Optional[Dict[Any, Any]] = None) -> OCEL: df = df[df[fie].str.len() > 0] setattr(ocel, tab, df) + # check if the event IDs or object IDs are unique + num_ev_ids = ocel.events[ocel.event_id_column].nunique() + num_obj_ids = ocel.objects[ocel.object_id_column].nunique() + + if num_ev_ids < len(ocel.events): + warnings.warn("The event identifiers in the OCEL are not unique!") + + if num_obj_ids < len(ocel.objects): + warnings.warn("The object identifiers in the OCEL are not unique!") + return ocel diff --git a/pm4py/objects/trie/obj.py b/pm4py/objects/trie/obj.py index df7f533bd3..84e1def570 100644 --- a/pm4py/objects/trie/obj.py +++ b/pm4py/objects/trie/obj.py @@ -63,3 +63,22 @@ def _get_depth(self): label = property(_get_label, _set_label) final = property(_get_final, _set_final) depth = property(_get_depth, _set_depth) + + def repr_trie(self, indent_level=0): + stri = [] + + if self.label: + stri.append("\t"*indent_level + self.label) + indent_level += 1 + for child in self.children: + stri.append(child.repr_trie(indent_level=indent_level)) + if self.final: + stri.append("\t"*indent_level + "-- END --") + + return "\n".join(stri) + + def __repr__(self): + return self.repr_trie() + + def __str__(self): + return self.repr_trie() diff --git a/pm4py/util/constants.py b/pm4py/util/constants.py index e1efa032a6..f7e233bcde 100644 --- a/pm4py/util/constants.py +++ b/pm4py/util/constants.py @@ -149,6 +149,7 @@ def get_default_is_aware_enabled(): OPENAI_EXEC_RESULT = True if get_param_from_env("PM4PY_OPENAI_EXEC_RESULT", "False").lower() == "true" else False DEFAULT_GVIZ_VIEW = get_param_from_env("PM4PY_DEFAULT_GVIZ_VIEW", None) DEFAULT_ENABLE_VISUALIZATIONS_VIEW = get_param_from_env("PM4PY_DEFAULT_ENABLE_VISUALIZATIONS_VIEW", True) +DEFAULT_ENABLE_GRAPH_TITLES = get_param_from_env("PM4PY_DEFAULT_ENABLE_GRAPH_TITLES", False) JQUERY_LINK = "https://code.jquery.com/jquery-3.6.3.min.js" GRAPHVIZJS_LINK = "https://github.com/mdaines/viz-js/releases/download/v1.8.2/viz.js" diff --git a/pm4py/vis.py b/pm4py/vis.py index 1b9aac64df..38e55136ad 100644 --- a/pm4py/vis.py +++ b/pm4py/vis.py @@ -47,7 +47,8 @@ def view_petri_net(petri_net: PetriNet, initial_marking: Optional[Marking] = None, final_marking: Optional[Marking] = None, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", - decorations: Dict[Any, Any] = None, debug: bool = False, rankdir: str = constants.DEFAULT_RANKDIR_GVIZ): + decorations: Dict[Any, Any] = None, debug: bool = False, rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, + graph_title: Optional[str] = None): """ Views a (composite) Petri net @@ -59,6 +60,7 @@ def view_petri_net(petri_net: PetriNet, initial_marking: Optional[Marking] = Non :param decorations: Decorations (color, label) associated to the elements of the Petri net :param debug: Boolean enabling/disabling the debug mode (show place and transition's names) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -69,13 +71,19 @@ def view_petri_net(petri_net: PetriNet, initial_marking: Optional[Marking] = Non """ format = str(format).lower() from pm4py.visualization.petri_net import visualizer as pn_visualizer + parameters = {pn_visualizer.Variants.WO_DECORATION.value.Parameters.FORMAT: format, "bgcolor": bgcolor, "decorations": decorations, "debug": debug, "set_rankdir": rankdir} + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title gviz = pn_visualizer.apply(petri_net, initial_marking, final_marking, - parameters={pn_visualizer.Variants.WO_DECORATION.value.Parameters.FORMAT: format, "bgcolor": bgcolor, "decorations": decorations, "debug": debug, "set_rankdir": rankdir}) + parameters=parameters) pn_visualizer.view(gviz) def save_vis_petri_net(petri_net: PetriNet, initial_marking: Marking, final_marking: Marking, file_path: str, bgcolor: str = "white", - decorations: Dict[Any, Any] = None, debug: bool = False, rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, **kwargs): + decorations: Dict[Any, Any] = None, debug: bool = False, rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, + graph_title: Optional[str] = None, **kwargs): """ Saves a Petri net visualization to a file @@ -87,6 +95,7 @@ def save_vis_petri_net(petri_net: PetriNet, initial_marking: Marking, final_mark :param decorations: Decorations (color, label) associated to the elements of the Petri net :param debug: Boolean enabling/disabling the debug mode (show place and transition's names) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -98,13 +107,19 @@ def save_vis_petri_net(petri_net: PetriNet, initial_marking: Marking, final_mark file_path = str(file_path) format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.petri_net import visualizer as pn_visualizer + parameters = {pn_visualizer.Variants.WO_DECORATION.value.Parameters.FORMAT: format, "bgcolor": bgcolor, "decorations": decorations, "debug": debug, "set_rankdir": rankdir} + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title gviz = pn_visualizer.apply(petri_net, initial_marking, final_marking, - parameters={pn_visualizer.Variants.WO_DECORATION.value.Parameters.FORMAT: format, "bgcolor": bgcolor, "decorations": decorations, "debug": debug, "set_rankdir": rankdir}) + parameters=parameters) return pn_visualizer.save(gviz, file_path) def view_performance_dfg(dfg: dict, start_activities: dict, end_activities: dict, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, - aggregation_measure="mean", bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, serv_time: Optional[Dict[str, float]] = None): + aggregation_measure="mean", bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, serv_time: Optional[Dict[str, float]] = None, + graph_title: Optional[str] = None): """ Views a performance DFG @@ -116,6 +131,7 @@ def view_performance_dfg(dfg: dict, start_activities: dict, end_activities: dict :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) :param serv_time: (optional) provides the activities' service times, used to decorate the graph + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -135,12 +151,17 @@ def view_performance_dfg(dfg: dict, start_activities: dict, end_activities: dict parameters[dfg_parameters.AGGREGATION_MEASURE] = aggregation_measure parameters["bgcolor"] = bgcolor parameters["rankdir"] = rankdir + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title gviz = dfg_perf_visualizer.apply(dfg, serv_time=serv_time, parameters=parameters) dfg_visualizer.view(gviz) def save_vis_performance_dfg(dfg: dict, start_activities: dict, end_activities: dict, file_path: str, - aggregation_measure="mean", bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, serv_time: Optional[Dict[str, float]] = None, **kwargs): + aggregation_measure="mean", bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, serv_time: Optional[Dict[str, float]] = None, + graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of a performance DFG @@ -152,6 +173,7 @@ def save_vis_performance_dfg(dfg: dict, start_activities: dict, end_activities: :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) :param serv_time: (optional) provides the activities' service times, used to decorate the graph + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -172,11 +194,15 @@ def save_vis_performance_dfg(dfg: dict, start_activities: dict, end_activities: parameters[dfg_parameters.AGGREGATION_MEASURE] = aggregation_measure parameters["bgcolor"] = bgcolor parameters["rankdir"] = rankdir + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title gviz = dfg_perf_visualizer.apply(dfg, serv_time=serv_time, parameters=parameters) return dfg_visualizer.save(gviz, file_path) -def view_dfg(dfg: dict, start_activities: dict, end_activities: dict, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", max_num_edges: int = sys.maxsize, rankdir: str = constants.DEFAULT_RANKDIR_GVIZ): +def view_dfg(dfg: dict, start_activities: dict, end_activities: dict, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", max_num_edges: int = sys.maxsize, rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None): """ Views a (composite) DFG @@ -187,6 +213,7 @@ def view_dfg(dfg: dict, start_activities: dict, end_activities: dict, format: st :param bgcolor: Background color of the visualization (default: white) :param max_num_edges: maximum number of edges to represent in the graph :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -205,12 +232,16 @@ def view_dfg(dfg: dict, start_activities: dict, end_activities: dict, format: st parameters["bgcolor"] = bgcolor parameters["rankdir"] = rankdir parameters["maxNoOfEdgesInDiagram"] = max_num_edges + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title gviz = dfg_visualizer.apply(dfg, variant=dfg_visualizer.Variants.FREQUENCY, parameters=parameters) dfg_visualizer.view(gviz) -def save_vis_dfg(dfg: dict, start_activities: dict, end_activities: dict, file_path: str, bgcolor: str = "white", max_num_edges: int = sys.maxsize, rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, **kwargs): +def save_vis_dfg(dfg: dict, start_activities: dict, end_activities: dict, file_path: str, bgcolor: str = "white", max_num_edges: int = sys.maxsize, rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None, **kwargs): """ Saves a DFG visualization to a file @@ -221,6 +252,7 @@ def save_vis_dfg(dfg: dict, start_activities: dict, end_activities: dict, file_p :param bgcolor: Background color of the visualization (default: white) :param max_num_edges: maximum number of edges to represent in the graph :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -240,12 +272,16 @@ def save_vis_dfg(dfg: dict, start_activities: dict, end_activities: dict, file_p parameters["bgcolor"] = bgcolor parameters["rankdir"] = rankdir parameters["maxNoOfEdgesInDiagram"] = max_num_edges + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title gviz = dfg_visualizer.apply(dfg, variant=dfg_visualizer.Variants.FREQUENCY, parameters=parameters) return dfg_visualizer.save(gviz, file_path) -def view_process_tree(tree: ProcessTree, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ): +def view_process_tree(tree: ProcessTree, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None): """ Views a process tree @@ -253,6 +289,7 @@ def view_process_tree(tree: ProcessTree, format: str = constants.DEFAULT_FORMAT_ :param format: Format of the visualization (if html is provided, GraphvizJS is used to render the visualization in an HTML page) :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -264,11 +301,16 @@ def view_process_tree(tree: ProcessTree, format: str = constants.DEFAULT_FORMAT_ format = str(format).lower() from pm4py.visualization.process_tree import visualizer as pt_visualizer parameters = pt_visualizer.Variants.WO_DECORATION.value.Parameters - gviz = pt_visualizer.apply(tree, parameters={parameters.FORMAT: format, "bgcolor": bgcolor, "rankdir": rankdir}) + properties = {parameters.FORMAT: format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + gviz = pt_visualizer.apply(tree, parameters=properties) pt_visualizer.view(gviz) -def save_vis_process_tree(tree: ProcessTree, file_path: str, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, **kwargs): +def save_vis_process_tree(tree: ProcessTree, file_path: str, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of a process tree @@ -276,6 +318,7 @@ def save_vis_process_tree(tree: ProcessTree, file_path: str, bgcolor: str = "whi :param file_path: Destination path :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -288,11 +331,16 @@ def save_vis_process_tree(tree: ProcessTree, file_path: str, bgcolor: str = "whi format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.process_tree import visualizer as pt_visualizer parameters = pt_visualizer.Variants.WO_DECORATION.value.Parameters - gviz = pt_visualizer.apply(tree, parameters={parameters.FORMAT: format, "bgcolor": bgcolor, "rankdir": rankdir}) + properties = {parameters.FORMAT: format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + gviz = pt_visualizer.apply(tree, parameters=properties) return pt_visualizer.save(gviz, file_path) -def save_vis_bpmn(bpmn_graph: BPMN, file_path: str, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, variant_str: str = "classic", **kwargs): +def save_vis_bpmn(bpmn_graph: BPMN, file_path: str, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, variant_str: str = "classic", graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of a BPMN graph @@ -301,6 +349,7 @@ def save_vis_bpmn(bpmn_graph: BPMN, file_path: str, bgcolor: str = "white", rank :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) :param variant_str: variant of the visualization to be used ("classic" or "dagrejs") + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -319,11 +368,17 @@ def save_vis_bpmn(bpmn_graph: BPMN, file_path: str, bgcolor: str = "white", rank elif variant_str == "dagrejs": variant = bpmn_visualizer.Variants.DAGREJS - gviz = bpmn_visualizer.apply(bpmn_graph, variant=variant, parameters={"format": format, "bgcolor": bgcolor, "rankdir": rankdir}) + properties = {"format": format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = bpmn_visualizer.apply(bpmn_graph, variant=variant, parameters=properties) return bpmn_visualizer.save(gviz, file_path, variant=variant) -def view_bpmn(bpmn_graph: BPMN, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, variant_str: str = "classic"): +def view_bpmn(bpmn_graph: BPMN, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, variant_str: str = "classic", graph_title: Optional[str] = None): """ Views a BPMN graph @@ -332,6 +387,7 @@ def view_bpmn(bpmn_graph: BPMN, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) :param variant_str: variant of the visualization to be used ("classic" or "dagrejs") + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -349,17 +405,24 @@ def view_bpmn(bpmn_graph: BPMN, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW elif variant_str == "dagrejs": variant = bpmn_visualizer.Variants.DAGREJS - gviz = bpmn_visualizer.apply(bpmn_graph, variant=variant, parameters={"format": format, "bgcolor": bgcolor, "rankdir": rankdir}) + properties = {"format": format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = bpmn_visualizer.apply(bpmn_graph, variant=variant, parameters=properties) bpmn_visualizer.view(gviz, variant=variant) -def view_heuristics_net(heu_net: HeuristicsNet, format: str = "png", bgcolor: str = "white"): +def view_heuristics_net(heu_net: HeuristicsNet, format: str = "png", bgcolor: str = "white", graph_title: Optional[str] = None): """ Views an heuristics net :param heu_net: Heuristics net :param format: Format of the visualization :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -371,17 +434,24 @@ def view_heuristics_net(heu_net: HeuristicsNet, format: str = "png", bgcolor: st format = str(format).lower() from pm4py.visualization.heuristics_net import visualizer as hn_visualizer parameters = hn_visualizer.Variants.PYDOTPLUS.value.Parameters - gviz = hn_visualizer.apply(heu_net, parameters={parameters.FORMAT: format, "bgcolor": bgcolor}) + properties = {parameters.FORMAT: format, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = hn_visualizer.apply(heu_net, parameters=properties) hn_visualizer.view(gviz) -def save_vis_heuristics_net(heu_net: HeuristicsNet, file_path: str, bgcolor: str = "white", **kwargs): +def save_vis_heuristics_net(heu_net: HeuristicsNet, file_path: str, bgcolor: str = "white", graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of an heuristics net :param heu_net: Heuristics net :param file_path: Destination path :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -394,7 +464,13 @@ def save_vis_heuristics_net(heu_net: HeuristicsNet, file_path: str, bgcolor: str format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.heuristics_net import visualizer as hn_visualizer parameters = hn_visualizer.Variants.PYDOTPLUS.value.Parameters - gviz = hn_visualizer.apply(heu_net, parameters={parameters.FORMAT: format, "bgcolor": bgcolor}) + properties = {parameters.FORMAT: format, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = hn_visualizer.apply(heu_net, parameters=properties) return hn_visualizer.save(gviz, file_path) @@ -417,7 +493,7 @@ def __dotted_attribute_selection(log: Union[EventLog, pd.DataFrame], attributes) return log, attributes -def view_dotted_chart(log: Union[EventLog, pd.DataFrame], format: str = "png", attributes=None, bgcolor: str = "white", show_legend: bool = True): +def view_dotted_chart(log: Union[EventLog, pd.DataFrame], format: str = "png", attributes=None, bgcolor: str = "white", show_legend: bool = True, graph_title: Optional[str] = None): """ Displays the dotted chart @@ -442,6 +518,7 @@ def view_dotted_chart(log: Union[EventLog, pd.DataFrame], format: str = "png", a :param attributes: Attributes that should be used to construct the dotted chart. If None, the default dotted chart will be shown: x-axis: time y-axis: cases (in order of occurrence in the event log) color: activity. For custom attributes, use a list of attributes of the form [x-axis attribute, y-axis attribute, color attribute], e.g., ["concept:name", "org:resource", "concept:name"]) :param bgcolor: background color to be used in the dotted chart :param show_legend: boolean (enables/disables showing the legend) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -461,13 +538,17 @@ def view_dotted_chart(log: Union[EventLog, pd.DataFrame], format: str = "png", a parameters["format"] = format parameters["bgcolor"] = bgcolor parameters["show_legend"] = show_legend + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title from pm4py.visualization.dotted_chart import visualizer as dotted_chart_visualizer gviz = dotted_chart_visualizer.apply(log, attributes, parameters=parameters) dotted_chart_visualizer.view(gviz) -def save_vis_dotted_chart(log: Union[EventLog, pd.DataFrame], file_path: str, attributes=None, bgcolor: str = "white", show_legend: bool = True, **kwargs): +def save_vis_dotted_chart(log: Union[EventLog, pd.DataFrame], file_path: str, attributes=None, bgcolor: str = "white", show_legend: bool = True, graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of the dotted chart @@ -492,6 +573,7 @@ def save_vis_dotted_chart(log: Union[EventLog, pd.DataFrame], file_path: str, at :param attributes: Attributes that should be used to construct the dotted chart (for example, ["concept:name", "org:resource"]) :param bgcolor: background color to be used in the dotted chart :param show_legend: boolean (enables/disables showing the legend) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -511,6 +593,10 @@ def save_vis_dotted_chart(log: Union[EventLog, pd.DataFrame], file_path: str, at parameters["format"] = format parameters["bgcolor"] = bgcolor parameters["show_legend"] = show_legend + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title from pm4py.visualization.dotted_chart import visualizer as dotted_chart_visualizer gviz = dotted_chart_visualizer.apply(log, attributes, parameters=parameters) @@ -577,7 +663,7 @@ def save_vis_sna(sna_metric: SNA, file_path: str, variant_str: Optional[str] = N return sna_visualizer.save(gviz, file_path, variant=variant) -def view_case_duration_graph(log: Union[EventLog, pd.DataFrame], format: str = "png", activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name"): +def view_case_duration_graph(log: Union[EventLog, pd.DataFrame], format: str = "png", activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", graph_title: Optional[str] = None): """ Visualizes the case duration graph @@ -586,6 +672,7 @@ def view_case_duration_graph(log: Union[EventLog, pd.DataFrame], format: str = " :param activity_key: attribute to be used as activity :param case_id_key: attribute to be used as case identifier :param timestamp_key: attribute to be used as timestamp + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -603,12 +690,16 @@ def view_case_duration_graph(log: Union[EventLog, pd.DataFrame], format: str = " from pm4py.statistics.traces.generic.log import case_statistics graph = case_statistics.get_kde_caseduration(log, parameters=get_properties(log, activity_key=activity_key, case_id_key=case_id_key, timestamp_key=timestamp_key)) from pm4py.visualization.graphs import visualizer as graphs_visualizer + properties = {"format": format} + if graph_title is not None: + properties["title"] = graph_title + graph_vis = graphs_visualizer.apply(graph[0], graph[1], variant=graphs_visualizer.Variants.CASES, - parameters={"format": format}) + parameters=properties) graphs_visualizer.view(graph_vis) -def save_vis_case_duration_graph(log: Union[EventLog, pd.DataFrame], file_path: str, activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", **kwargs): +def save_vis_case_duration_graph(log: Union[EventLog, pd.DataFrame], file_path: str, activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", graph_title: Optional[str] = None, **kwargs): """ Saves the case duration graph in the specified path @@ -617,6 +708,7 @@ def save_vis_case_duration_graph(log: Union[EventLog, pd.DataFrame], file_path: :param activity_key: attribute to be used as activity :param case_id_key: attribute to be used as case identifier :param timestamp_key: attribute to be used as timestamp + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -635,12 +727,16 @@ def save_vis_case_duration_graph(log: Union[EventLog, pd.DataFrame], file_path: graph = case_statistics.get_kde_caseduration(log, parameters=get_properties(log, activity_key=activity_key, case_id_key=case_id_key, timestamp_key=timestamp_key)) format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.graphs import visualizer as graphs_visualizer + properties = {"format": format} + if graph_title is not None: + properties["title"] = graph_title + graph_vis = graphs_visualizer.apply(graph[0], graph[1], variant=graphs_visualizer.Variants.CASES, - parameters={"format": format}) + parameters=properties) return graphs_visualizer.save(graph_vis, file_path) -def view_events_per_time_graph(log: Union[EventLog, pd.DataFrame], format: str = "png", activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name"): +def view_events_per_time_graph(log: Union[EventLog, pd.DataFrame], format: str = "png", activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", graph_title: Optional[str] = None): """ Visualizes the events per time graph @@ -649,6 +745,7 @@ def view_events_per_time_graph(log: Union[EventLog, pd.DataFrame], format: str = :param activity_key: attribute to be used as activity :param case_id_key: attribute to be used as case identifier :param timestamp_key: attribute to be used as timestamp + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -666,12 +763,16 @@ def view_events_per_time_graph(log: Union[EventLog, pd.DataFrame], format: str = from pm4py.statistics.attributes.log import get as attributes_get graph = attributes_get.get_kde_date_attribute(log, parameters=get_properties(log, activity_key=activity_key, case_id_key=case_id_key, timestamp_key=timestamp_key)) from pm4py.visualization.graphs import visualizer as graphs_visualizer + properties = {"format": format} + if graph_title is not None: + properties["title"] = graph_title + graph_vis = graphs_visualizer.apply(graph[0], graph[1], variant=graphs_visualizer.Variants.DATES, - parameters={"format": format}) + parameters=properties) graphs_visualizer.view(graph_vis) -def save_vis_events_per_time_graph(log: Union[EventLog, pd.DataFrame], file_path: str, activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", **kwargs): +def save_vis_events_per_time_graph(log: Union[EventLog, pd.DataFrame], file_path: str, activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", graph_title: Optional[str] = None, **kwargs): """ Saves the events per time graph in the specified path @@ -680,6 +781,7 @@ def save_vis_events_per_time_graph(log: Union[EventLog, pd.DataFrame], file_path :param activity_key: attribute to be used as activity :param case_id_key: attribute to be used as case identifier :param timestamp_key: attribute to be used as timestamp + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -698,12 +800,17 @@ def save_vis_events_per_time_graph(log: Union[EventLog, pd.DataFrame], file_path graph = attributes_get.get_kde_date_attribute(log, attribute=timestamp_key, parameters=get_properties(log, activity_key=activity_key, case_id_key=case_id_key, timestamp_key=timestamp_key)) format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.graphs import visualizer as graphs_visualizer + properties = {"format": format} + if graph_title is not None: + properties["title"] = graph_title + graph_vis = graphs_visualizer.apply(graph[0], graph[1], variant=graphs_visualizer.Variants.DATES, - parameters={"format": format}) + parameters=properties) + return graphs_visualizer.save(graph_vis, file_path) -def view_performance_spectrum(log: Union[EventLog, pd.DataFrame], activities: List[str], format: str = "png", activity_key: str = "concept:name", timestamp_key: str = "time:timestamp", case_id_key: str = "case:concept:name", bgcolor: str = "white"): +def view_performance_spectrum(log: Union[EventLog, pd.DataFrame], activities: List[str], format: str = "png", activity_key: str = "concept:name", timestamp_key: str = "time:timestamp", case_id_key: str = "case:concept:name", bgcolor: str = "white", graph_title: Optional[str] = None): """ Displays the performance spectrum @@ -720,6 +827,7 @@ def view_performance_spectrum(log: Union[EventLog, pd.DataFrame], activities: Li :param case_id_key: attribute to be used as case identifier :param timestamp_key: attribute to be used as timestamp :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -738,11 +846,18 @@ def view_performance_spectrum(log: Union[EventLog, pd.DataFrame], activities: Li perf_spectrum = performance_spectrum.apply(log, activities, parameters=properties) from pm4py.visualization.performance_spectrum import visualizer as perf_spectrum_visualizer from pm4py.visualization.performance_spectrum.variants import neato - gviz = perf_spectrum_visualizer.apply(perf_spectrum, parameters={neato.Parameters.FORMAT.value: format, "bgcolor": bgcolor}) + + parameters = {neato.Parameters.FORMAT.value: format, "bgcolor": bgcolor} + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title + + gviz = perf_spectrum_visualizer.apply(perf_spectrum, parameters=parameters) perf_spectrum_visualizer.view(gviz) -def save_vis_performance_spectrum(log: Union[EventLog, pd.DataFrame], activities: List[str], file_path: str, activity_key: str = "concept:name", timestamp_key: str = "time:timestamp", case_id_key: str = "case:concept:name", bgcolor: str = "white", **kwargs): +def save_vis_performance_spectrum(log: Union[EventLog, pd.DataFrame], activities: List[str], file_path: str, activity_key: str = "concept:name", timestamp_key: str = "time:timestamp", case_id_key: str = "case:concept:name", bgcolor: str = "white", graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of the performance spectrum to a file @@ -757,6 +872,7 @@ def save_vis_performance_spectrum(log: Union[EventLog, pd.DataFrame], activities :param timestamp_key: attribute to be used for the timestamp :param case_id_key: attribute to be used as case identifier :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -776,7 +892,14 @@ def save_vis_performance_spectrum(log: Union[EventLog, pd.DataFrame], activities from pm4py.visualization.performance_spectrum import visualizer as perf_spectrum_visualizer from pm4py.visualization.performance_spectrum.variants import neato format = os.path.splitext(file_path)[1][1:].lower() - gviz = perf_spectrum_visualizer.apply(perf_spectrum, parameters={neato.Parameters.FORMAT.value: format, "bgcolor": bgcolor}) + + parameters = {neato.Parameters.FORMAT.value: format, "bgcolor": bgcolor} + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title + + gviz = perf_spectrum_visualizer.apply(perf_spectrum, parameters=parameters) return perf_spectrum_visualizer.save(gviz, file_path) @@ -822,7 +945,7 @@ def __builds_events_distribution_graph(log: Union[EventLog, pd.DataFrame], param return title, x_axis, y_axis, x, y -def view_events_distribution_graph(log: Union[EventLog, pd.DataFrame], distr_type: str = "days_week", format="png", activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name"): +def view_events_distribution_graph(log: Union[EventLog, pd.DataFrame], distr_type: str = "days_week", format="png", activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", graph_title: Optional[str] = None): """ Shows the distribution of the events in the specified dimension @@ -834,6 +957,7 @@ def view_events_distribution_graph(log: Union[EventLog, pd.DataFrame], distr_typ :param activity_key: attribute to be used as activity :param case_id_key: attribute to be used as case identifier :param timestamp_key: attribute to be used as timestamp + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -852,13 +976,16 @@ def view_events_distribution_graph(log: Union[EventLog, pd.DataFrame], distr_typ parameters["x_axis"] = x_axis; parameters["y_axis"] = y_axis; parameters["format"] = format + if graph_title is not None: + parameters["title"] = graph_title + from pm4py.visualization.graphs import visualizer as graphs_visualizer gviz = graphs_visualizer.apply(x, y, variant=graphs_visualizer.Variants.BARPLOT, parameters=parameters) graphs_visualizer.view(gviz) def save_vis_events_distribution_graph(log: Union[EventLog, pd.DataFrame], file_path: str, - distr_type: str = "days_week", activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", **kwargs): + distr_type: str = "days_week", activity_key="concept:name", timestamp_key="time:timestamp", case_id_key="case:concept:name", graph_title: Optional[str] = None, **kwargs): """ Saves the distribution of the events in a picture file @@ -870,6 +997,7 @@ def save_vis_events_distribution_graph(log: Union[EventLog, pd.DataFrame], file_ :param activity_key: attribute to be used as activity :param case_id_key: attribute to be used as case identifier :param timestamp_key: attribute to be used as timestamp + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -889,12 +1017,15 @@ def save_vis_events_distribution_graph(log: Union[EventLog, pd.DataFrame], file_ parameters["x_axis"] = x_axis; parameters["y_axis"] = y_axis; parameters["format"] = format + if graph_title is not None: + parameters["title"] = graph_title + from pm4py.visualization.graphs import visualizer as graphs_visualizer gviz = graphs_visualizer.apply(x, y, variant=graphs_visualizer.Variants.BARPLOT, parameters=parameters) return graphs_visualizer.save(gviz, file_path) -def view_ocdfg(ocdfg: Dict[str, Any], annotation: str = "frequency", act_metric: str = "events", edge_metric="event_couples", act_threshold: int = 0, edge_threshold: int = 0, performance_aggregation: str = "mean", format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ): +def view_ocdfg(ocdfg: Dict[str, Any], annotation: str = "frequency", act_metric: str = "events", edge_metric="event_couples", act_threshold: int = 0, edge_threshold: int = 0, performance_aggregation: str = "mean", format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None): """ Views an OC-DFG (object-centric directly-follows graph) with the provided configuration. @@ -910,6 +1041,7 @@ def view_ocdfg(ocdfg: Dict[str, Any], annotation: str = "frequency", act_metric: :param format: The format of the output visualization (if html is provided, GraphvizJS is used to render the visualization in an HTML page) :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -932,11 +1064,16 @@ def view_ocdfg(ocdfg: Dict[str, Any], annotation: str = "frequency", act_metric: parameters[classic.Parameters.PERFORMANCE_AGGREGATION_MEASURE] = performance_aggregation parameters["bgcolor"] = bgcolor parameters["rankdir"] = rankdir + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title + gviz = classic.apply(ocdfg, parameters=parameters) visualizer.view(gviz) -def save_vis_ocdfg(ocdfg: Dict[str, Any], file_path: str, annotation: str = "frequency", act_metric: str = "events", edge_metric="event_couples", act_threshold: int = 0, edge_threshold: int = 0, performance_aggregation: str = "mean", bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, **kwargs): +def save_vis_ocdfg(ocdfg: Dict[str, Any], file_path: str, annotation: str = "frequency", act_metric: str = "events", edge_metric="event_couples", act_threshold: int = 0, edge_threshold: int = 0, performance_aggregation: str = "mean", bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of an OC-DFG (object-centric directly-follows graph) with the provided configuration. @@ -952,6 +1089,7 @@ def save_vis_ocdfg(ocdfg: Dict[str, Any], file_path: str, annotation: str = "fre :param performance_aggregation: The aggregation measure to use for the performance: mean, median, min, max, sum :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -974,11 +1112,16 @@ def save_vis_ocdfg(ocdfg: Dict[str, Any], file_path: str, annotation: str = "fre parameters[classic.Parameters.PERFORMANCE_AGGREGATION_MEASURE] = performance_aggregation parameters["bgcolor"] = bgcolor parameters["rankdir"] = rankdir + parameters["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + parameters["enable_graph_title"] = True + parameters["graph_title"] = graph_title + gviz = classic.apply(ocdfg, parameters=parameters) return visualizer.save(gviz, file_path) -def view_ocpn(ocpn: Dict[str, Any], format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ): +def view_ocpn(ocpn: Dict[str, Any], format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None): """ Visualizes on the screen the object-centric Petri net @@ -986,6 +1129,7 @@ def view_ocpn(ocpn: Dict[str, Any], format: str = constants.DEFAULT_FORMAT_GVIZ_ :param format: Format of the visualization (if html is provided, GraphvizJS is used to render the visualization in an HTML page) :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -997,11 +1141,17 @@ def view_ocpn(ocpn: Dict[str, Any], format: str = constants.DEFAULT_FORMAT_GVIZ_ format = str(format).lower() from pm4py.visualization.ocel.ocpn import visualizer as ocpn_visualizer - gviz = ocpn_visualizer.apply(ocpn, parameters={"format": format, "bgcolor": bgcolor, "rankdir": rankdir}) + properties = {"format": format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = ocpn_visualizer.apply(ocpn, parameters=properties) ocpn_visualizer.view(gviz) -def save_vis_ocpn(ocpn: Dict[str, Any], file_path: str, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, **kwargs): +def save_vis_ocpn(ocpn: Dict[str, Any], file_path: str, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of the object-centric Petri net into a file @@ -1009,6 +1159,7 @@ def save_vis_ocpn(ocpn: Dict[str, Any], file_path: str, bgcolor: str = "white", :param file_path: Target path of the visualization :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1020,11 +1171,17 @@ def save_vis_ocpn(ocpn: Dict[str, Any], file_path: str, bgcolor: str = "white", file_path = str(file_path) format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.ocel.ocpn import visualizer as ocpn_visualizer - gviz = ocpn_visualizer.apply(ocpn, parameters={"format": format, "bgcolor": bgcolor, "rankdir": rankdir}) + properties = {"format": format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = ocpn_visualizer.apply(ocpn, parameters=properties) return ocpn_visualizer.save(gviz, file_path) -def view_network_analysis(network_analysis: Dict[Tuple[str, str], Dict[str, Any]], variant: str = "frequency", format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, activity_threshold: int = 1, edge_threshold: int = 1, bgcolor: str = "white"): +def view_network_analysis(network_analysis: Dict[Tuple[str, str], Dict[str, Any]], variant: str = "frequency", format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, activity_threshold: int = 1, edge_threshold: int = 1, bgcolor: str = "white", graph_title: Optional[str] = None): """ Visualizes the network analysis @@ -1034,6 +1191,7 @@ def view_network_analysis(network_analysis: Dict[Tuple[str, str], Dict[str, Any] :param activity_threshold: The minimum number of occurrences for an activity to be included (default: 1) :param edge_threshold: The minimum number of occurrences for an edge to be included (default: 1) :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1046,11 +1204,17 @@ def view_network_analysis(network_analysis: Dict[Tuple[str, str], Dict[str, Any] from pm4py.visualization.network_analysis import visualizer as network_analysis_visualizer variant = network_analysis_visualizer.Variants.PERFORMANCE if variant == "performance" else network_analysis_visualizer.Variants.FREQUENCY - gviz = network_analysis_visualizer.apply(network_analysis, variant=variant, parameters={"format": format, "activity_threshold": activity_threshold, "edge_threshold": edge_threshold, "bgcolor": bgcolor}) + properties = {"format": format, "activity_threshold": activity_threshold, "edge_threshold": edge_threshold, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = network_analysis_visualizer.apply(network_analysis, variant=variant, parameters=properties) network_analysis_visualizer.view(gviz) -def save_vis_network_analysis(network_analysis: Dict[Tuple[str, str], Dict[str, Any]], file_path: str, variant: str = "frequency", activity_threshold: int = 1, edge_threshold: int = 1, bgcolor: str = "white", **kwargs): +def save_vis_network_analysis(network_analysis: Dict[Tuple[str, str], Dict[str, Any]], file_path: str, variant: str = "frequency", activity_threshold: int = 1, edge_threshold: int = 1, bgcolor: str = "white", graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of the network analysis @@ -1060,6 +1224,7 @@ def save_vis_network_analysis(network_analysis: Dict[Tuple[str, str], Dict[str, :param activity_threshold: The minimum number of occurrences for an activity to be included (default: 1) :param edge_threshold: The minimum number of occurrences for an edge to be included (default: 1) :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1072,17 +1237,24 @@ def save_vis_network_analysis(network_analysis: Dict[Tuple[str, str], Dict[str, format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.network_analysis import visualizer as network_analysis_visualizer variant = network_analysis_visualizer.Variants.PERFORMANCE if variant == "performance" else network_analysis_visualizer.Variants.FREQUENCY - gviz = network_analysis_visualizer.apply(network_analysis, variant=variant, parameters={"format": format, "activity_threshold": activity_threshold, "edge_threshold": edge_threshold, "bgcolor": bgcolor}) + properties = {"format": format, "activity_threshold": activity_threshold, "edge_threshold": edge_threshold, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = network_analysis_visualizer.apply(network_analysis, variant=variant, parameters=properties) return network_analysis_visualizer.save(gviz, file_path) -def view_transition_system(transition_system: TransitionSystem, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white"): +def view_transition_system(transition_system: TransitionSystem, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", graph_title: Optional[str] = None): """ Views a transition system :param transition_system: Transition system :param format: Format of the visualization (if html is provided, GraphvizJS is used to render the visualization in an HTML page) :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1094,17 +1266,24 @@ def view_transition_system(transition_system: TransitionSystem, format: str = co format = str(format).lower() from pm4py.visualization.transition_system import visualizer as ts_visualizer - gviz = ts_visualizer.apply(transition_system, parameters={"format": format, "bgcolor": bgcolor}) + properties = {"format": format, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = ts_visualizer.apply(transition_system, parameters=properties) ts_visualizer.view(gviz) -def save_vis_transition_system(transition_system: TransitionSystem, file_path: str, bgcolor: str = "white", **kwargs): +def save_vis_transition_system(transition_system: TransitionSystem, file_path: str, bgcolor: str = "white", graph_title: Optional[str] = None, **kwargs): """ Persists the visualization of a transition system :param transition_system: Transition system :param file_path: Destination path :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1116,17 +1295,24 @@ def save_vis_transition_system(transition_system: TransitionSystem, file_path: s file_path = str(file_path) format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.transition_system import visualizer as ts_visualizer - gviz = ts_visualizer.apply(transition_system, parameters={"format": format, "bgcolor": bgcolor}) + properties = {"format": format, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = ts_visualizer.apply(transition_system, parameters=properties) return ts_visualizer.save(gviz, file_path) -def view_prefix_tree(trie: Trie, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white"): +def view_prefix_tree(trie: Trie, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", graph_title: Optional[str] = None): """ Views a prefix tree :param prefix_tree: Prefix tree :param format: Format of the visualization (if html is provided, GraphvizJS is used to render the visualization in an HTML page) :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1138,17 +1324,24 @@ def view_prefix_tree(trie: Trie, format: str = constants.DEFAULT_FORMAT_GVIZ_VIE format = str(format).lower() from pm4py.visualization.trie import visualizer as trie_visualizer - gviz = trie_visualizer.apply(trie, parameters={"format": format, "bgcolor": bgcolor}) + properties = {"format": format, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = trie_visualizer.apply(trie, parameters=properties) trie_visualizer.view(gviz) -def save_vis_prefix_tree(trie: Trie, file_path: str, bgcolor: str = "white", **kwargs): +def save_vis_prefix_tree(trie: Trie, file_path: str, bgcolor: str = "white", graph_title: Optional[str] = None, **kwargs): """ Persists the visualization of a prefix tree :param prefix_tree: Prefix tree :param file_path: Destination path :param bgcolor: Background color of the visualization (default: white) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1160,18 +1353,24 @@ def save_vis_prefix_tree(trie: Trie, file_path: str, bgcolor: str = "white", **k file_path = str(file_path) format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.trie import visualizer as trie_visualizer - gviz = trie_visualizer.apply(trie, parameters={"format": format, "bgcolor": bgcolor}) + properties = {"format": format, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = trie_visualizer.apply(trie, parameters=properties) return trie_visualizer.save(gviz, file_path) -def view_alignments(log: Union[EventLog, pd.DataFrame], aligned_traces: List[Dict[str, Any]], format: str = "png"): +def view_alignments(log: Union[EventLog, pd.DataFrame], aligned_traces: List[Dict[str, Any]], format: str = "png", graph_title: Optional[str] = None): """ Views the alignment table as a figure :param log: event log :param aligned_traces: results of an alignment :param format: format of the visualization (default: png) - + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1185,17 +1384,24 @@ def view_alignments(log: Union[EventLog, pd.DataFrame], aligned_traces: List[Dic format = str(format).lower() from pm4py.visualization.align_table import visualizer - gviz = visualizer.apply(log, aligned_traces, parameters={"format": format}) + properties = {"format": format} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = visualizer.apply(log, aligned_traces, parameters=properties) visualizer.view(gviz) -def save_vis_alignments(log: Union[EventLog, pd.DataFrame], aligned_traces: List[Dict[str, Any]], file_path: str, **kwargs): +def save_vis_alignments(log: Union[EventLog, pd.DataFrame], aligned_traces: List[Dict[str, Any]], file_path: str, graph_title: Optional[str] = None, **kwargs): """ Saves an alignment table's figure in the disk :param log: event log :param aligned_traces: results of an alignment :param file_path: target path in the disk + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1209,16 +1415,23 @@ def save_vis_alignments(log: Union[EventLog, pd.DataFrame], aligned_traces: List file_path = str(file_path) format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.align_table import visualizer - gviz = visualizer.apply(log, aligned_traces, parameters={"format": format}) + properties = {"format": format} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = visualizer.apply(log, aligned_traces, parameters=properties) return visualizer.save(gviz, file_path) -def view_footprints(footprints: Union[Tuple[Dict[str, Any], Dict[str, Any]], Dict[str, Any]], format: str = "png"): +def view_footprints(footprints: Union[Tuple[Dict[str, Any], Dict[str, Any]], Dict[str, Any]], format: str = "png", graph_title: Optional[str] = None): """ Views the footprints as a figure :param footprints: footprints :param format: format of the visualization (default: png) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1231,21 +1444,27 @@ def view_footprints(footprints: Union[Tuple[Dict[str, Any], Dict[str, Any]], Dic format = str(format).lower() from pm4py.visualization.footprints import visualizer as fps_visualizer + properties = {"format": format} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title if isinstance(footprints, dict): - gviz = fps_visualizer.apply(footprints, parameters={"format": format}) + gviz = fps_visualizer.apply(footprints, parameters=properties) else: - gviz = fps_visualizer.apply(footprints[0], footprints[1], variant=fps_visualizer.Variants.COMPARISON_SYMMETRIC, parameters={"format": format}) + gviz = fps_visualizer.apply(footprints[0], footprints[1], variant=fps_visualizer.Variants.COMPARISON_SYMMETRIC, parameters=properties) fps_visualizer.view(gviz) -def save_vis_footprints(footprints: Union[Tuple[Dict[str, Any], Dict[str, Any]], Dict[str, Any]], file_path: str, **kwargs): +def save_vis_footprints(footprints: Union[Tuple[Dict[str, Any], Dict[str, Any]], Dict[str, Any]], file_path: str, graph_title: Optional[str] = None, **kwargs): """ Saves the footprints' visualization on disk :param footprints: footprints :param file_path: target path of the visualization + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1259,16 +1478,21 @@ def save_vis_footprints(footprints: Union[Tuple[Dict[str, Any], Dict[str, Any]], format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.footprints import visualizer as fps_visualizer + properties = {"format": format} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title if isinstance(footprints, dict): - gviz = fps_visualizer.apply(footprints, parameters={"format": format}) + gviz = fps_visualizer.apply(footprints, parameters=properties) else: - gviz = fps_visualizer.apply(footprints[0], footprints[1], variant=fps_visualizer.Variants.COMPARISON_SYMMETRIC, parameters={"format": format}) + gviz = fps_visualizer.apply(footprints[0], footprints[1], variant=fps_visualizer.Variants.COMPARISON_SYMMETRIC, parameters=properties) return fps_visualizer.save(gviz, file_path) -def view_powl(powl: POWL, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", variant_str: str = "basic"): +def view_powl(powl: POWL, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", variant_str: str = "basic", graph_title: Optional[str] = None): """ Perform a visualization of a POWL model. @@ -1280,6 +1504,7 @@ def view_powl(powl: POWL, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgco :param bgcolor: background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) :param variant_str: variant of the visualization to be used (values: "basic", "net") + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1299,15 +1524,19 @@ def view_powl(powl: POWL, format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgco variant = POWLVisualizationVariants.NET format = str(format).lower() - parameters = parameters={"format": format, "bgcolor": bgcolor} + properties = {"format": format, "bgcolor": bgcolor} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title from pm4py.visualization.powl import visualizer as powl_visualizer - gviz = powl_visualizer.apply(powl, variant=variant, parameters=parameters) + gviz = powl_visualizer.apply(powl, variant=variant, parameters=properties) - powl_visualizer.view(gviz, parameters=parameters) + powl_visualizer.view(gviz, parameters=properties) -def save_vis_powl(powl: POWL, file_path: str, bgcolor: str = "white", rankdir: str = "TB", **kwargs): +def save_vis_powl(powl: POWL, file_path: str, bgcolor: str = "white", rankdir: str = "TB", graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of a POWL model. @@ -1318,6 +1547,7 @@ def save_vis_powl(powl: POWL, file_path: str, bgcolor: str = "white", rankdir: s :param file_path: target path of the visualization :param bgcolor: background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1329,15 +1559,19 @@ def save_vis_powl(powl: POWL, file_path: str, bgcolor: str = "white", rankdir: s """ file_path = str(file_path) format = os.path.splitext(file_path)[1][1:].lower() - parameters = {"format": format, "bgcolor": bgcolor, "rankdir": rankdir} + properties = {"format": format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title from pm4py.visualization.powl import visualizer as powl_visualizer - gviz = powl_visualizer.apply(powl, parameters=parameters) + gviz = powl_visualizer.apply(powl, parameters=properties) - return powl_visualizer.save(gviz, file_path, parameters=parameters) + return powl_visualizer.save(gviz, file_path, parameters=properties) -def view_object_graph(ocel: OCEL, graph: Set[Tuple[str, str]], format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ): +def view_object_graph(ocel: OCEL, graph: Set[Tuple[str, str]], format: str = constants.DEFAULT_FORMAT_GVIZ_VIEW, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None): """ Visualizes an object graph on the screen @@ -1346,6 +1580,7 @@ def view_object_graph(ocel: OCEL, graph: Set[Tuple[str, str]], format: str = con :param format: format of the visualization (if html is provided, GraphvizJS is used to render the visualization in an HTML page) :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1358,11 +1593,17 @@ def view_object_graph(ocel: OCEL, graph: Set[Tuple[str, str]], format: str = con format = str(format).lower() from pm4py.visualization.ocel.object_graph import visualizer as obj_graph_vis - gviz = obj_graph_vis.apply(ocel, graph, parameters={"format": format, "bgcolor": bgcolor, "rankdir": rankdir}) + properties = {"format": format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = obj_graph_vis.apply(ocel, graph, parameters=properties) obj_graph_vis.view(gviz) -def save_vis_object_graph(ocel: OCEL, graph: Set[Tuple[str, str]], file_path: str, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, **kwargs): +def save_vis_object_graph(ocel: OCEL, graph: Set[Tuple[str, str]], file_path: str, bgcolor: str = "white", rankdir: str = constants.DEFAULT_RANKDIR_GVIZ, graph_title: Optional[str] = None, **kwargs): """ Saves the visualization of an object graph @@ -1371,6 +1612,7 @@ def save_vis_object_graph(ocel: OCEL, graph: Set[Tuple[str, str]], file_path: st :param file_path: Destination path :param bgcolor: Background color of the visualization (default: white) :param rankdir: sets the direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + :param graph_title: Sets the title of the visualization (if provided) .. code-block:: python3 @@ -1383,5 +1625,11 @@ def save_vis_object_graph(ocel: OCEL, graph: Set[Tuple[str, str]], file_path: st file_path = str(file_path) format = os.path.splitext(file_path)[1][1:].lower() from pm4py.visualization.ocel.object_graph import visualizer as obj_graph_vis - gviz = obj_graph_vis.apply(ocel, graph, parameters={"format": format, "bgcolor": bgcolor, "rankdir": rankdir}) + properties = {"format": format, "bgcolor": bgcolor, "rankdir": rankdir} + properties["enable_graph_title"] = constants.DEFAULT_ENABLE_GRAPH_TITLES + if graph_title: + properties["enable_graph_title"] = True + properties["graph_title"] = graph_title + + gviz = obj_graph_vis.apply(ocel, graph, parameters=properties) return obj_graph_vis.save(gviz, file_path) diff --git a/pm4py/visualization/align_table/variants/classic.py b/pm4py/visualization/align_table/variants/classic.py index 477cbe20ea..54fd9ab3c0 100644 --- a/pm4py/visualization/align_table/variants/classic.py +++ b/pm4py/visualization/align_table/variants/classic.py @@ -23,7 +23,7 @@ import tempfile from pm4py.statistics.variants.log import get as variants_get -from pm4py.util import exec_utils +from pm4py.util import exec_utils, constants from enum import Enum from typing import Optional, Dict, Any from pm4py.objects.log.obj import EventLog @@ -33,6 +33,8 @@ class Parameters(Enum): FORMAT = "format" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def apply(log: EventLog, aligned_traces: typing.ListAlignments, parameters: Optional[Dict[Any, Any]] = None) -> graphviz.Source: @@ -64,8 +66,18 @@ def apply(log: EventLog, aligned_traces: typing.ListAlignments, parameters: Opti variants_idx_list = sorted(variants_idx_list, key=lambda x: len(x[1]), reverse=True) image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Alignments") + + table_alignments_list = ["digraph {\n"] + + if enable_graph_title: + table_alignments_list.append('label=<'+graph_title+'>;\nlabelloc="top";\n') + + table_alignments_list.append("tbl [\n") + table_alignments_list.append("shape=plaintext\n") + table_alignments_list.append("label=<\n") - table_alignments_list = ["digraph {\n", "tbl [\n", "shape=plaintext\n", "label=<\n"] table_alignments_list.append("\n") table_alignments_list.append("\n") @@ -100,6 +112,7 @@ def apply(log: EventLog, aligned_traces: typing.ListAlignments, parameters: Opti filename.close() gviz = Source(table_alignments, filename=filename.name) + gviz.format = image_format return gviz diff --git a/pm4py/visualization/dfg/util/dfg_gviz.py b/pm4py/visualization/dfg/util/dfg_gviz.py index 1fe3218efc..9cdbcb2519 100644 --- a/pm4py/visualization/dfg/util/dfg_gviz.py +++ b/pm4py/visualization/dfg/util/dfg_gviz.py @@ -22,9 +22,12 @@ import tempfile from copy import copy +import sys from graphviz import Digraph from pm4py.util import constants +from typing import Dict, List, Tuple +from collections import defaultdict, deque from pm4py.visualization.common.utils import * @@ -140,7 +143,8 @@ def assign_penwidth_edges(dfg): def graphviz_visualization(activities_count, dfg, image_format="png", measure="frequency", max_no_of_edges_in_diagram=100000, start_activities=None, end_activities=None, serv_time=None, - font_size="12", bgcolor=constants.DEFAULT_BGCOLOR, rankdir=constants.DEFAULT_RANKDIR_GVIZ): + font_size="12", bgcolor=constants.DEFAULT_BGCOLOR, rankdir=constants.DEFAULT_RANKDIR_GVIZ, + enable_graph_title: bool = constants.DEFAULT_ENABLE_GRAPH_TITLES, graph_title: str = "Directly-Follows Graph"): """ Do GraphViz visualization of a DFG graph @@ -168,6 +172,10 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f Background color of the visualization (i.e., 'transparent', 'white', ...) rankdir Direction of the graph ("LR" for left-to-right; "TB" for top-to-bottom) + enable_graph_title + Enables the visualization of a graph's title + graph_title + Graph title to display (if enable_graph_title) Returns ----------- @@ -184,6 +192,9 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f viz = Digraph("", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor, 'rankdir': rankdir}) + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + # first, remove edges in diagram that exceeds the maximum number of edges in the diagram dfg_key_value_list = [] for edge in dfg: @@ -198,9 +209,6 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f if edge not in dfg_allowed_keys: del dfg[edge] - # calculate edges penwidth - penwidth = assign_penwidth_edges(dfg) - activities_count_int = copy(activities_count) activities_in_dfg = set(activities_count) @@ -220,6 +228,22 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f # take unique elements as a list not as a set (in this way, nodes are added in the same order to the graph) activities_to_include = sorted(list(set(activities_in_dfg))) + start_activities_to_include = [act for act in start_activities if act in activities_to_include] + end_activities_to_include = [act for act in end_activities if act in activities_to_include] + + # calculate edges penwidth + ext_dfg = copy(dfg) + if start_activities_to_include is not None and start_activities_to_include: + for sact in start_activities_to_include: + ext_dfg[(constants.DEFAULT_ARTIFICIAL_START_ACTIVITY, sact)] = start_activities[sact] + if end_activities_to_include is not None and end_activities_to_include: + for eact in end_activities_to_include: + ext_dfg[(eact, constants.DEFAULT_ARTIFICIAL_END_ACTIVITY)] = end_activities[eact] + + penwidth = assign_penwidth_edges(ext_dfg) + + dfg_edges = sorted(list(dfg.keys())) + activities_map = {} for act in activities_to_include: @@ -235,9 +259,6 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f viz.node(str(hash(act)), act, fontsize=font_size) activities_map[act] = str(hash(act)) - # make edges addition always in the same order - dfg_edges = sorted(list(dfg.keys())) - # represent edges for edge in dfg_edges: if "frequency" in measure or "cost" in measure: @@ -246,21 +267,18 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f label = human_readable_stat(dfg[edge]) viz.edge(str(hash(edge[0])), str(hash(edge[1])), label=label, penwidth=str(penwidth[edge]), fontsize=font_size) - start_activities_to_include = [act for act in start_activities if act in activities_map] - end_activities_to_include = [act for act in end_activities if act in activities_map] - if start_activities_to_include: viz.node("@@startnode", "<●>", shape='circle', fontsize="34") for act in start_activities_to_include: label = str(start_activities[act]) if isinstance(start_activities, dict) and measure == "frequency" else "" - viz.edge("@@startnode", activities_map[act], label=label, fontsize=font_size) + viz.edge("@@startnode", activities_map[act], label=label, fontsize=font_size, penwidth=str(penwidth[(constants.DEFAULT_ARTIFICIAL_START_ACTIVITY, act)])) if end_activities_to_include: # <■> viz.node("@@endnode", "<■>", shape='doublecircle', fontsize="32") for act in end_activities_to_include: label = str(end_activities[act]) if isinstance(end_activities, dict) and measure == "frequency" else "" - viz.edge(activities_map[act], "@@endnode", label=label, fontsize=font_size) + viz.edge(activities_map[act], "@@endnode", label=label, fontsize=font_size, penwidth=str(penwidth[(act, constants.DEFAULT_ARTIFICIAL_END_ACTIVITY)])) viz.attr(overlap='false') viz.attr(fontsize='11') diff --git a/pm4py/visualization/dfg/variants/cost.py b/pm4py/visualization/dfg/variants/cost.py index 96e6610201..d1442606f3 100644 --- a/pm4py/visualization/dfg/variants/cost.py +++ b/pm4py/visualization/dfg/variants/cost.py @@ -49,6 +49,8 @@ class Parameters(Enum): AGGREGATION_MEASURE = "aggregation_measure" RANKDIR = "rankdir" BGCOLOR = "bgcolor" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Optional[Dict[Any, Any]] = None, @@ -89,6 +91,8 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, constants.DEFAULT_RANKDIR_GVIZ) bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Cost-Based Directly-Follows Graph") # if all the aggregation measures are provided for a given key, # then pick one of the values for the representation @@ -126,4 +130,5 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt return dfg_gviz.graphviz_visualization(activities_count, dfg, image_format=image_format, measure="cost", max_no_of_edges_in_diagram=max_no_of_edges_in_diagram, start_activities=start_activities, end_activities=end_activities, serv_time=serv_time, - font_size=font_size, bgcolor=bgcolor, rankdir=rankdir) + font_size=font_size, bgcolor=bgcolor, rankdir=rankdir, + enable_graph_title=enable_graph_title, graph_title=graph_title) diff --git a/pm4py/visualization/dfg/variants/frequency.py b/pm4py/visualization/dfg/variants/frequency.py index a23fd8dc3c..fa1b07846f 100644 --- a/pm4py/visualization/dfg/variants/frequency.py +++ b/pm4py/visualization/dfg/variants/frequency.py @@ -45,7 +45,8 @@ class Parameters(Enum): FONT_SIZE = "font_size" RANKDIR = "rankdir" BGCOLOR = "bgcolor" - STAT_LOCALE = "stat_locale" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Optional[Dict[Any, Any]] = None, activities_count : Dict[str, int] = None, serv_time: Dict[str, float] = None) -> graphviz.Digraph: @@ -89,7 +90,8 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, constants.DEFAULT_RANKDIR_GVIZ) bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) - stat_locale = exec_utils.get_param_value(Parameters.STAT_LOCALE, parameters, {}) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Frequency Directly-Follows Graph") if activities_count is None: if log is not None: @@ -114,4 +116,5 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt return dfg_gviz.graphviz_visualization(activities_count, dfg, image_format=image_format, measure="frequency", max_no_of_edges_in_diagram=max_no_of_edges_in_diagram, start_activities=start_activities, end_activities=end_activities, serv_time=serv_time, - font_size=font_size, bgcolor=bgcolor, rankdir=rankdir) + font_size=font_size, bgcolor=bgcolor, rankdir=rankdir, + enable_graph_title=enable_graph_title, graph_title=graph_title) diff --git a/pm4py/visualization/dfg/variants/performance.py b/pm4py/visualization/dfg/variants/performance.py index 571a18f3d6..69b3c79925 100644 --- a/pm4py/visualization/dfg/variants/performance.py +++ b/pm4py/visualization/dfg/variants/performance.py @@ -49,7 +49,9 @@ class Parameters(Enum): AGGREGATION_MEASURE = "aggregation_measure" RANKDIR = "rankdir" BGCOLOR = "bgcolor" - STAT_LOCALE = "stat_locale" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" + def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Optional[Dict[Any, Any]] = None, activities_count : Dict[str, int] = None, serv_time: Dict[str, float] = None) -> graphviz.Digraph: """ @@ -88,7 +90,8 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, constants.DEFAULT_RANKDIR_GVIZ) bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) - stat_locale = exec_utils.get_param_value(Parameters.STAT_LOCALE, parameters, {}) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Performance Directly-Follows Graph") # if all the aggregation measures are provided for a given key, # then pick one of the values for the representation @@ -126,4 +129,5 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt return dfg_gviz.graphviz_visualization(activities_count, dfg, image_format=image_format, measure="performance", max_no_of_edges_in_diagram=max_no_of_edges_in_diagram, start_activities=start_activities, end_activities=end_activities, serv_time=serv_time, - font_size=font_size, bgcolor=bgcolor, rankdir=rankdir) + font_size=font_size, bgcolor=bgcolor, rankdir=rankdir, + enable_graph_title=enable_graph_title, graph_title=graph_title) diff --git a/pm4py/visualization/dotted_chart/variants/classic.py b/pm4py/visualization/dotted_chart/variants/classic.py index 4d990062fa..235dc0084a 100644 --- a/pm4py/visualization/dotted_chart/variants/classic.py +++ b/pm4py/visualization/dotted_chart/variants/classic.py @@ -27,7 +27,7 @@ from random import randint from typing import List, Any, Tuple, Dict, Optional, Union -from pm4py.util import exec_utils +from pm4py.util import exec_utils, constants class Parameters(Enum): @@ -35,6 +35,8 @@ class Parameters(Enum): DOT_SIZE = "dot_size" LAYOUT_EXT_MULTIPLIER = "layout_ext_multiplier" SHOW_LEGEND = "show_legend" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def __build_unique_values(points_list: List[Any]) -> List[Any]: @@ -154,6 +156,9 @@ def apply(points_list: List[Any], attributes: List[str], parameters: Optional[Di layout_ext_multiplier = exec_utils.get_param_value(Parameters.LAYOUT_EXT_MULTIPLIER, parameters, 50) show_legend = exec_utils.get_param_value(Parameters.SHOW_LEGEND, parameters, True) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Dotted Chart") + unique_values = __build_unique_values(points_list) corr_dict, attr_type = __build_corr_dict(unique_values) color_dict = __build_color_dict(unique_values[2]) if len(attributes) == 3 else None @@ -174,6 +179,10 @@ def apply(points_list: List[Any], attributes: List[str], parameters: Optional[Di output_file_img.close() lines = ["graph G {"] + + if enable_graph_title: + lines.append('label=<'+graph_title+'>;\nlabelloc="top";\n') + lines.append("origin [label=\"\", shape=none, width=\"0px\", height=\"0px\", pos=\"0,0!\"];") lines.append("rightX [label=\"\", shape=none, width=\"0px\", height=\"0px\", pos=\"%d,0!\"];" % (x_length)) lines.append("topY [label=\"\", shape=none, width=\"0px\", height=\"0px\", pos=\"0,%d!\"];" % (y_length)) diff --git a/pm4py/visualization/footprints/variants/comparison.py b/pm4py/visualization/footprints/variants/comparison.py index 8ce8634625..520d3e46c4 100644 --- a/pm4py/visualization/footprints/variants/comparison.py +++ b/pm4py/visualization/footprints/variants/comparison.py @@ -21,13 +21,15 @@ ''' from graphviz import Source import tempfile -from pm4py.util import exec_utils +from pm4py.util import exec_utils, constants from enum import Enum from typing import Optional, Dict, Any, Union class Parameters(Enum): FORMAT = "format" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" XOR_SYMBOL = "#" @@ -85,11 +87,21 @@ def apply(fp1: Dict[str, Any], fp2: Dict[str, Any], parameters: Optional[Dict[Un fp_table[x[0]][x[1]] = ("red", PARALLEL_SYMBOL) image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Footprints") filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() - footprints_table = ["digraph {\n", "tbl [\n", "shape=plaintext\n", "label=<\n"] + footprints_table = ["digraph {\n"] + + if enable_graph_title: + footprints_table.append('label=<'+graph_title+'>;\nlabelloc="top";\n') + + footprints_table.append("tbl [\n") + footprints_table.append("shape=plaintext\n") + footprints_table.append("label=<\n") + footprints_table.append("
VariantAlignment
\n") footprints_table.append("") diff --git a/pm4py/visualization/footprints/variants/comparison_symmetric.py b/pm4py/visualization/footprints/variants/comparison_symmetric.py index b120f27d02..d1416052f4 100644 --- a/pm4py/visualization/footprints/variants/comparison_symmetric.py +++ b/pm4py/visualization/footprints/variants/comparison_symmetric.py @@ -21,13 +21,15 @@ ''' from graphviz import Source import tempfile -from pm4py.util import exec_utils +from pm4py.util import exec_utils, constants from enum import Enum from typing import Optional, Dict, Any, Union class Parameters(Enum): FORMAT = "format" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" UNKNOWN_SYMBOL = "?" @@ -70,11 +72,21 @@ def apply(fp1: Dict[str, Any], fp2: Dict[str, Any], parameters: Optional[Dict[Un fp_table = {} image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Footprints") filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() - footprints_table = ["digraph {\n", "tbl [\n", "shape=plaintext\n", "label=<\n"] + footprints_table = ["digraph {\n"] + + if enable_graph_title: + footprints_table.append('label=<'+graph_title+'>;\nlabelloc="top";\n') + + footprints_table.append("tbl [\n") + footprints_table.append("shape=plaintext\n") + footprints_table.append("label=<\n") + footprints_table.append("
\n") footprints_table.append("") diff --git a/pm4py/visualization/footprints/variants/single.py b/pm4py/visualization/footprints/variants/single.py index 236e98854b..53cf576bf6 100644 --- a/pm4py/visualization/footprints/variants/single.py +++ b/pm4py/visualization/footprints/variants/single.py @@ -21,13 +21,15 @@ ''' from graphviz import Source import tempfile -from pm4py.util import exec_utils +from pm4py.util import exec_utils, constants from enum import Enum from typing import Optional, Dict, Any, Union class Parameters(Enum): FORMAT = "format" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" XOR_SYMBOL = "#" @@ -77,11 +79,21 @@ def apply(fp: Dict[str, Any], parameters: Optional[Dict[Union[str, Parameters], fp_table[x[0]][x[1]] = PARALLEL_SYMBOL image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Footprints") filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() - footprints_table = ["digraph {\n", "tbl [\n", "shape=plaintext\n", "label=<\n"] + footprints_table = ["digraph {\n"] + + if enable_graph_title: + footprints_table.append('label=<'+graph_title+'>;\nlabelloc="top";\n') + + footprints_table.append("tbl [\n") + footprints_table.append("shape=plaintext\n") + footprints_table.append("label=<\n") + footprints_table.append("
\n") footprints_table.append("") for act in activities: diff --git a/pm4py/visualization/heuristics_net/variants/pydotplus_vis.py b/pm4py/visualization/heuristics_net/variants/pydotplus_vis.py index 3275ce1a02..c931166bf0 100644 --- a/pm4py/visualization/heuristics_net/variants/pydotplus_vis.py +++ b/pm4py/visualization/heuristics_net/variants/pydotplus_vis.py @@ -36,6 +36,8 @@ class Parameters(Enum): FORMAT = "format" BGCOLOR = "bgcolor" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def get_corr_hex(num): @@ -143,11 +145,20 @@ def get_graph(heu_net: HeuristicsNet, parameters: Optional[Dict[Union[str, Param if parameters is None: parameters = {} + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Heuristics Net") + bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) graph = pydotplus.Dot(strict=True) graph.obj_dict['attributes']['bgcolor'] = bgcolor graph.set_bgcolor(bgcolor) + if enable_graph_title: + graph.set_label(graph_title) + graph.set_labelloc("top") + graph.set_labeljust("center") + graph.set_fontsize(20) + corr_nodes = {} corr_nodes_names = {} is_frequency = False diff --git a/pm4py/visualization/network_analysis/variants/frequency.py b/pm4py/visualization/network_analysis/variants/frequency.py index 8bfaf60a94..df2e0eaa40 100644 --- a/pm4py/visualization/network_analysis/variants/frequency.py +++ b/pm4py/visualization/network_analysis/variants/frequency.py @@ -34,6 +34,8 @@ class Parameters(Enum): BGCOLOR = "bgcolor" ACTIVITY_THRESHOLD = "activity_threshold" EDGE_THRESHOLD = "edge_threshold" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def apply(network_analysis_edges: Dict[Tuple[str, str], Dict[str, int]], parameters: Optional[Dict[Any, Any]] = None) -> Digraph: @@ -64,12 +66,18 @@ def apply(network_analysis_edges: Dict[Tuple[str, str], Dict[str, int]], paramet activity_threshold = exec_utils.get_param_value(Parameters.ACTIVITY_THRESHOLD, parameters, 1) edge_threshold = exec_utils.get_param_value(Parameters.EDGE_THRESHOLD, parameters, 1) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Network Analysis") + filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() viz = Digraph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + nodes = set(x[0] for x in network_analysis_edges).union(set(x[1] for x in network_analysis_edges)) nodes_in_degree = {x: 0 for x in nodes} nodes_out_degree = {x: 0 for x in nodes} diff --git a/pm4py/visualization/network_analysis/variants/performance.py b/pm4py/visualization/network_analysis/variants/performance.py index ecc811b6b9..ce654e4a23 100644 --- a/pm4py/visualization/network_analysis/variants/performance.py +++ b/pm4py/visualization/network_analysis/variants/performance.py @@ -35,6 +35,8 @@ class Parameters(Enum): ACTIVITY_THRESHOLD = "activity_threshold" EDGE_THRESHOLD = "edge_threshold" AGGREGATION_MEASURE = "aggregation_measure" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def apply(network_analysis_edges0: Dict[Tuple[str, str], Dict[str, Any]], parameters: Optional[Dict[Any, Any]] = None) -> Digraph: @@ -82,12 +84,18 @@ def apply(network_analysis_edges0: Dict[Tuple[str, str], Dict[str, Any]], parame elif aggregation_measure == "sum": aggregation_f = sum + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Network Analysis") + filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() viz = Digraph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + network_analysis_edges = {} network_analysis_edges_agg_performance = {} for x in network_analysis_edges0: diff --git a/pm4py/visualization/ocel/object_graph/variants/graphviz.py b/pm4py/visualization/ocel/object_graph/variants/graphviz.py index 9e84386458..e1046c9c2c 100644 --- a/pm4py/visualization/ocel/object_graph/variants/graphviz.py +++ b/pm4py/visualization/ocel/object_graph/variants/graphviz.py @@ -34,6 +34,8 @@ class Parameters(Enum): BGCOLOR = "bgcolor" RANKDIR = "rankdir" DIRECTED = "directed" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def ot_to_color(ot: str) -> str: @@ -76,6 +78,9 @@ def apply(ocel: OCEL, graph: Set[Tuple[str, str]], parameters: Optional[Dict[Any rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, constants.DEFAULT_RANKDIR_GVIZ) directed = exec_utils.get_param_value(Parameters.DIRECTED, parameters, True) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Object-Centric Graph") + filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() @@ -85,6 +90,9 @@ def apply(ocel: OCEL, graph: Set[Tuple[str, str]], parameters: Optional[Dict[Any viz = Graph("ograph", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + ob_type = ocel.objects.groupby(ocel.object_id_column).first()[ocel.object_type_column].to_dict() nodes = set(x[0] for x in graph).union(set(x[1] for x in graph)) diff --git a/pm4py/visualization/ocel/ocdfg/variants/classic.py b/pm4py/visualization/ocel/ocdfg/variants/classic.py index 1a7636c0ff..a3fbeca765 100644 --- a/pm4py/visualization/ocel/ocdfg/variants/classic.py +++ b/pm4py/visualization/ocel/ocdfg/variants/classic.py @@ -38,6 +38,8 @@ class Parameters(Enum): EDGE_THRESHOLD = "edge_threshold" ANNOTATION = "annotation" PERFORMANCE_AGGREGATION_MEASURE = "aggregationMeasure" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def ot_to_color(ot: str) -> str: @@ -191,6 +193,8 @@ def apply(ocdfg: Dict[str, Any], parameters: Optional[Dict[Any, Any]] = None) -> annotation = exec_utils.get_param_value(Parameters.ANNOTATION, parameters, "frequency") performance_aggregation_measure = exec_utils.get_param_value(Parameters.PERFORMANCE_AGGREGATION_MEASURE, parameters, "mean") + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Object-Centric DFG") act_count = {} act_ot_count = {} @@ -241,6 +245,9 @@ def apply(ocdfg: Dict[str, Any], parameters: Optional[Dict[Any, Any]] = None) -> viz = Digraph("ocdfg", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + min_edges_count = {} max_edges_count = {} diff --git a/pm4py/visualization/ocel/ocpn/variants/wo_decoration.py b/pm4py/visualization/ocel/ocpn/variants/wo_decoration.py index 0622b5515f..33ea3a3a93 100644 --- a/pm4py/visualization/ocel/ocpn/variants/wo_decoration.py +++ b/pm4py/visualization/ocel/ocpn/variants/wo_decoration.py @@ -33,6 +33,8 @@ class Parameters(Enum): FORMAT = "format" BGCOLOR = "bgcolor" RANKDIR = "rankdir" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def ot_to_color(ot: str) -> str: @@ -75,12 +77,18 @@ def apply(ocpn: Dict[str, Any], parameters: Optional[Dict[Any, Any]] = None) -> bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, constants.DEFAULT_RANKDIR_GVIZ) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Object-Centric Petri net") + filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() - viz = Digraph("ocdfg", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) + viz = Digraph("ocpn", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + activities_map = {} transition_map = {} places = {} diff --git a/pm4py/visualization/performance_spectrum/variants/neato.py b/pm4py/visualization/performance_spectrum/variants/neato.py index b7f5df38b1..b515c3b971 100644 --- a/pm4py/visualization/performance_spectrum/variants/neato.py +++ b/pm4py/visualization/performance_spectrum/variants/neato.py @@ -30,7 +30,7 @@ import matplotlib as mpl import matplotlib.cm as cm -from pm4py.util import exec_utils +from pm4py.util import exec_utils, constants from pm4py.util.dt_parsing.variants import strpfromiso from pm4py.util.colors import get_string_from_int_below_255 @@ -43,6 +43,8 @@ class Parameters(Enum): N_DIV_DATES = "n_div_dates" PERC_PATHS = "perc_paths" LAYOUT_EXT_MULTIPLIER = "layout_ext_multiplier" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def give_color_to_line(dir: float) -> str: @@ -106,6 +108,9 @@ def apply(perf_spectrum: Dict[str, Any], parameters: Optional[Dict[Union[str, Pa perc_paths = exec_utils.get_param_value(Parameters.PERC_PATHS, parameters, 1.0) layout_ext_multiplier = exec_utils.get_param_value(Parameters.LAYOUT_EXT_MULTIPLIER, parameters, 100) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Performance Spectrum") + output_file_gv = tempfile.NamedTemporaryFile(suffix=".gv") output_file_gv.close() @@ -115,6 +120,9 @@ def apply(perf_spectrum: Dict[str, Any], parameters: Optional[Dict[Union[str, Pa lines = [] lines.append("graph G {") + if enable_graph_title: + lines.append('label=<'+graph_title+'>;\nlabelloc="top";\n') + min_x = min(x[0] for x in perf_spectrum["points"]) max_x = max(x[-1] for x in perf_spectrum["points"]) all_diffs = [x[i + 1] - x[i] for x in perf_spectrum["points"] for i in range(len(x) - 1)] diff --git a/pm4py/visualization/petri_net/common/visualize.py b/pm4py/visualization/petri_net/common/visualize.py index 98921a8317..80d420d3cc 100644 --- a/pm4py/visualization/petri_net/common/visualize.py +++ b/pm4py/visualization/petri_net/common/visualize.py @@ -23,10 +23,12 @@ from graphviz import Digraph -from pm4py.objects.petri_net.obj import Marking +from pm4py.objects.petri_net.obj import Marking, PetriNet from pm4py.objects.petri_net import properties as petri_properties from pm4py.util import exec_utils, constants from enum import Enum +from typing import List, Tuple, Dict +from collections import defaultdict, deque from pm4py.util.constants import PARAMETER_CONSTANT_ACTIVITY_KEY, PARAMETER_CONSTANT_TIMESTAMP_KEY @@ -40,6 +42,8 @@ class Parameters(Enum): FONT_SIZE = "font_size" BGCOLOR = "bgcolor" DECORATIONS = "decorations" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def apply(net, initial_marking, final_marking, decorations=None, parameters=None): @@ -73,17 +77,21 @@ def apply(net, initial_marking, final_marking, decorations=None, parameters=None set_rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, None) font_size = exec_utils.get_param_value(Parameters.FONT_SIZE, parameters, "12") bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Petri Net") if decorations is None: decorations = exec_utils.get_param_value(Parameters.DECORATIONS, parameters, None) return graphviz_visualization(net, image_format=image_format, initial_marking=initial_marking, final_marking=final_marking, decorations=decorations, debug=debug, - set_rankdir=set_rankdir, font_size=font_size, bgcolor=bgcolor) + set_rankdir=set_rankdir, font_size=font_size, bgcolor=bgcolor, + enable_graph_title=enable_graph_title, graph_title=graph_title) def graphviz_visualization(net, image_format="png", initial_marking=None, final_marking=None, decorations=None, - debug=False, set_rankdir=None, font_size="12", bgcolor=constants.DEFAULT_BGCOLOR): + debug=False, set_rankdir=None, font_size="12", bgcolor=constants.DEFAULT_BGCOLOR, + enable_graph_title: bool = constants.DEFAULT_ENABLE_GRAPH_TITLES, graph_title: str = "Petri Net"): """ Provides visualization for the petrinet @@ -103,6 +111,10 @@ def graphviz_visualization(net, image_format="png", initial_marking=None, final_ Enables debug mode set_rankdir Sets the rankdir to LR (horizontal layout) + enable_graph_title + Enables the visualization of a graph's title + graph_title + Graph title to display (if enable_graph_title) Returns ------- @@ -127,6 +139,9 @@ def graphviz_visualization(net, image_format="png", initial_marking=None, final_ else: viz.graph_attr['rankdir'] = 'LR' + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + # transitions viz.attr('node', shape='box') for t in net.transitions: diff --git a/pm4py/visualization/powl/variants/basic.py b/pm4py/visualization/powl/variants/basic.py index 6bd9a8eb3f..5976c74493 100644 --- a/pm4py/visualization/powl/variants/basic.py +++ b/pm4py/visualization/powl/variants/basic.py @@ -24,7 +24,8 @@ from enum import Enum from graphviz import Digraph from pm4py.objects.process_tree.obj import Operator -from pm4py.util import exec_utils +from pm4py.util import exec_utils, constants +from typing import Optional, Dict, Any from pm4py.objects.powl.obj import POWL, Transition, SilentTransition, StrictPartialOrder, OperatorPOWL, \ FrequentTransition @@ -43,9 +44,11 @@ class Parameters(Enum): ENABLE_DEEPCOPY = "enable_deepcopy" FONT_SIZE = "font_size" BGCOLOR = "bgcolor" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" -def apply(powl: POWL) -> Digraph: +def apply(powl: POWL, parameters: Optional[Dict[Any, Any]] = None) -> Digraph: """ Obtain a POWL model representation through GraphViz @@ -59,6 +62,11 @@ def apply(powl: POWL) -> Digraph: gviz GraphViz Digraph """ + if parameters is None: + parameters = {} + + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "POWL model") filename = tempfile.NamedTemporaryFile(suffix='.gv') diff --git a/pm4py/visualization/powl/variants/net.py b/pm4py/visualization/powl/variants/net.py index 2c2f241052..8796646aa2 100644 --- a/pm4py/visualization/powl/variants/net.py +++ b/pm4py/visualization/powl/variants/net.py @@ -26,8 +26,9 @@ from enum import Enum import tempfile import graphviz +from typing import Optional, Dict, Any from graphviz import Digraph -from pm4py.util import constants +from pm4py.util import constants, exec_utils from pm4py.objects.bpmn.obj import BPMN from pm4py.objects.bpmn.util.sorting import get_sorted_nodes_edges from pm4py.objects.conversion.powl.converter import apply as powl_to_pn @@ -38,6 +39,8 @@ class Parameters(Enum): FORMAT = "format" RANKDIR = "rankdir" BGCOLOR = "bgcolor" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" FREQUENCY_TAG_IMAGES = True @@ -156,7 +159,13 @@ def add_node(n, viz): raise Exception("Unexpected instance of class " + str(type(n)) + "!") -def apply(powl: POWL) -> graphviz.Digraph: +def apply(powl: POWL, parameters: Optional[Dict[Any, Any]] = None) -> graphviz.Digraph: + if parameters is None: + parameters = {} + + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "POWL Model") + pn_2, init_2, final_2 = powl_to_pn(powl) bpmn_graph = to_bpmn(pn_2, init_2, final_2) @@ -186,6 +195,9 @@ def apply(powl: POWL) -> graphviz.Digraph: viz = Digraph("", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) viz.graph_attr['rankdir'] = rankdir + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + gateway_edges = {} for e in edges: diff --git a/pm4py/visualization/powl/visualizer.py b/pm4py/visualization/powl/visualizer.py index e10b49cd3d..38c0cfe4c8 100644 --- a/pm4py/visualization/powl/visualizer.py +++ b/pm4py/visualization/powl/visualizer.py @@ -77,7 +77,7 @@ def apply(powl: POWL, variant=DEFAULT_VARIANT, frequency_tags=True, parameters: if frequency_tags: powl = powl.simplify_using_frequent_transitions() - viz = exec_utils.get_variant(variant).apply(powl) + viz = exec_utils.get_variant(variant).apply(powl, parameters=parameters) svg_content = viz.pipe().decode('utf-8') def inline_images_and_svgs(svg_content): diff --git a/pm4py/visualization/process_tree/variants/frequency_annotation.py b/pm4py/visualization/process_tree/variants/frequency_annotation.py index cc4079a4ed..616009eec2 100644 --- a/pm4py/visualization/process_tree/variants/frequency_annotation.py +++ b/pm4py/visualization/process_tree/variants/frequency_annotation.py @@ -41,6 +41,8 @@ class Parameters(Enum): RANKDIR = "rankdir" NUM_EVENTS_PROPERTY = "num_events_property" NUM_CASES_PROPERTY = "num_cases_property" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" # maps the operators to the ProM strings @@ -109,10 +111,17 @@ def apply(tree: ProcessTree, parameters: Optional[Dict[Union[str, Parameters], A bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, constants.DEFAULT_RANKDIR_GVIZ) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Process Tree") + font_size = exec_utils.get_param_value(Parameters.FONT_SIZE, parameters, 15) + font_size = str(font_size) viz = Graph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor, "rankdir": rankdir}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") enable_deepcopy = exec_utils.get_param_value(Parameters.ENABLE_DEEPCOPY, parameters, False) diff --git a/pm4py/visualization/process_tree/variants/symbolic.py b/pm4py/visualization/process_tree/variants/symbolic.py index 03d42b2a9c..0598a0ff29 100644 --- a/pm4py/visualization/process_tree/variants/symbolic.py +++ b/pm4py/visualization/process_tree/variants/symbolic.py @@ -39,6 +39,8 @@ class Parameters(Enum): FONT_SIZE = "font_size" BGCOLOR = "bgcolor" RANKDIR = "rankdir" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def get_color(node, color_map): @@ -105,10 +107,17 @@ def apply(tree: ProcessTree, parameters: Optional[Dict[Union[str, Parameters], A bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, constants.DEFAULT_RANKDIR_GVIZ) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Process Tree") + font_size = exec_utils.get_param_value(Parameters.FONT_SIZE, parameters, 15) + font_size = str(font_size) viz = Graph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor, "rankdir": rankdir}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") color_map = exec_utils.get_param_value(Parameters.COLOR_MAP, parameters, {}) diff --git a/pm4py/visualization/process_tree/variants/wo_decoration.py b/pm4py/visualization/process_tree/variants/wo_decoration.py index ea240df1b2..fe7bf57966 100644 --- a/pm4py/visualization/process_tree/variants/wo_decoration.py +++ b/pm4py/visualization/process_tree/variants/wo_decoration.py @@ -39,6 +39,8 @@ class Parameters(Enum): FONT_SIZE = "font_size" BGCOLOR = "bgcolor" RANKDIR = "rankdir" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" # maps the operators to the ProM strings @@ -108,10 +110,17 @@ def apply(tree: ProcessTree, parameters: Optional[Dict[Union[str, Parameters], A bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) rankdir = exec_utils.get_param_value(Parameters.RANKDIR, parameters, constants.DEFAULT_RANKDIR_GVIZ) + font_size = exec_utils.get_param_value(Parameters.FONT_SIZE, parameters, 15) + font_size = str(font_size) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Process Tree") viz = Graph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor, 'rankdir': rankdir}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") color_map = exec_utils.get_param_value(Parameters.COLOR_MAP, parameters, {}) diff --git a/pm4py/visualization/transition_system/util/visualize_graphviz.py b/pm4py/visualization/transition_system/util/visualize_graphviz.py index a7796944b8..605a4a36e8 100644 --- a/pm4py/visualization/transition_system/util/visualize_graphviz.py +++ b/pm4py/visualization/transition_system/util/visualize_graphviz.py @@ -35,6 +35,8 @@ class Parameters(Enum): FILLCOLORS = "fillcolors" FONT_SIZE = "font_size" BGCOLOR = "bgcolor" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def visualize(ts, parameters=None): @@ -49,6 +51,8 @@ def visualize(ts, parameters=None): font_size = exec_utils.get_param_value(Parameters.FONT_SIZE, parameters, 11) font_size = str(font_size) bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Transition System") for state in ts.states: state.label = state.name @@ -68,6 +72,9 @@ def visualize(ts, parameters=None): viz = Digraph(ts.name, filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + # states viz.attr('node') for s in ts.states: diff --git a/pm4py/visualization/transition_system/variants/trans_frequency.py b/pm4py/visualization/transition_system/variants/trans_frequency.py index 56ae5bf869..3a62b94e28 100644 --- a/pm4py/visualization/transition_system/variants/trans_frequency.py +++ b/pm4py/visualization/transition_system/variants/trans_frequency.py @@ -35,6 +35,8 @@ class Parameters(Enum): FORMAT = "format" BGCOLOR = "bgcolor" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def get_perc(total_events, arc_events): @@ -50,11 +52,17 @@ def apply(tsys: TransitionSystem, parameters: Optional[Dict[Union[str, Parameter image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Transition System") + filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() viz = Digraph(tsys.name, filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + states_dictio = {} for s in tsys.states: diff --git a/pm4py/visualization/transition_system/variants/view_based.py b/pm4py/visualization/transition_system/variants/view_based.py index 423b07bdf2..43152acb3b 100644 --- a/pm4py/visualization/transition_system/variants/view_based.py +++ b/pm4py/visualization/transition_system/variants/view_based.py @@ -33,6 +33,8 @@ class Parameters(Enum): FORCE_NAMES = "force_names" FILLCOLORS = "fillcolors" FONT_SIZE = "font_size" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def apply(tsys: TransitionSystem, parameters: Optional[Dict[Union[str, Parameters], Any]] = None) -> graphviz.Digraph: diff --git a/pm4py/visualization/trie/variants/classic.py b/pm4py/visualization/trie/variants/classic.py index 2bc1f8956d..429e4c31f7 100644 --- a/pm4py/visualization/trie/variants/classic.py +++ b/pm4py/visualization/trie/variants/classic.py @@ -32,6 +32,8 @@ class Parameters(Enum): FORMAT = "format" BGCOLOR = "bgcolor" + ENABLE_GRAPH_TITLE = "enable_graph_title" + GRAPH_TITLE = "graph_title" def draw_recursive(trie_node: Trie, parent: Union[str, None], gviz: Graph): @@ -79,12 +81,18 @@ def apply(trie: Trie, parameters: Optional[Dict[Union[str, Parameters], Any]] = image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png") bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, constants.DEFAULT_BGCOLOR) + enable_graph_title = exec_utils.get_param_value(Parameters.ENABLE_GRAPH_TITLE, parameters, constants.DEFAULT_ENABLE_GRAPH_TITLES) + graph_title = exec_utils.get_param_value(Parameters.GRAPH_TITLE, parameters, "Prefix Tree") + filename = tempfile.NamedTemporaryFile(suffix='.gv') filename.close() viz = Graph("pt", filename=filename.name, engine='dot', graph_attr={'bgcolor': bgcolor}) viz.attr('node', shape='ellipse', fixedsize='false') + if enable_graph_title: + viz.attr(label='<'+graph_title+'>', labelloc="top") + draw_recursive(trie, None, viz) viz.attr(overlap='false') diff --git a/safety_checks/20240322 b/safety_checks/20240322 new file mode 100644 index 0000000000..e4dd557af5 --- /dev/null +++ b/safety_checks/20240322 @@ -0,0 +1,47 @@ ++==============================================================================+ + + /$$$$$$ /$$ + /$$__ $$ | $$ + /$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$ + /$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$ + | $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$ + \____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$ + /$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$ + |_______/ \_______/|__/ \_______/ \___/ \____ $$ + /$$ | $$ + | $$$$$$/ + by safetycli.com \______/ + ++==============================================================================+ + + REPORT + + Safety is using PyUp's free open-source vulnerability database. This +data is 30 days old and limited. + For real-time enhanced vulnerability data, fix recommendations, severity +reporting, cybersecurity support, team and project policy management and more +sign up at https://pyup.io or email sales@pyup.io + + Safety v3.0.1 is scanning for Vulnerabilities... + Scanning dependencies in your files: + + -> requirements_stable.txt + + Using open-source vulnerability database + Found and scanned 24 packages + Timestamp 2024-03-22 07:25:58 + 0 vulnerabilities reported + 0 vulnerabilities ignored ++==============================================================================+ + + No known security vulnerabilities reported. + ++==============================================================================+ + + Safety is using PyUp's free open-source vulnerability database. This +data is 30 days old and limited. + For real-time enhanced vulnerability data, fix recommendations, severity +reporting, cybersecurity support, team and project policy management and more +sign up at https://pyup.io or email sales@pyup.io + ++==============================================================================+ diff --git a/safety_checks/20240327 b/safety_checks/20240327 new file mode 100644 index 0000000000..183420d018 --- /dev/null +++ b/safety_checks/20240327 @@ -0,0 +1,47 @@ ++==============================================================================+ + + /$$$$$$ /$$ + /$$__ $$ | $$ + /$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$ + /$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$ + | $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$ + \____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$ + /$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$ + |_______/ \_______/|__/ \_______/ \___/ \____ $$ + /$$ | $$ + | $$$$$$/ + by safetycli.com \______/ + ++==============================================================================+ + + REPORT + + Safety is using PyUp's free open-source vulnerability database. This +data is 30 days old and limited. + For real-time enhanced vulnerability data, fix recommendations, severity +reporting, cybersecurity support, team and project policy management and more +sign up at https://pyup.io or email sales@pyup.io + + Safety v3.0.1 is scanning for Vulnerabilities... + Scanning dependencies in your files: + + -> requirements_stable.txt + + Using open-source vulnerability database + Found and scanned 24 packages + Timestamp 2024-03-27 11:56:49 + 0 vulnerabilities reported + 0 vulnerabilities ignored ++==============================================================================+ + + No known security vulnerabilities reported. + ++==============================================================================+ + + Safety is using PyUp's free open-source vulnerability database. This +data is 30 days old and limited. + For real-time enhanced vulnerability data, fix recommendations, severity +reporting, cybersecurity support, team and project policy management and more +sign up at https://pyup.io or email sales@pyup.io + ++==============================================================================+ diff --git a/safety_checks/20240417 b/safety_checks/20240417 new file mode 100644 index 0000000000..5ebd008ad1 --- /dev/null +++ b/safety_checks/20240417 @@ -0,0 +1,47 @@ ++==============================================================================+ + + /$$$$$$ /$$ + /$$__ $$ | $$ + /$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$ + /$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$ + | $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$ + \____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$ + /$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$ + |_______/ \_______/|__/ \_______/ \___/ \____ $$ + /$$ | $$ + | $$$$$$/ + by safetycli.com \______/ + ++==============================================================================+ + + REPORT + + Safety is using PyUp's free open-source vulnerability database. This +data is 30 days old and limited. + For real-time enhanced vulnerability data, fix recommendations, severity +reporting, cybersecurity support, team and project policy management and more +sign up at https://pyup.io or email sales@pyup.io + + Safety v3.0.1 is scanning for Vulnerabilities... + Scanning dependencies in your files: + + -> requirements_stable.txt + + Using open-source vulnerability database + Found and scanned 26 packages + Timestamp 2024-04-17 08:08:26 + 0 vulnerabilities reported + 0 vulnerabilities ignored ++==============================================================================+ + + No known security vulnerabilities reported. + ++==============================================================================+ + + Safety is using PyUp's free open-source vulnerability database. This +data is 30 days old and limited. + For real-time enhanced vulnerability data, fix recommendations, severity +reporting, cybersecurity support, team and project policy management and more +sign up at https://pyup.io or email sales@pyup.io + ++==============================================================================+ diff --git a/safety_checks/20270524 b/safety_checks/20270524 new file mode 100644 index 0000000000..7d0483a704 Binary files /dev/null and b/safety_checks/20270524 differ