Skip to content

Commit

Permalink
Merge remote-tracking branch 'github/release' into release-github
Browse files Browse the repository at this point in the history
  • Loading branch information
fit-sebastiaan-van-zelst committed Oct 15, 2021
2 parents 5353e97 + 7680b65 commit bb78f51
Show file tree
Hide file tree
Showing 15 changed files with 94 additions and 62 deletions.
7 changes: 5 additions & 2 deletions pm4py/objects/petri/performance_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@ def find_min_max_arc_performance(statistics, aggregation_measure):
return min_performance, max_performance


def aggregate_statistics(statistics, measure="frequency", aggregation_measure=None):
def aggregate_statistics(statistics, measure="frequency", aggregation_measure=None,
stat_locale: dict = {}):
"""
Gets aggregated statistics
Expand All @@ -382,6 +383,8 @@ def aggregate_statistics(statistics, measure="frequency", aggregation_measure=No
Desidered view on data (frequency or performance)
aggregation_measure
Aggregation measure (e.g. mean, min) to use
stat_locale
Dict to locale the stat strings
Returns
----------
Expand All @@ -401,7 +404,7 @@ def aggregate_statistics(statistics, measure="frequency", aggregation_measure=No
elif measure == "performance":
if statistics[elem]["performance"]:
aggr_stat = aggregate_stats(statistics, elem, aggregation_measure)
aggr_stat_hr = human_readable_stat(aggr_stat)
aggr_stat_hr = human_readable_stat(aggr_stat, stat_locale)
arc_penwidth = get_arc_penwidth(aggr_stat, min_arc_performance, max_arc_performance)
aggregated_statistics[elem] = {"label": aggr_stat_hr, "penwidth": str(arc_penwidth)}
elif type(elem) is PetriNet.Transition:
Expand Down
9 changes: 6 additions & 3 deletions pm4py/objects/petri_net/utils/performance_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@ def find_min_max_arc_performance(statistics, aggregation_measure):
return min_performance, max_performance


def aggregate_statistics(statistics, measure="frequency", aggregation_measure=None):
def aggregate_statistics(statistics, measure="frequency", aggregation_measure=None,
stat_locale: dict = {}):
"""
Gets aggregated statistics
Expand All @@ -382,7 +383,9 @@ def aggregate_statistics(statistics, measure="frequency", aggregation_measure=No
Desidered view on data (frequency or performance)
aggregation_measure
Aggregation measure (e.g. mean, min) to use
stat_locale
Dict to locale the stat strings
Returns
----------
aggregated_statistics
Expand All @@ -401,7 +404,7 @@ def aggregate_statistics(statistics, measure="frequency", aggregation_measure=No
elif measure == "performance":
if statistics[elem]["performance"]:
aggr_stat = aggregate_stats(statistics, elem, aggregation_measure)
aggr_stat_hr = human_readable_stat(aggr_stat)
aggr_stat_hr = human_readable_stat(aggr_stat, stat_locale)
arc_penwidth = get_arc_penwidth(aggr_stat, min_arc_performance, max_arc_performance)
aggregated_statistics[elem] = {"label": aggr_stat_hr, "penwidth": str(arc_penwidth)}
elif type(elem) is PetriNet.Transition:
Expand Down
33 changes: 18 additions & 15 deletions pm4py/util/vis_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,41 @@
MIN_EDGE_PENWIDTH_GRAPHVIZ = 1.0


def human_readable_stat(c):
def human_readable_stat(timedelta_seconds, stat_locale: dict = {}):
"""
Transform a timedelta expressed in seconds into a human readable string
Parameters
----------
c
timedelta_seconds
Timedelta expressed in seconds
stat_locale
Dict mapping stat strings
Returns
----------
string
Human readable string
"""
c = int(float(c))
years = c // 31104000
months = c // 2592000
days = c // 86400
hours = c // 3600 % 24
minutes = c // 60 % 60
seconds = c % 60
timedelta_seconds = int(float(timedelta_seconds))
years = timedelta_seconds // 31104000
months = timedelta_seconds // 2592000
days = timedelta_seconds // 86400
hours = timedelta_seconds // 3600 % 24
minutes = timedelta_seconds // 60 % 60
seconds = timedelta_seconds % 60

if years > 0:
return str(years) + "Y"
return str(years) + stat_locale.get("year", "Y")
if months > 0:
return str(months) + "MO"
return str(months) + stat_locale.get("month", "MO")
if days > 0:
return str(days) + "D"
return str(days) + stat_locale.get("day", "D")
if hours > 0:
return str(hours) + "h"
return str(hours) + stat_locale.get("hour", "h")
if minutes > 0:
return str(minutes) + "m"
return str(seconds) + "s"
return str(minutes) + stat_locale.get("minute", "m")
return str(seconds) + stat_locale.get("second", "s")


def get_arc_penwidth(arc_measure, min_arc_measure, max_arc_measure):
Expand Down
25 changes: 13 additions & 12 deletions pm4py/visualization/dfg/variants/frequency.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@
from pm4py.statistics.sojourn_time.log import get as soj_time_get
from enum import Enum
from pm4py.util import constants
from typing import Optional, Dict, Any, Union, Tuple
from pm4py.objects.log.obj import EventLog, EventStream
from pm4py.util import typing
import graphviz
from typing import Optional, Dict, Any, Tuple
from pm4py.objects.log.obj import EventLog


Expand Down Expand Up @@ -132,8 +129,9 @@ def get_activities_color(activities_count):


def graphviz_visualization(activities_count, dfg, image_format="png", measure="frequency",
max_no_of_edges_in_diagram=100000, start_activities=None, end_activities=None, soj_time=None,
font_size="12", bgcolor="transparent"):
max_no_of_edges_in_diagram=100000, start_activities=None,
end_activities=None, soj_time=None, font_size="12",
bgcolor="transparent"):
"""
Do GraphViz visualization of a DFG graph
Expand All @@ -155,7 +153,9 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f
End activities of the log
soj_time
For each activity, the sojourn time in the log
stat_locale
Dict to locale the stat strings
Returns
-----------
viz
Expand Down Expand Up @@ -212,7 +212,8 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f
fillcolor=activities_color[act], fontsize=font_size)
activities_map[act] = str(hash(act))
else:
viz.node(str(hash(act)), act + " (" + human_readable_stat(soj_time[act]) + ")", fontsize=font_size)
stat_string = human_readable_stat(soj_time[act], stat_locale)
viz.node(str(hash(act)), act + f" ({stat_string})", fontsize=font_size)
activities_map[act] = str(hash(act))

# make edges addition always in the same order
Expand All @@ -223,7 +224,7 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f
if "frequency" in measure:
label = str(dfg[edge])
else:
label = human_readable_stat(dfg[edge])
label = human_readable_stat(dfg[edge], stat_locale)
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]
Expand All @@ -249,7 +250,7 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f
return viz


def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Optional[Dict[Any, Any]] = None, activities_count : Dict[str, int] = None, soj_time: Dict[str, float] = None) -> graphviz.Digraph:
def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Optional[Dict[Any, Any]] = None, activities_count : Dict[str, int] = None, soj_time: Dict[str, float] = None) -> Digraph:
"""
Visualize a frequency directly-follows graph
Expand Down Expand Up @@ -307,5 +308,5 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt

return 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, soj_time=soj_time,
font_size=font_size, bgcolor=bgcolor)
start_activities=start_activities, end_activities=end_activities,
soj_time=soj_time, font_size=font_size, bgcolor=bgcolor)
31 changes: 17 additions & 14 deletions pm4py/visualization/dfg/variants/performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,10 @@
from pm4py.visualization.common.utils import *
from pm4py.util import exec_utils
from pm4py.statistics.sojourn_time.log import get as soj_time_get
from enum import Enum
from pm4py.util import constants
from enum import Enum

