QPR ProcessAnalyzer Expression Examples: Difference between revisions

From QPR ProcessAnalyzer Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 385: Line 385:
</pre>
</pre>


// Call function objects using given parameters
<pre>
Call function objects using given parameters
[Def("", "a", "b", a+b), Def("", "a", "b", a*b)].(_(1,2))
[Def("", "a", "b", a+b), Def("", "a", "b", a*b)].(_(1,2))


// The sum of all the durations between the first occurrence of "Invoice" and the last occurrence of "Sales Order" event types.
The sum of all the durations between the first occurrence of "Invoice" and the last occurrence of "Sales Order" event types.
Def("FirstOccurrence", "&condition", _.Events[0].RecursiveFind(NextInCase, condition())[0]);
Def("FirstOccurrence", "&condition", _.Events[0].RecursiveFind(NextInCase, condition())[0]);
Def("LastOccurrence", "&condition", _.Events[Count(_) - 1].RecursiveFind(PreviousInCase, condition())[0]);
Def("LastOccurrence", "&condition", _.Events[Count(_) - 1].RecursiveFind(PreviousInCase, condition())[0]);

Revision as of 11:08, 6 April 2018

The following examples assume that there is a filter with id 1. point. All KPI analyses also have an EventLog as a starting point, so expression written after the "EventLogById(1)." is a valid expression for KPI Analyses.

Get all event types that appear among the events:

EventLogById(1).EventTypes

Same as previous, except return event type names (Strings):

EventLogById(1).EventTypes.Name

Get a hierarchical array, where the upper level are EventTypes and the leaf level are Events:

EventLogById(1).EventTypes.Events

Get a hierarchical array, where the upper level are EventTypes and the leaf level are also EventTypes (all branches contain same EventTypes):

EventLogById(1).EventTypes:Events.Type
EventLogById(1).EventTypes:Events.TimeStamp

List all Events as leaf nodes which timestamp is after 2012-01-01:

EventLogById(1).EventTypes:Events.Where(Timestamp > DateTime(2012,1,1))

Calculates count of Events in the leaf nodes:

Count(EventLogById(1).EventTypes:Events.Where(Timestamp > DateTime(2012,1,1)))

Events are ordered beginning from the last one:

EventLogById(1).EventTypes.OrderByDescending(Events, Timestamp)

Same as the previous but with explicit context specified:

EventLogById(1).EventTypes.OrderByDescending(_.Events, _.Timestamp)

Shows all event types in a single comma separated string list:

EventLogById(1).(StringJoin(", ", EventTypes.Name))

Same as the previous, except in addition the EventTypes are ordered by name:

EventLogById(1).(StringJoin(", ", OrderByDescending(EventTypes, Name).Name))

List of all the Starter flows in a model from filterId=1:

RemoveLeaves(EventLogById(1).Flows:From.Where(IsNull(_)))

Go through the model recursively using the flows:

RemoveLeaves(EventLogById(1).Flows:From.Where(IsNull(_))).To.OutgoingFlows.To.OutgoingFlows.To

Traverse the model using events:

RemoveLeaves(EventLogById(1).Flows:From.Where(IsNull(_))).FlowOccurrences.To.NextInCase.NextInCase.NextInCase

All case attribute values within the model grouped by case attribute types:

Let("cases", EventLogById(1).Cases, EventLogById(1).CaseAttributes:(Let("attribute", _, cases.Attribute(attribute))))

All event attribute values within the model grouped by event attribute types:

Let("events", EventLogById(1).Events, EventLogById(1).EventAttributes:(Let("attribute", _, events.Attribute(attribute))))

Define a new user defined functions named "First", which takes the first item in an array:

Def("First", "a", GetAt(0, a));

The defined array can be used as follows:

First(Array(1, 2, 3))
returns: 1
Def("x", "a", Distinct(RecurseLeaves(OrderBy(EventLogById(a).Cases.Events, Type.Name), Type.Name)))

Function definition that uses itself, i.e. recursive functions:

