def split_loop_infrequent(cut, l, activity_key): n = len(cut) new_logs = [obj.EventLog() for i in range(0, n)] for trace in l: s = cut[0] st = obj.Trace() for act in trace: if act in s: st.insert(act) else: j = 0 for j in range(0, len(cut)): if cut[j] == s: break new_logs[j].append(st) st = obj.Trace() for partition in cut: if act[activity_key] in partition: s.append(partition) # L_j <- L_j + [st] with sigma_j = s j = 0 for j in range(0, len(cut)): if cut[j] == s: break new_logs[j].append(st) if s != cut[0]: new_logs[0].append(obj.EventLog()) return new_logs
def map_(func, log): ''' Maps the log according to a given lambda function. domain and target of the function need to be of the same type (either trace or event) otherwise, the map behaves unexpected Parameters ---------- func log Returns ------- ''' if isinstance(log, log_inst.EventLog): return log_inst.EventLog(list(map(func, log)), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) elif isinstance(log, log_inst.EventStream): return log_inst.EventStream(list(map(func, log)), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) else: warnings.warn( 'input log object not of appropriate type, map() not applied') return log
def split_loop(cut, l, activity_key): new_logs = [] for c in cut: # for cut partition lo = obj.EventLog() for trace in l: # for all traces j = 0 while j in range(0, len(trace)): # for all events if trace[j][activity_key] in c: trace_new = obj.Trace() # declared here and not above, so that we can generate multiple traces from one trace and # cut (repetition) # append those events that are contained in c: while trace[j][activity_key] in c: trace_new.append(trace[j]) if j + 1 < len(trace): j += 1 else: j += 1 break lo.append(trace_new) else: j += 1 if len(lo) != 0: new_logs.append(lo) return new_logs
def split_sequence(cut, l, activity_key): new_logs = [] for c in cut: # for all cut-partitions lo = obj.EventLog() for trace in l: # for all traces in the log not_in_c = True trace_new = obj.Trace() for j in range(0, len(trace)): # for every event in the current trace if trace[j][activity_key] in c: not_in_c = False while trace[j][activity_key] in c: trace_new.append( trace[j] ) # we only add the events that match the cut partition if j + 1 < len(trace): j += 1 else: j += 1 break lo.append(trace_new) break if not_in_c: lo.append(trace_new) new_logs.append(lo) if len(new_logs) > 0: return new_logs
def filter_(func, log): ''' Filters the log according to a given lambda function. Parameters ---------- func log Returns ------- ''' if isinstance(log, log_inst.EventLog): return log_inst.EventLog(list(filter(func, log)), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) elif isinstance(log, log_inst.EventStream): return log_inst.EventStream(list(filter(func, log)), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) else: warnings.warn( 'input log object not of appropriate type, filter() not applied') return log
def __transform_event_stream_to_event_log( log, case_glue=Parameters.CASE_ID_KEY.value, include_case_attributes=True, case_attribute_prefix=Parameters.CASE_ATTRIBUTE_PREFIX.value, enable_deepcopy=False): """ Converts the event stream to an event log Parameters ---------- log: :class:`pm4py.log.log.EventLog` An event stream case_glue: Case identifier. Default is 'case:concept:name' include_case_attributes: Default is True case_attribute_prefix: Default is 'case:' enable_deepcopy Enables deepcopy (avoid references between input and output objects) Returns ------- log : :class:`pm4py.log.log.EventLog` An event log """ if enable_deepcopy: log = deepcopy(log) traces = {} for orig_event in log: event = copy(orig_event) glue = event[case_glue] if glue not in traces: trace_attr = {} if include_case_attributes: for k in event.keys(): if k.startswith(case_attribute_prefix): trace_attr[k.replace(case_attribute_prefix, '')] = event[k] if xes.DEFAULT_TRACEID_KEY not in trace_attr: trace_attr[xes.DEFAULT_TRACEID_KEY] = glue traces[glue] = log_instance.Trace(attributes=trace_attr) if include_case_attributes: for k in list(event.keys()): if k.startswith(case_attribute_prefix): del event[k] traces[glue].append(event) return log_instance.EventLog(traces.values(), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties)
def split_parallel(cut, l, activity_key): new_logs = [] for c in cut: lo = obj.EventLog() for trace in l: new_trace = obj.Trace() for event in trace: if event[activity_key] in c: new_trace.append(event) lo.append(new_trace) new_logs.append(lo) return new_logs
def split_xor(cut, l, activity_key): new_logs = [] for c in cut: # for cut partition lo = obj.EventLog() for i in range(0, len(l)): # for trace in log fits = True for j in range(0, len(l[i])): # for event in trace if l[i][j][activity_key] not in c: fits = False # if not every event fits the current cut-partition, we don't add it's trace if fits: lo.append(l[i]) new_logs.append(lo) return new_logs # new_logs is a list that contains logs
def empty_trace_filtering(l, f): enough_traces = False empty_traces_present, counter = __count_empty_traces(l) if counter >= len(l) * f: enough_traces = True if empty_traces_present: new_log = obj.EventLog() for trace in l: if len(trace) != 0: new_log.append(trace) return empty_traces_present, enough_traces, new_log else: return False, False, l
def apply_from_variants_list(var_list, petri_net, initial_marking, final_marking, parameters=None): """ Apply the alignments from the specification of a list of variants in the log Parameters ------------- var_list List of variants (for each item, the first entry is the variant itself, the second entry may be the number of cases) petri_net Petri net initial_marking Initial marking final_marking Final marking parameters Parameters of the algorithm (same as 'apply' method, plus 'variant_delimiter' that is , by default) Returns -------------- dictio_alignments Dictionary that assigns to each variant its alignment """ if parameters is None: parameters = {} log = log_implementation.EventLog() dictio_alignments = {} for varitem in var_list: variant = varitem[0] trace = variants_util.variant_to_trace(variant, parameters=parameters) log.append(trace) alignment = apply(log, petri_net, initial_marking, final_marking) for index, varitem in enumerate(var_list): variant = varitem[0] dictio_alignments[variant] = alignment[index] return dictio_alignments
def sort_log( log: log_inst.EventLog, key, reverse: bool = False ) -> Union[log_inst.EventLog, log_inst.EventStream]: """ Sorts the event log according to a given key. Parameters ---------- log event log object; either EventLog or EventStream key sorting key reverse indicates whether sorting should be reversed or not Returns ------- sorted event log if object provided is correct; original log if not correct """ if type(log) not in [pd.DataFrame, EventLog, EventStream]: raise Exception( "the method can be applied only to a traditional event log!") if isinstance(log, log_inst.EventLog): return log_inst.EventLog(sorted(log, key=key, reverse=reverse), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) elif isinstance(log, log_inst.EventStream): return log_inst.EventStream(sorted(log, key=key, reverse=reverse), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) else: warnings.warn( 'input log object not of appropriate type, sorted() not applied') return log
def sort_(func, log, reverse=False): if isinstance(log, log_inst.EventLog): return log_inst.EventLog(sorted(log, key=func, reverse=reverse), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) elif isinstance(log, log_inst.EventStream): return log_inst.EventStream(sorted(log, key=func, reverse=reverse), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) else: warnings.warn( 'input log object not of appropriate type, map() not applied') return log
def split_sequence_infrequent(cut, l, activity_key): # write L_1,...,L_n like in second line of code on page 206 n = len(cut) new_logs = [obj.EventLog() for j in range(0, n)] ignore = [] split_points_list = [0] * len(l) for i in range(0, n): split_point = 0 # write our ignore list with all elements from past cut partitions if i != 0: for element in cut[i-1]: ignore.append(element) for j in range(len(l)): trace = l[j] new_split_point = find_split_point(trace, cut[i], split_points_list[j], ignore, activity_key) cutted_trace = cut_trace_between_two_points(trace, split_points_list[j], new_split_point) filtered_trace = filter_trace_on_cut_partition(cutted_trace, cut[i], activity_key) new_logs[i].append(filtered_trace) split_points_list[j] = new_split_point return new_logs
def filter_log( f: Callable[[Any], bool], log: log_inst.EventLog ) -> Union[log_inst.EventLog, log_inst.EventStream]: """ Filters the log according to a given (lambda) function. Parameters ---------- f function that specifies the filter criterion, may be a lambda log event log; either EventLog or EventStream Object Returns ------- log filtered event log if object provided is correct; original log if not correct """ if type(log) not in [pd.DataFrame, EventLog, EventStream]: raise Exception( "the method can be applied only to a traditional event log!") if isinstance(log, log_inst.EventLog): return log_inst.EventLog(list(filter(f, log)), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) elif isinstance(log, log_inst.EventStream): return log_inst.EventStream(list(filter(f, log)), attributes=log.attributes, classifiers=log.classifiers, omni_present=log.omni_present, extensions=log.extensions, properties=log.properties) else: warnings.warn( 'input log object not of appropriate type, filter() not applied') return log
def split_xor_infrequent(cut, l, activity_key): # TODO think of empty logs # creating the empty L_1,...,L_n from the second code-line on page 205 n = len(cut) new_logs = [obj.EventLog() for i in range(0, n)] for trace in l: # for all traces number_of_events_in_trace = 0 index_of_cut_partition = 0 i = 0 # use i as index here so that we can write in L_i for i in range(0, len(cut)): # for all cut partitions temp_counter = 0 for event in trace: # for all events in current trace if event[activity_key] in cut[i]: # count amount of events from trace in partition temp_counter += 1 if temp_counter > number_of_events_in_trace: number_of_events_in_trace = temp_counter index_of_cut_partition = i filtered_trace = filter_trace_on_cut_partition(trace, cut[index_of_cut_partition], activity_key) new_logs[index_of_cut_partition].append(filtered_trace) return new_logs
def apply_playout(net, initial_marking, no_traces=100, max_trace_length=100, case_id_key=xes_constants.DEFAULT_TRACEID_KEY, activity_key=xes_constants.DEFAULT_NAME_KEY, timestamp_key=xes_constants.DEFAULT_TIMESTAMP_KEY, final_marking=None, smap=None, log=None, return_visited_elements=False): """ Do the playout of a Petrinet generating a log Parameters ---------- net Petri net to play-out initial_marking Initial marking of the Petri net no_traces Number of traces to generate max_trace_length Maximum number of events per trace (do break) case_id_key Trace attribute that is the case ID activity_key Event attribute that corresponds to the activity timestamp_key Event attribute that corresponds to the timestamp final_marking If provided, the final marking of the Petri net smap Stochastic map log Log """ if final_marking is None: # infer the final marking from the net final_marking = final_marking_discovery.discover_final_marking(net) if smap is None: if log is None: raise Exception( "please provide at least one between stochastic map and log") smap = replay.get_map_from_log_and_net(log, net, initial_marking, final_marking, parameters={ Parameters.ACTIVITY_KEY: activity_key, Parameters.TIMESTAMP_KEY: timestamp_key }) # assigns to each event an increased timestamp from 1970 curr_timestamp = 10000000 all_visited_elements = [] for i in range(no_traces): visited_elements = [] visible_transitions_visited = [] marking = copy(initial_marking) while len(visible_transitions_visited) < max_trace_length: visited_elements.append(marking) if not semantics.enabled_transitions( net, marking): # supports nets with possible deadlocks break all_enabled_trans = semantics.enabled_transitions(net, marking) if final_marking is not None and marking == final_marking: en_t_list = list(all_enabled_trans.union({None})) else: en_t_list = list(all_enabled_trans) trans = stochastic_utils.pick_transition(en_t_list, smap) if trans is None: break visited_elements.append(trans) if trans.label is not None: visible_transitions_visited.append(trans) marking = semantics.execute(trans, net, marking) all_visited_elements.append(tuple(visited_elements)) if return_visited_elements: return all_visited_elements log = log_instance.EventLog() for index, visited_elements in enumerate(all_visited_elements): trace = log_instance.Trace() trace.attributes[case_id_key] = str(index) for element in visited_elements: if type(element ) is PetriNet.Transition and element.label is not None: event = log_instance.Event() event[activity_key] = element.label event[timestamp_key] = datetime.datetime.fromtimestamp( curr_timestamp) trace.append(event) # increases by 1 second curr_timestamp += 1 log.append(trace) return log
def apply(net: PetriNet, initial_marking: Marking, final_marking: Marking = None, parameters: Optional[Dict[Union[str, Parameters], Any]] = None) -> EventLog: """ Do the playout of a Petrinet generating a log (extensive search; stop at the maximum trace length specified Parameters ----------- net Petri net to play-out initial_marking Initial marking of the Petri net final_marking If provided, the final marking of the Petri net parameters Parameters of the algorithm: Parameters.MAX_TRACE_LENGTH -> Maximum trace length Parameters.PETRI_SEMANTICS -> Petri net semantics """ if parameters is None: parameters = {} case_id_key = exec_utils.get_param_value(Parameters.CASE_ID_KEY, parameters, xes_constants.DEFAULT_TRACEID_KEY) activity_key = exec_utils.get_param_value(Parameters.ACTIVITY_KEY, parameters, xes_constants.DEFAULT_NAME_KEY) timestamp_key = exec_utils.get_param_value(Parameters.TIMESTAMP_KEY, parameters, xes_constants.DEFAULT_TIMESTAMP_KEY) max_trace_length = exec_utils.get_param_value(Parameters.MAX_TRACE_LENGTH, parameters, 10) return_elements = exec_utils.get_param_value(Parameters.RETURN_ELEMENTS, parameters, False) max_marking_occ = exec_utils.get_param_value(Parameters.MAX_MARKING_OCC, parameters, sys.maxsize) semantics = exec_utils.get_param_value(Parameters.PETRI_SEMANTICS, parameters, petri_net.semantics.ClassicSemantics()) # assigns to each event an increased timestamp from 1970 curr_timestamp = 10000000 feasible_elements = [] to_visit = [(initial_marking, (), ())] visited = set() while len(to_visit) > 0: state = to_visit.pop(0) m = state[POSITION_MARKING] trace = state[POSITION_TRACE] elements = state[POSITION_ELEMENTS] if (m, trace) in visited: continue visited.add((m, trace)) en_t = semantics.enabled_transitions(net, m) if (final_marking is not None and m == final_marking) or (final_marking is None and len(en_t) == 0): if len(trace) <= max_trace_length: feasible_elements.append(elements) for t in en_t: new_elements = elements + (m,) new_elements = new_elements + (t,) counter_elements = Counter(new_elements) if counter_elements[m] > max_marking_occ: continue new_m = semantics.weak_execute(t, net, m) if t.label is not None: new_trace = trace + (t.label,) else: new_trace = trace new_state = (new_m, new_trace, new_elements) if new_state in visited or len(new_trace) > max_trace_length: continue to_visit.append(new_state) if return_elements: return feasible_elements log = log_instance.EventLog() for elements in feasible_elements: log_trace = log_instance.Trace() log_trace.attributes[case_id_key] = str(len(log)) activities = [x.label for x in elements if type(x) is PetriNet.Transition and x.label is not None] for act in activities: curr_timestamp = curr_timestamp + 1 log_trace.append( log_instance.Event({activity_key: act, timestamp_key: datetime.datetime.fromtimestamp(curr_timestamp)})) log.append(log_trace) return log
def apply_playout(net, initial_marking, no_traces=100, max_trace_length=100, initial_timestamp=10000000, initial_case_id=0, case_id_key=xes_constants.DEFAULT_TRACEID_KEY, activity_key=xes_constants.DEFAULT_NAME_KEY, timestamp_key=xes_constants.DEFAULT_TIMESTAMP_KEY, final_marking=None, return_visited_elements=False, semantics=petri_net.semantics.ClassicSemantics()): """ Do the playout of a Petrinet generating a log Parameters ---------- net Petri net to play-out initial_marking Initial marking of the Petri net no_traces Number of traces to generate max_trace_length Maximum number of events per trace (do break) initial_timestamp Increased timestamp from 1970 for the first event initial_case_id Case id of the first event case_id_key Trace attribute that is the case ID activity_key Event attribute that corresponds to the activity timestamp_key Event attribute that corresponds to the timestamp final_marking If provided, the final marking of the Petri net semantics Semantics of the Petri net to be used (default: petri_net.semantics.ClassicSemantics()) """ # assigns to each event an increased timestamp from 1970 curr_timestamp = initial_timestamp all_visited_elements = [] for i in range(no_traces): visited_elements = [] visible_transitions_visited = [] marking = copy(initial_marking) while len(visible_transitions_visited) < max_trace_length: visited_elements.append(marking) if not semantics.enabled_transitions(net, marking): # supports nets with possible deadlocks break all_enabled_trans = semantics.enabled_transitions(net, marking) if final_marking is not None and marking == final_marking: trans = choice(list(all_enabled_trans.union({None}))) else: trans = choice(list(all_enabled_trans)) if trans is None: break visited_elements.append(trans) if trans.label is not None: visible_transitions_visited.append(trans) marking = semantics.execute(trans, net, marking) all_visited_elements.append(tuple(visited_elements)) if return_visited_elements: return all_visited_elements log = log_instance.EventLog() for index, visited_elements in enumerate(all_visited_elements): trace = log_instance.Trace() trace.attributes[case_id_key] = str(index+initial_case_id) for element in visited_elements: if type(element) is PetriNet.Transition and element.label is not None: event = log_instance.Event() event[activity_key] = element.label event[timestamp_key] = datetime.datetime.fromtimestamp(curr_timestamp) trace.append(event) # increases by 1 second curr_timestamp += 1 log.append(trace) return log