Incremental Calculation of Cycle and Lead Time

Incremental calculation of Cycle and Lead Time

Two important KPI for a process executions are:

  • The Lead Time: the overall time in which the instance was worked, from the start to the end, without considering if it was actively worked or not.
  • The Cycle Time: the overall time in which the instance was worked, from the start to the end, considering only the times where it was actively worked.

For these concepts, it is important to consider only business hours (so, excluding nights and weekends). Indeed, in that period the machinery and the workforce is at home, so could not proceed in working the instance, so the time “wasted” there is not recoverable.

Within ‘interval’ event logs (that have a start and an end timestamp), it is possible to calculate incrementally the lead time and the cycle time (event per event). The lead time and the cycle time that are reported on the last event of the case are the ones related to the process execution. With this, it is easy to understand which activities of the process have caused a bottleneck (e.g. the lead time increases significantly more than the cycle time).

The algorithm implemented in PM4Py start sorting each case by the start timestamp (so, activities started earlier are reported earlier in the log), and is able to calculate the lead and cycle time in all the situations, also the complex ones reported in the following picture:

In the following, we aim to insert the following attributes to events inside a log:

  • @@approx_bh_partial_cycle_time => incremental cycle time associated to the event (the cycle time of the last event is the cycle time of the instance)
  • @@approx_bh_partial_lead_time => incremental lead time associated to the event
  • @@approx_bh_overall_wasted_time => difference between the partial lead time and the partial cycle time values
  • @@approx_bh_this_wasted_time => wasted time ONLY with regards to the activity described by the ‘interval’ event
  • @@approx_bh_ratio_cycle_lead_time => measures the incremental Flow Rate (between 0 and 1).

The method that calculates the lead and the cycle time could accept the following optional parameters:

  • worktiming: the work timing (e.g. [7, 17])
  • weekends: the specification of the weekends (e.g. [6, 7])

And could be applied with the following line of code:

from pm4py.objects.log.util import interval_lifecycle

enriched_log = interval_lifecycle.assign_lead_cycle_time(log)

With this, an enriched log that contains for each event the corresponding attributes for lead/cycle time is obtained.

Filtering on activities duration

Having a log enriched with interval and duration information, it is possible to use this log to identify the process instances where the actual cycle time (COMPLETE – START) of an activity is in a given range

To do so, it is possible to use the numeric attribute filter. Let’s suppose to start with the BPI Challenge 2012 log (for velocity, only the first 50 cases are actually imported).

from pm4py.objects.log.importer.xes import factory as xes_importer

log = xes_importer.apply("bpic2012.xes", variant="nonstandard", parameters={"max_no_traces_to_import": 50})

and to use the log utility to transform it into an “interval” event log

from pm4py.objects.log.util import interval_lifecycle

enriched_log = interval_lifecycle.assign_lead_cycle_time(log)

Then, suppose we want to filter the cases where the activity W_Nabellen offertes (from START to COMPLETE) has had a duration comprised between 300.0 and 1000.0 seconds. The numeric attribute filter can be used:

from pm4py.algo.filtering.log.attributes import attributes_filter

With the following specification (here, the attribute to which the numeric attribute filter is applied is the @@duration, while an additional filter is imposed on the concept:name attribute to be equal to W_Nabellen offertes)

filtered_log = attributes_filter.apply_numeric(deepcopy(enriched_log), 300.0, 1000.0, parameters={constants.PARAMETER_CONSTANT_ATTRIBUTE_KEY: "@@duration", "stream_filter_key1": "concept:name", "stream_filter_value1": "W_Nabellen offertes"})

Suppose we want to impose the further restriction on having the activity W_Nabellen offertes done by the resource 10931, then the following code can be provided:

filtered_log2 = attributes_filter.apply_numeric(deepcopy(enriched_log), 300.0, 1000.0, parameters={constants.PARAMETER_CONSTANT_ATTRIBUTE_KEY: "@@duration", "stream_filter_key1": "concept:name", "stream_filter_value1": "W_Nabellen offertes", "stream_filter_key2": "org:resource", "stream_filter_value2": "10913"})

.