Def("Fib", "a", If(a < 2, 1, Fib(a - 1) + Fib(a - 2)));
For("i", 0, i < 10, i + 1, Fib(i));

Takas all models, and show them in "id:name" format:

Models.StringJoin(":", [Id, Name])
Models.[Id, Name]

How many times activity "Sales Order Changed" occurs on average in cases?

Average(Count(Cases.Events.Where(Type.Name == "Sales Order Changed")))

What's the average cost per case of all sales order changes? (assumption Sales Order Changed activity has attribute Cost)

Average(Sum(Cases.Events.Where(Type.Name == "Sales Order Changed").Cost))

What's the total cost of all events that occured this year?

Sum(Sum(Cases.Events.Where(TimeStamp.Year == Now.Year).Cost))

What the total cost calculated from events, where cost is calculated by "unit cost" times "number of units"?

Sum(
    Cases.Sum(
        Events.(
            UnitCost * NumberOfUnits
        )
    )
)

In how many cases duration between "Invoice Sent" and "Payment Received" is more than 30 days?

Count(
  Cases.Where(
    (
      Max(
        Events.Where(
          In(Type.Name, "et1", "et2")
        ).TimeStamp
      ) -
      Min(
        Events.Where(
          In(Type.Name, "et1", "et2")
        ).TimeStamp
      )
    ).TotalMinutes
    > 60
  )
)

In how many cases "Invoice Sent" is not directly followed by "Payment Received"?

Count(
  RemoveLeaves(
    (
      Cases.Events.Type.Name
    ).Where(
      Count(
        IndexOfSubArray(
          Array("et2", "et3")
        )
      ) <  Count(
        IndexOfSubArray(
          "et2"
        )
      )
    )
  )
)

In how many cases "Payment Received" is before "Invoice Sent"?

Count(
  RemoveLeaves(
    (
      Cases.Events.Type.Name
    ).Where(
      Catch(
        Max(
          IndexOfSubArray("et3")
        )
        < Min(
          IndexOfSubArray(
            "et1"
          )
        ),
        false
      )
    )
  )
)

In how many cases, invoice wasn't paid within the due date? (i.e. invoice is due and it's not paid, or invoice was paid after due date)

Count(
  Cases.Where(
        (
            (Count(
                Events.Where(
                    Type.Name == "Invoice Paid"
                )
            ) == 0)
            &&
            (
                Catch(
                    GetAt(0, Events.[Invoice Sent].StopOnNull()) < Now
                , false)
                ||
                Catch(
                    GetAt(0, Events.[Due Date].StopOnNull()) < Now
                , false)
            )
        )
        ||
        (
            Catch(
                GetAt(0,
                    Events.Where(
                        Type.Name == "Invoice Paid"
                    ).TimeStamp
                )
                >
                Coalesce(
                    Catch(GetAt(0, Events.[Invoice Sent]), null),
                    Catch(GetAt(0, Events.[Due Date]), null)
                ),
            null)
        )
    )
  )
)

Cases average duration between the first occurrence of event A and last occurrence of event B. Average is calculated only for those cases that contain both events A and B. Usecase: How long it takes to receive payment on average?

Average(
  Sum(
    Cases.Events.Where(
      Type.Name == "Payment Received"
    ).(
      TimeStamp - GetAt(
        0,
        Case.Events.Where(
          Type.Name == "Invoice Sent"
        )
      ).TimeStamp
    )
    .TotalMinutes
  )
)

What is the % of POs (cases) in a certain price range (case attribute "price")?

100 * Count(Cases.Where(Price > 1 && Price < 4)) / Count(Cases)

What is the average duration from event A directly followed by event B for those cases that directly continue to event C after B? (take the first occurrence if there are many)

Average(
  Cases.Let(
    "indexes",
    Box(
      Events.Type.Name
    ).IndexOfSubArray(
      Array("et2", "et3", "et4")
    )
  ).Where(
    Count(indexes) > 0
  ).(
    GetAt(
      GetAt(0,
        GetAt(0, indexes)
    ),
    Events
    )
  ).(
    NextInCase.TimeStamp - TimeStamp
  ).TotalMinutes
)

