library(bupaverse)
library(processanimateR)
Here, we use the patients
event log provided by the
eventdataR
package.
A basic animation with static color and token size:
animate_process(patients)
Default token color, size, or image can be changed as follows:
animate_process(patients, mapping = token_aes(size = token_scale(12), shape = "rect"))
animate_process(patients, mapping = token_aes(color = token_scale("red")))
The example animation on the top of this site:
animate_process(patients, mode = "relative", jitter = 10, legend = "color",
mapping = token_aes(color = token_scale("employee",
scale = "ordinal",
range = RColorBrewer::brewer.pal(7, "Paired"))))
Tokens can also be assigned images, for example:
animate_process(patients,
mapping = token_aes(shape = "image",
size = token_scale(10),
image = token_scale("https://upload.wikimedia.org/wikipedia/en/5/5f/Pacman.gif")))
It is possible to use a secondary data frame to determine the aesthetics of tokens regardless of the timestamps at which activities occurred. This could be useful if some measurements were taken throughout a process, but the measurement event itself should not be included in the process map.
For example, the lacticacid measurements of the
sepsis
data set could be used in that way:
library(processanimateR)
library(dplyr)
library(bupaR)
# Extract only the lacticacid measurements
<- sepsis %>%
lactic mutate(lacticacid = as.numeric(lacticacid)) %>%
filter_activity(c("LacticAcid")) %>%
as.data.frame() %>%
select("case" = case_id,
"time" = timestamp,
value = lacticacid) # format needs to be 'case,time,value'
# Remove the measurement events from the sepsis log
<- sepsis %>%
sepsisBase filter_activity(c("LacticAcid", "CRP", "Leucocytes", "Return ER",
"IV Liquid", "IV Antibiotics"), reverse = T) %>%
filter_trace_frequency(percentage = 0.95)
# Animate with the secondary data frame `lactic`
animate_process(sepsisBase,
mode = "relative",
duration = 300,
legend = "color",
mapping = token_aes(color = token_scale(lactic,
scale = "linear",
range = c("#fff5eb","#7f2704"))))
processanimateR
animation can be also used interactively
as part of a (Shiny) web-application. Here, an example application that
expects attributes are of an appropriate data type and automatically
chooses appropriate color scales is given. We first define a function
ianimate_process()
that defines our Shiny application as
follows:
library(processanimateR)
library(shiny)
library(shinycssloaders)
<- function(eventlog, min.time = 30, max.time = 600, default.time = 60) {
ianimate_process
<- function(request) {
ui fluidPage(
$head(tags$style("#process{height:90vh !important;}")),
tagstitlePanel("Hello processanimateR!"),
sidebarLayout(
sidebarPanel(
width = 2,
sliderInput("duration", "Animation duration", min.time, max.time, default.time),
selectInput("type", "Animation type", c("relative", "absolute"), "relative"),
selectInput("sizeAttribute", "Size attribute", c("none", colnames(eventlog)), "none"),
selectInput("colorAttribute", "Color attribute", c("none", colnames(eventlog)), "none"),
selectInput("orientation", "Orientation", c("horizontal"="LR", "vertical"="TB"), "horizontal"),
h4("Selected cases"),
textOutput("token_selection"),
h4("Selected activities"),
textOutput("activity_selection")
),
mainPanel(
width = 10,
::withSpinner(processanimaterOutput("process"))
shinycssloaders
)
)
)
}
<- function(session, input, output) {
server
<- reactive({
data
if (input$colorAttribute != "none") {
<- rlang::sym(input$colorAttribute)
attr <- eventlog %>% pull(!!attr)
val if (!(is.character(val) || is.factor(val))) {
warning("Trying to use a numeric attribute for the token color!")
}
}
if (input$sizeAttribute != "none") {
# This only works for numeric attributes
<- rlang::sym(input$sizeAttribute)
attr <- eventlog %>% pull(!!attr)
val if (!is.numeric(val)) {
warning("Trying to use a non-numeric attribute for the token size!")
}
}
eventlog
})
$token_selection <- renderText({
output
paste0(input$process_tokens, ",")
})
$activity_selection <- renderText({
output
paste0(input$process_activities, ",")
})
$process <- renderProcessanimater(expr = {
output<- processmapR::process_map(data(), render = F)
graph <- DiagrammeR::add_global_graph_attrs(graph, attr = "rankdir", value = input$orientation, attr_type = "graph")
model if (input$sizeAttribute != "none" && input$colorAttribute != "none") {
animate_process(data(), model,
mode = input$type,
legend = "color",
mapping = token_aes(color = token_scale(input$colorAttribute, scale = "ordinal",
range = RColorBrewer::brewer.pal(5, "YlOrBr")),
size = token_scale(input$sizeAttribute, scale = "linear", range = c(6,10))),
duration = input$duration)
else if (input$sizeAttribute != "none") {
} animate_process(data(), model,
mode = input$type,
legend = "size",
mapping = token_aes(size = token_scale(input$sizeAttribute, scale = "linear", range = c(6,10))),
duration = input$duration)
else if (input$colorAttribute != "none") {
} animate_process(data(), model,
mode = input$type,
legend = "color",
mapping = token_aes(color = token_scale(input$colorAttribute, scale = "ordinal", range = RColorBrewer::brewer.pal(5, "YlOrBr"))),
duration = input$duration)
else {
} animate_process(data(), model,
mode = input$type,
duration = input$duration)
}
})
}
shinyApp(ui, server, options = list(height = 500))
}
Then, the application can be, for example, launched by calling:
library(eventdataR)
library(edeaR)
library(dplyr)
ianimate_process(sepsis %>%
filter_trace_frequency(percentage = 0.2) %>%
filter_activity(c("Return ER"), reverse = T) %>%
# we fix the datatype of some of the attributes to allow proper rendering of the token color
# the token size option currently only support numeric attributes
mutate_at(c("lacticacid", "leucocytes", "crp", "age"), as.numeric) %>%
mutate_at(c("disfuncorg", "sirscriteria2ormore", "infectionsuspected"), as.logical))
Selections made in the processanimateR
animation can be
used as input by Shiny applications. Here a simple example in which a
Shiny module is created that renders the case identifiers of selected
tokens and the identifiers and names of selected activities:
library(shiny)
library(processanimateR)
library(eventdataR)
library(jsonlite)
<- function(eventlog, min.time = 30, max.time = 600, default.time = 60) {
shinyAnimation
# Define Shiny Module
<- function(id, title) {
animationUI <- NS(id)
ns tagList(
h2(title),
processanimaterOutput(ns("process")),
h4("Selected cases"),
textOutput(ns("token_selection")),
h4("Selected activities"),
textOutput(ns("activity_selection")),
h4("Current time"),
textOutput(ns("activity_time"))
)
}
<- function(input, output, session, ...) {
animation
$token_selection <- renderText({
outputif (is.null(input$process_tokens)) {
"None"
else {
} paste0(input$process_tokens, collapse = ",")
}
})
$activity_selection <- renderText({
outputif (is.null(input$process_activities)) {
"None"
else {
} <- jsonlite::fromJSON(input$process_activities)
activities paste0("(", activities$id, ",", activities$activity, ")", collapse = ",")
}
})
$activity_time <- renderText({
outputif (is.null(input$process_time)) {
"0"
else {
} $process_time
input
}
})
$process <- renderProcessanimater(expr = {
outputanimate_process(eventlog, ...)
})
}
<- fluidPage(
ui animationUI("module1", "Relative"),
animationUI("module2", "Absolute")
)
# Two animations
<- function(input, output, session) {
server callModule(animation, "module1", mode = "relative")
callModule(animation, "module2")
}
shinyApp(ui, server, options = list(height = 500))
}
shinyAnimation(patients)
Contributed by Dominic Rowney.
It is possible to change the aesthetics of tokens based on the timestamp of the animation.
For example, the number of days a ‘patient’ has been in the system.
# Libraries ---------------------------------------------------------------
library(dplyr) ##pipes
library(tidyr) ##tidy data, partcularly the crossing() function
##
## Attaching package: 'tidyr'
## The following object is masked from 'package:processcheckR':
##
## contains
library(lubridate) ##date time manipulation
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(bupaR) ##buisness process analytics
library(processanimateR) ##animates process
# Create performance time flags ------------------------------------------------
<- data.frame(value = c(0,2,4,8,16)) %>%
my_flags mutate(day = days(value)) #convert numeric value into days
This will change the colour of the token at 0, 2, 4, 8, and 16 days.
crossing()
joins the cases from patients
to
my_flags
and creates all possible combinations.
# Create timestamps of flags ----------------------------------------------
<- patients %>%
my_timeflags %>%
cases crossing(my_flags) %>% ##similar to a SQL outer join
mutate(time = start_timestamp + day) %>%
filter(time <= complete_timestamp) %>%
select("case" = patient,time,value) ##must be case, time, value
The data for the token_scale()
must have the column
headings ‘case, time, value’.
Without setting domain = my_flags$value
the flags follow
alphabetic order (e.g. 0, 16, 2, 4, 8) rather than the numeric order we
want. See d3-legend for further information.
# Animate process ---------------------------------------------------------
%>%
patients animate_process(mode ="absolute",
jitter=10,
legend = "color",
mapping = token_aes(
color = token_scale(my_timeflags
scale = "ordinal"
, domain = my_flags$value
, range = rev(RColorBrewer::brewer.pal(5,"Spectral"))
, )))
The colors can be adjusted by the range
argument. In
this case the scale is reversed with rev()
to go from blue
to red. See RColorBrewer::brewer.pal.info
for all options:
Acknowledgement
Thanks to Dominic Rowney
for this nice example of an advanced processanimateR
usage.
The original example code can be found here.
Several aesthetics of tokens (color
, size
,
opacity
, image
) can be dynamically determined
based on event log attributes using D3 scales.
animate_process(patients,
legend = "color",
mapping = token_aes(color = token_scale("employee",
scale = "ordinal",
range = RColorBrewer::brewer.pal(8, "Paired"))))
animate_process(sample_n(traffic_fines, 1000) %>% filter_trace_frequency(percentage = 0.95),
mode = "relative",
legend = "color",
mapping = token_aes(color = token_scale("amount",
scale = "linear",
range = c("yellow","red"))))
animate_process(patients,
mapping = token_aes(color = token_scale("time",
scale = "time",
range = c("blue","red"))))
Source: https://bupaverse.github.io/processanimateR/
Read more:
Copyright © 2023 bupaR - Hasselt University