playground/process_analysis/status_transition_graph_stats_plot.py (48 lines of code) (raw):
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from plotly.graph_objs import Box, Figure
from playground.process_analysis.status_transition_graph import StatusTransitionGraph
class StatusTransitionGraphStatsPlot:
"""
Plot statistics of a status transition graph.
"""
@staticmethod
def boxplot(source: StatusTransitionGraph, max_edges: int = 8) -> Figure:
"""
Create a boxplot of status transition durations.
"""
fig = Figure()
most_common_edges_upto_max = sorted(
source.graph.edges.data(),
key=lambda edge: len(edge[2]["durations"]),
reverse=True,
)[:max_edges]
edge_counts = list(map(lambda edge: len(edge[2]["durations"]), most_common_edges_upto_max))
lowest_edge_count = min(edge_counts, default=1)
highest_edge_count = max(edge_counts, default=1)
edges_by_max_duration = sorted(
most_common_edges_upto_max,
key=lambda edge: max(edge[2]["durations"]),
reverse=True,
)
for edge in edges_by_max_duration:
durations = list(map(lambda d: round(d, 5), edge[2]["durations"]))
color = _color_for_edge(len(durations), lowest_edge_count, highest_edge_count)
fig.add_trace(
Box(
y=durations,
name=f"{edge[0]} ⮕ {edge[1]} ({len(durations)}x)",
boxpoints="outliers",
boxmean=True,
marker_color=color,
line_color=color,
)
)
fig.update_layout(
title_text="Status Transition Duration Statistics",
yaxis_title="Days",
xaxis_title="Transitions with occurences",
showlegend=False,
)
return fig
def _color_for_edge(edge_count: int, min_count: int, max_count: int) -> str:
base_r, base_g, base_b = (106, 29, 87) # dark base color
factor = (
0 if min_count == max_count else (1 - (edge_count - min_count) / (max_count - min_count)) * 0.8
) # 0.8 is the max brightning factor
edge_r = base_r + (255 - base_r) * factor
edge_g = base_g + (255 - base_g) * factor
edge_b = base_b + (255 - base_b) * factor
return f"rgb({edge_r},{edge_g},{edge_b})"