What is the average duration of completely received orders?

Average(
    Cases.Where(
        Count(
            Events.Where(
                Type.Name == "et3"
            )
        ) > 0
    ).(
        GetAt(0,
            Events.Where(
                Type.Name == "et2"
            ).TimeStamp
        )
        -
        GetAt(0,
            Events.Where(
                Type.Name == "et1"
            ).TimeStamp
        )
    ).TotalMinutes
)

Basic model usage

Models;
Models[0].EventLog;
Let("el", Models[0].EventLog);
el.Cases
el.Cases.Events
el.Cases:Events
el.Cases:Events.Type.Name
el.Cases.StringJoin("->", Events.Type.Name)
Def("show", "cases", cases.(CalcId + ": " + StringJoin("->",  Events.Type.Name)))
show(el.Cases)

Arrays

[1,2,3][0]
[1,2,3][Count(_)-1]
[1,2,3][[0,Count(_) - 1]]
[1,2,3][NumberRange(0, Count(_)-1)]

Calculating median

Def("Median", "array", (Let("a", OrderByValue(array)), If(Count(a) % 2 == 1, a[(Count(a) - 1) / 2], (a[(Count(a) / 2) - 1] + a[Count(a) / 2]) / 2)));
Median([4,1,3,2])
Median([1,2,3])

Remove tandem repeats of length 1

Def("RemoveTandemRepeatsOfLength1", "a", (Let("prev", null),ForEach("item", a, If(item == prev, _remove, (Set("prev", item), item)))._));
RemoveTandemRepeatsOfLength1([1,2,2,3,5,5,5,5,2,5])

Aggregation functions

Percentage of cases having event type named "Invoice"
Average(Count(el.Cases.Events.Where(Type.Name == "Invoice")))

Average flow durations for non-starter/-finisher flows
Average(el.Flows:FlowOccurrences.Where(!IsNull(From) && !IsNull(To), _remove).(To.TimeStamp - From.TimeStamp))

Average of all the flow durations
Average(Flatten(el.Flows:FlowOccurrences.Where(!IsNull(From) && !IsNull(To), _remove).(To.TimeStamp - From.TimeStamp)))

Context labeling

Let("a", ["foo": ["a": 1, "b": 2], "bar": 2])
GetContext(a[0])
GetValueOfContext("bar", a)

Recursion

el.Events[0].Recurse(NextInCase)
el.Events[0].RecurseWithHierarchy(NextInCase)
el.Events[0].RecursiveFind(NextInCase, Type.Name == "Invoice")
el.Flows.Where(IsNull(From)).To.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 2)

Functions

// Create Fibonacci number generator and a tester for it
Def("Fib", "a", If(a < 2, 1, Fib(a - 1) + Fib(a - 2)));
Def("FibTest", "a", For("i", 0, i < a, i + 1, Fib(i)));
FibTest(10);
Call function objects using given parameters
[Def("", "a", "b", a+b), Def("", "a", "b", a*b)].(_(1,2))

The sum of all the durations between the first occurrence of "Invoice" and the last occurrence of "Sales Order" event types.
Def("FirstOccurrence", "&condition", _.Events[0].RecursiveFind(NextInCase, condition())[0]);
Def("LastOccurrence", "&condition", _.Events[Count(_) - 1].RecursiveFind(PreviousInCase, condition())[0]);
TimeSpan(0, 0, 0, Average(el.Cases.(Catch(FirstOccurrence(Type.Name == "Invoice").TimeStamp - LastOccurrence(Type.Name == "Sales Order").TimeStamp, _remove).TotalSeconds)))

Miscellaneous examples

Return all starter flows
RemoveLeaves(el.Flows:From.Where(IsNull(_)))
el.Flows.Where(IsNull(From))

Create a range of dates
Def("DateRange", "minYear", "minMonth", "maxYear", "maxMonth", (Let("min", DateTime(minYear, minMonth)),Let("max", DateTime(maxYear, maxMonth)),For("dt", DateTime(min.Year, min.month), dt <= DateTime(max.Year, max.Month), dt.Month == 12 ? DateTime(dt.Year + 1, 1) : DateTime(dt.Year, dt.Month + 1), dt)))
DateRange(2011, 5, 2013, 1)