from typing import Optional, Dict, Any, Union, Tuple
from pm4py.objects.log.obj import EventLog, EventStream
from pm4py.util import typing
import graphviz
from typing import Optional, Dict, Any, Tuple
from pm4py.objects.log.obj import EventLog


Expand All @@ -46,7 +43,7 @@ class Parameters(Enum):
FONT_SIZE = "font_size"
AGGREGATION_MEASURE = "aggregation_measure"
BGCOLOR = "bgcolor"

STAT_LOCALE = "stat_locale"

def get_min_max_value(dfg):
"""
Expand Down Expand Up @@ -132,8 +129,9 @@ def get_activities_color_soj_time(soj_time):


def graphviz_visualization(activities_count, dfg, image_format="png", measure="frequency",
max_no_of_edges_in_diagram=100000, start_activities=None, end_activities=None, soj_time=None,
font_size="12", bgcolor="transparent"):
max_no_of_edges_in_diagram=100000, start_activities=None,
end_activities=None, soj_time=None, font_size="12",
bgcolor="transparent", stat_locale: dict = {}):
"""
Do GraphViz visualization of a DFG graph
Expand All @@ -155,7 +153,9 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f
End activities of the log
soj_time
For each activity, the sojourn time in the log
stat_locale
Dict to locale the stat strings
Returns
-----------
viz
Expand Down Expand Up @@ -212,7 +212,8 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f
fillcolor=activities_color[act], fontsize=font_size)
activities_map[act] = str(hash(act))
else:
viz.node(str(hash(act)), act + " (" + human_readable_stat(soj_time[act]) + ")", fontsize=font_size,
stat_string = human_readable_stat(soj_time[act], stat_locale)
viz.node(str(hash(act)), act + f" ({stat_string})", fontsize=font_size,
style='filled', fillcolor=activities_color[act])
activities_map[act] = str(hash(act))

Expand All @@ -224,7 +225,7 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f
if "frequency" in measure:
label = str(dfg[edge])
else:
label = human_readable_stat(dfg[edge])
label = human_readable_stat(dfg[edge], stat_locale)
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]
Expand All @@ -248,7 +249,7 @@ def graphviz_visualization(activities_count, dfg, image_format="png", measure="f
return viz


def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Optional[Dict[Any, Any]] = None, activities_count : Dict[str, int] = None, soj_time: Dict[str, float] = None) -> graphviz.Digraph:
def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Optional[Dict[Any, Any]] = None, activities_count : Dict[str, int] = None, soj_time: Dict[str, float] = None) -> Digraph:
"""
Visualize a performance directly-follows graph
Expand Down Expand Up @@ -283,6 +284,7 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt
activities = dfg_utils.get_activities_from_dfg(dfg)
aggregation_measure = exec_utils.get_param_value(Parameters.AGGREGATION_MEASURE, parameters, "mean")
bgcolor = exec_utils.get_param_value(Parameters.BGCOLOR, parameters, "transparent")
stat_locale = exec_utils.get_param_value(Parameters.STAT_LOCALE, parameters, {})

if activities_count is None:
if log is not None:
Expand Down Expand Up @@ -311,5 +313,6 @@ def apply(dfg: Dict[Tuple[str, str], int], log: EventLog = None, parameters: Opt

return 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, soj_time=soj_time,
font_size=font_size, bgcolor=bgcolor)
start_activities=start_activities, end_activities=end_activities,
soj_time=soj_time, font_size=font_size, bgcolor=bgcolor,
stat_locale=stat_locale)
12 changes: 8 additions & 4 deletions pm4py/visualization/heuristics_net/variants/pydotplus.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

class Parameters(Enum):
FORMAT = "format"
STAT_LOCALE = "stat_locale"


def get_corr_hex(num):
Expand Down Expand Up @@ -126,6 +127,7 @@ def apply(heu_net: HeuristicsNet, parameters: Optional[Dict[Union[str, Parameter
parameters
Possible parameters of the algorithm, including:
- Parameters.FORMAT
- Parameters.STAT_LOCALE
Returns
------------
Expand All @@ -136,6 +138,7 @@ def apply(heu_net: HeuristicsNet, parameters: Optional[Dict[Union[str, Parameter
parameters = {}

image_format = exec_utils.get_param_value(Parameters.FORMAT, parameters, "png")
stat_locale = exec_utils.get_param_value(Parameters.STAT_LOCALE, parameters, {})

graph = pydotplus.Dot(strict=True)
graph.obj_dict['attributes']['bgcolor'] = 'transparent'
Expand All @@ -154,9 +157,10 @@ def apply(heu_net: HeuristicsNet, parameters: Optional[Dict[Union[str, Parameter
label=node_name + " (" + str(node_occ) + ")", fillcolor=node.get_fill_color(graycolor),
fontcolor=node.get_font_color())
else:
time_str = f" ({human_readable_stat(heu_net.sojourn_times[node_name], stat_locale)})" if (
node_name in heu_net.sojourn_times) else node_name + " (0s)"
n = pydotplus.Node(name=node_name, shape="box", style="filled",
label=node_name + " (" + human_readable_stat(heu_net.sojourn_times[
node_name]) + ")" if node_name in heu_net.sojourn_times else node_name + " (0s)",
label=node_name + time_str,
fillcolor=node.get_fill_color(graycolor),
fontcolor=node.get_font_color())
corr_nodes[node] = n
Expand Down Expand Up @@ -188,7 +192,7 @@ def apply(heu_net: HeuristicsNet, parameters: Optional[Dict[Union[str, Parameter
penwidth=edge.get_penwidth(this_pen_width))
else:
e = pydotplus.Edge(src=corr_nodes[node], dst=corr_nodes[other_node],
label=edge.net_name + " (" + human_readable_stat(repr_value) + ")",
label=f"{edge.net_name} ({human_readable_stat(repr_value, stat_locale)})",
color=edge.get_color(),
fontcolor=edge.get_font_color(),
penwidth=edge.get_penwidth(this_pen_width))
Expand All @@ -200,7 +204,7 @@ def apply(heu_net: HeuristicsNet, parameters: Optional[Dict[Union[str, Parameter
penwidth=edge.get_penwidth(this_pen_width))
else:
e = pydotplus.Edge(src=corr_nodes[node], dst=corr_nodes[other_node],
label=human_readable_stat(repr_value),
label=human_readable_stat(repr_value, stat_locale),
color=edge.get_color(),
fontcolor=edge.get_font_color(),
penwidth=edge.get_penwidth(this_pen_width))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def get_shortest_paths(net, enable_extension=False):


def get_decorations_from_dfg_spaths_acticount(net, dfg, spaths, activities_count, variant="frequency",
aggregation_measure=None):
aggregation_measure=None, stat_locale: dict = {}):
"""
Get decorations from Petrinet without doing any replay
but based on DFG measures, shortest paths and activities count.
Expand All @@ -179,6 +179,8 @@ def get_decorations_from_dfg_spaths_acticount(net, dfg, spaths, activities_count
Describe how to decorate the Petri net (could be frequency or performance)
aggregation_measure
Specifies the aggregation measure
stat_locale
Dict to locale the stat strings
Returns
-----------
Expand Down Expand Up @@ -226,7 +228,7 @@ def get_decorations_from_dfg_spaths_acticount(net, dfg, spaths, activities_count
arcs_max_value = max(list(decorations_int.values()))
for arc in decorations_int:
if "performance" in variant:
arc_label = human_readable_stat(decorations_int[arc])
arc_label = human_readable_stat(decorations_int[arc], stat_locale)
else:
arc_label = str(decorations_int[arc])
decorations[arc] = {"label": arc_label,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Parameters(Enum):
TIMESTAMP_KEY = PARAMETER_CONSTANT_TIMESTAMP_KEY
AGGREGATION_MEASURE = "aggregationMeasure"
FONT_SIZE = "font_size"
STAT_LOCALE = "stat_locale"


def get_decorated_net(net, initial_marking, final_marking, log, parameters=None, variant="frequency"):
Expand Down Expand Up @@ -70,6 +71,7 @@ def get_decorated_net(net, initial_marking, final_marking, log, parameters=None,
"sum" if "frequency" in variant else "mean")

activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, xes.DEFAULT_NAME_KEY)
stat_locale = exec_utils.get_param_value(Parameters.STAT_LOCALE, parameters, {})

# we find the DFG
if variant == "performance":
Expand All @@ -83,7 +85,8 @@ def get_decorated_net(net, initial_marking, final_marking, log, parameters=None,
aggregated_statistics = get_decorations_from_dfg_spaths_acticount(net, dfg, spaths,
activities_count,
variant=variant,
aggregation_measure=aggregation_measure)
aggregation_measure=aggregation_measure,
stat_locale=stat_locale)

return visualize.apply(net, initial_marking, final_marking, parameters=parameters,
decorations=aggregated_statistics)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def get_decorations(log, net, initial_marking, final_marking, parameters=None, m
ht_perf_method
Method to use in order to annotate hidden transitions (performance value could be put on the last possible
point (last) or in the first possible point (first)
Returns
------------
decorations
Expand Down
Loading

0 comments on commit bb78f51

Please sign in to comment.