Group numbers into groups with specified minimum, maximum and range size.
Def("GroupNumbers", "a", "min", "max", "rangeSize", a.Ceiling(Max(0.0, Min(((max - min) / rangeSize) + 1, (_ - min)/rangeSize))));
GroupNumbers([-20, 0, 0.2, 3.2, 7, 9.2, 20], 0, 9, 3)

Show cases having at least one "Sales Order Changed (VA02)"
show(el.Cases.Where(Count(Events.Where(Type.Name == "Sales Order Changed (VA02)")) > 0))
show(Flatten(el.Cases.Events[0].RecursiveFind(NextInCase, Type.Name == "Sales Order Changed (VA02)").Case))

Show cases having "Sales Order Changed (VA02)" before "Outbound Delivery" at least once
show(el.Cases.Where(([Events.Type.Name].Coalesce(Min(IndexOfSubArray("Sales Order Changed (VA02)"))-Max(IndexOfSubArray("Outbound Delivery")), 0))[0] < 0))
show(Flatten(el.Cases.Events[0].RecursiveFind(NextInCase, Type.Name == "Sales Order Changed (VA02)").RecursiveFind(NextInCase, Type.Name == "Outbound Delivery").Case))

Workload analysis
Def("ActiveEventsAt", "at", "attribute", "value",
Cases:Events[0]
  .RecursiveFind(
    NextInCase,
    TimeStamp < at 
    && (Attribute(attribute) == value) 
    && (IsNull(NextInCase) || (NextInCase.TimeStamp > at))
)
);
Def("ActiveEventsAt", "at", "attribute", "value",Cases:Events[0].RecursiveFind(NextInCase,TimeStamp < at && (Attribute(attribute) == value) && (IsNull(NextInCase) || (NextInCase.TimeStamp > at))));
ReplaceLeafValues(el.ActiveEventsAt(DateTime(2011,9,6), "Organization", "Finance"), "item", [item, item.Organization], 0)
ReplaceLeafValues(el.ActiveEventsAt(DateTime(2011,9,6), "Organization", "Delivery"), "item", [item, item.Organization], 0)
ReplaceLeafValues(el.ActiveEventsAt(DateTime(2011,9,6), "Organization", "Sales"), "item", [item, item.Organization], 0)

Get names and durations of all the cases sorted by case duration grouped by variations sorted by the number of cases in each variation.
ReplaceLeafValues(OrderBy(OrderBy(el.Variations, CaseCount):Cases, Duration), "leaf", "" + leaf.Name + ": " + leaf.Duration, 0)

Conformance analysis
Let("xml", LoadTextFile("C:\\Users\\marhink\\Desktop\\SAP o2c.bpmn"));
Let("cm", ConformanceModelFromXml(xml));
Let("vars", OrderByDescending(EventLogById(1).Variations, CaseCount));
vars.(Let("analysis",AnalyzeConformance(cm)),""+CaseCount+" * "+(CountTop(analysis)==0)+((CountTop(analysis)>0)?" ("+StringJoin("",ReplaceLeafValues(analysis,"i",i[0]==3?i[2]+"->"+i[3]:(i[0]==2?"DNF":"")))+")":"")+": "+StringJoin("->",EventTypes.Name))
Let("cases", OrderBy(EventLogById(1).Cases, Name));
cases.(Let("analysis",AnalyzeConformance(cm)),""+(CountTop(analysis)==0)+((CountTop(analysis)>0)? " ("+StringJoin("",ReplaceLeafValues(analysis,"i",i[0]==3?i[2]+"->"+i[3]:(i[0]==2?"DNF":"")))+")":"")+": "+StringJoin("->",Events.Type.Name))

Hierarchy
el.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 10);
cm.StartEvents.RecurseWithHierarchy(OutgoingFlows.To, !IsNull(_), 10);