├── _pkgdown.yml ├── inst ├── python │ └── pm4pytools │ │ ├── __init__.py │ │ └── log.py └── tmp │ ├── bpmn.Rds │ └── testing.Rmd ├── tests ├── testthat.R └── testthat │ ├── test_conformance.R │ ├── test_bridge.R │ └── test_discovery.R ├── docs ├── pkgdown.yml ├── link.svg ├── docsearch.js ├── pkgdown.js ├── reference │ ├── test.pnml │ ├── reexports.html │ ├── version.html │ ├── pm4py_version.html │ ├── pm4py_available.html │ ├── pm4py.html │ ├── parameters.html │ ├── as_pm4py_marking.html │ ├── petrinet_check_wfnet.html │ ├── write_pnml.html │ ├── petrinet_check_relaxed_soundness.html │ └── install_pm4py.html ├── 404.html ├── authors.html ├── pkgdown.css └── news │ └── index.html ├── .gitignore ├── .Rbuildignore ├── man ├── pm4py_version.Rd ├── pm4py_available.Rd ├── version.Rd ├── as_pm4py_marking.Rd ├── reexports.Rd ├── petrinet_check_wfnet.Rd ├── write_pnml.Rd ├── petrinet_check_relaxed_soundness.Rd ├── parameters.Rd ├── petrinet_synchronous_product.Rd ├── diagnostics_token_based_replay.Rd ├── deprecated.Rd ├── fitness_token_based_replay.Rd ├── discover_alpha_plus.Rd ├── precision_token_based_replay.Rd ├── discover_inductive_bpmn.Rd ├── diagnostics_alignments.Rd ├── pm4py.Rd ├── precision_alignments.Rd ├── discover_alpha.Rd ├── fitness_alignments.Rd ├── install_pm4py.Rd ├── conformance.Rd ├── discover_inductive.Rd └── evaluation.Rd ├── NEWS.md ├── pm4py.Rproj ├── R ├── version.R ├── package.R ├── io.R ├── utils.R ├── discover_alpha_plus.R ├── deprecated.R ├── parameters.R ├── pm4py.R ├── fitness_token_based_replay.R ├── install.R ├── precision_token_based_replay.R ├── diagnostics_token_based_replay.R ├── discover_inductive.R ├── fitness_alignments.R ├── discover_alpha.R ├── precision_alignments.R ├── diagnostics_alignments.R ├── petrinet.R ├── conformance.R ├── evaluation.R ├── discover_inductive_bpmn.R └── bridge.R ├── .travis.yml ├── DESCRIPTION ├── NAMESPACE └── README.md /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inst/python/pm4pytools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inst/tmp/bpmn.Rds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bupaverse/pm4py/HEAD/inst/tmp/bpmn.Rds -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(pm4py) 3 | 4 | test_check("pm4py") 5 | -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: 2.7.2 2 | pkgdown: 1.4.1 3 | pkgdown_sha: ~ 4 | articles: [] 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rhistory 2 | .RData 3 | .Rproj.user 4 | cran-comments.md 5 | inst/python/pm4pytools/__pycache__/ 6 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^CRAN-RELEASE$ 2 | ^cran-comments\.md$ 3 | ^docs$ 4 | ^_pkgdown\.yml$ 5 | ^LICENSE\.md$ 6 | ^.*\.Rproj$ 7 | ^\.Rproj\.user$ 8 | ^\.travis\.yml$ 9 | -------------------------------------------------------------------------------- /man/pm4py_version.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package.R 3 | \name{pm4py_version} 4 | \alias{pm4py_version} 5 | \title{Returns PM4Py version used} 6 | \usage{ 7 | pm4py_version() 8 | } 9 | \value{ 10 | package_version S3 class 11 | } 12 | \description{ 13 | Returns PM4Py version used 14 | } 15 | -------------------------------------------------------------------------------- /man/pm4py_available.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package.R 3 | \name{pm4py_available} 4 | \alias{pm4py_available} 5 | \title{Is the PM4Py module available} 6 | \usage{ 7 | pm4py_available() 8 | } 9 | \value{ 10 | \code{TRUE} is PM4Py is installed 11 | } 12 | \description{ 13 | Is the PM4Py module available 14 | } 15 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | 2 | # pm4py 2.0.0 3 | 4 | * discovery_alpha has been replaced with discover_alpha and discover_alpha_plus 5 | 6 | 7 | 8 | 9 | 10 | # pm4py 1.2.8.9000 11 | 12 | * Update to PM4Py v1.2.8 13 | 14 | # pm4py 1.2.7 15 | 16 | * Update to PM4Py v1.2.7 17 | * Some Petrinet functions added 18 | 19 | # pm4py 1.0.1 20 | 21 | * Initial release compatible with PM4Py v1.1.1 22 | -------------------------------------------------------------------------------- /man/version.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/package.R 3 | \name{version} 4 | \alias{version} 5 | \title{This function is deprecated, please use \code{\link{pm4py_version}}.} 6 | \usage{ 7 | version() 8 | } 9 | \value{ 10 | package_version S3 class 11 | } 12 | \description{ 13 | This function is deprecated, please use \code{\link{pm4py_version}}. 14 | } 15 | -------------------------------------------------------------------------------- /pm4py.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: ISO8859-1 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace,vignette 22 | -------------------------------------------------------------------------------- /man/as_pm4py_marking.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bridge.R 3 | \name{as_pm4py_marking} 4 | \alias{as_pm4py_marking} 5 | \title{Convert to a PM4Py marking} 6 | \usage{ 7 | as_pm4py_marking(x, petrinet) 8 | } 9 | \arguments{ 10 | \item{x}{A character vector with (possible duplicate) place identifiers.} 11 | 12 | \item{petrinet}{A PM4Py Petri net.} 13 | } 14 | \value{ 15 | PM4Py marking object 16 | } 17 | \description{ 18 | Converts a character vector of place identifiers to a PM4Py marking object. 19 | } 20 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bridge.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{r_to_py} 7 | \alias{py_to_r} 8 | \title{Objects exported from other packages} 9 | \keyword{internal} 10 | \description{ 11 | These objects are imported from other packages. Follow the links 12 | below to see their documentation. 13 | 14 | \describe{ 15 | \item{reticulate}{\code{\link[reticulate:r-py-conversion]{py_to_r}}, \code{\link[reticulate:r-py-conversion]{r_to_py}}} 16 | }} 17 | 18 | -------------------------------------------------------------------------------- /R/version.R: -------------------------------------------------------------------------------- 1 | # 2 | # This file acts both as R source file as well as file defining environment variables that are sources in the CI/CD pipeline. 3 | # 4 | 5 | # Whether the version is a package or a development version 6 | PM4PY_DEVELOPMENT=FALSE 7 | 8 | # PM4PY dependencies for development versions 9 | PM4PY_DEVELOPMENT_DEPENDENCIES="https://raw.githubusercontent.com/pm4py/pm4py-source/release/requirements.txt" 10 | 11 | # PM4PY pip installer literal. For example: 12 | # pm4py==1.0.21 13 | # or a Github link to a specific branch/tag/hash: 14 | # https://github.com/pm4py/pm4py-source/archive/develop.zip 15 | PM4PY_PIP="pm4py==2.7.2" 16 | -------------------------------------------------------------------------------- /man/petrinet_check_wfnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/petrinet.R 3 | \name{petrinet_check_wfnet} 4 | \alias{petrinet_check_wfnet} 5 | \title{Check Workflow net property} 6 | \usage{ 7 | petrinet_check_wfnet(pn, convert = TRUE) 8 | } 9 | \arguments{ 10 | \item{pn}{Petri net} 11 | 12 | \item{convert}{\code{TRUE} to automatically convert Python objects to their R equivalent. If you pass \code{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 13 | } 14 | \value{ 15 | A single logical 16 | } 17 | \description{ 18 | Checks if the Petri net is a Workflow net 19 | } 20 | -------------------------------------------------------------------------------- /tests/testthat/test_conformance.R: -------------------------------------------------------------------------------- 1 | context("Conformance") 2 | 3 | library(bupaR) 4 | 5 | test_that("Alignment", { 6 | pm4py:::skip_if_no_pm4py() 7 | 8 | data("patients") 9 | 10 | patients <- patients %>% filter(registration_type == "complete") 11 | 12 | net <- discover_inductive(patients) 13 | 14 | # test two times for proper garbage collection 15 | res <- lapply(1:2, function(x) { 16 | a <- diagnostics_alignments(patients, 17 | net, 18 | convert = TRUE) 19 | numTraces <- n_cases(patients) 20 | numAlignments <- nrow(a) 21 | expect_equal(numAlignments, numTraces) 22 | return(a) 23 | }) 24 | 25 | expect_length(res, 2) 26 | 27 | }) 28 | -------------------------------------------------------------------------------- /man/write_pnml.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/io.R 3 | \name{write_pnml} 4 | \alias{write_pnml} 5 | \title{Write Petri net as PNML} 6 | \usage{ 7 | write_pnml(petrinet, file, initial_marking = NULL, final_marking = NULL) 8 | } 9 | \arguments{ 10 | \item{petrinet}{A bupaR or PM4PY Petri net.} 11 | 12 | \item{file}{File name of the PNML file} 13 | 14 | \item{initial_marking}{A R vector with the place identifiers of the initial marking or a PM4PY marking. 15 | By default the initial marking of the bupaR Petri net will be used if available.} 16 | 17 | \item{final_marking}{A R vector with the place identifiers of the final marking or a PM4PY marking.} 18 | } 19 | \value{ 20 | .pnml file written to system 21 | } 22 | \description{ 23 | Write Petri net as PNML 24 | } 25 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /man/petrinet_check_relaxed_soundness.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/petrinet.R 3 | \name{petrinet_check_relaxed_soundness} 4 | \alias{petrinet_check_relaxed_soundness} 5 | \title{Check Relaxed soundness property} 6 | \usage{ 7 | petrinet_check_relaxed_soundness(pn, im = NULL, fm = NULL, convert = TRUE) 8 | } 9 | \arguments{ 10 | \item{pn}{Petri net} 11 | 12 | \item{im}{Initial marking of the Petri net (optional for workflow nets)} 13 | 14 | \item{fm}{Final marking of the Petri net (optional for workflow nets)} 15 | 16 | \item{convert}{\code{TRUE} to automatically convert Python objects to their R equivalent. If you pass \code{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 17 | } 18 | \value{ 19 | A single logical 20 | } 21 | \description{ 22 | Checks if the Petri net is relaxed sound 23 | } 24 | -------------------------------------------------------------------------------- /R/package.R: -------------------------------------------------------------------------------- 1 | #' Returns PM4Py version used 2 | #' 3 | #' @return package_version S3 class 4 | #' @export 5 | #' 6 | pm4py_version <- function() { 7 | if (pm4py_available()) { 8 | ver <- pm4py$`__version__` 9 | ver <- regmatches(ver, regexec("^([0-9\\.]+).*$", ver))[[1]][[2]] 10 | package_version(ver) 11 | } else { 12 | stop("No PM4Py installation found, please install it by using `pm4py::install_pm4py()`!") 13 | } 14 | } 15 | 16 | #' This function is deprecated, please use \code{\link{pm4py_version}}. 17 | #' 18 | #' @return package_version S3 class 19 | #' @export 20 | #' 21 | version <- function() { 22 | .Deprecated("pm4py_version") 23 | pm4py_version() 24 | } 25 | 26 | 27 | #' Is the PM4Py module available 28 | #' 29 | #' @return `TRUE` is PM4Py is installed 30 | #' @export 31 | #' 32 | #' 33 | pm4py_available <- function() { 34 | py_module_available("pm4py") 35 | } 36 | -------------------------------------------------------------------------------- /inst/python/pm4pytools/log.py: -------------------------------------------------------------------------------- 1 | import pandas 2 | import pm4py 3 | 4 | from pm4py import util as pmutil 5 | from pm4py.objects.log.log import EventLog, Trace 6 | from pm4py.objects.log.util import xes 7 | 8 | def get_trace_ids(log, parameters): 9 | if pmutil.constants.PARAMETER_CONSTANT_CASEID_KEY in parameters: 10 | caseid = parameters[pmutil.constants.PARAMETER_CONSTANT_CASEID_KEY] 11 | else: 12 | raise ValueError("Missing case id parameter") 13 | if isinstance(log, pandas.core.frame.DataFrame): 14 | return log[caseid].unique() 15 | if isinstance(log, pm4py.objects.log.log.EventStream) and (not isinstance(log, pm4py.objects.log.log.EventLog)): 16 | ids = set() 17 | for event in log: 18 | ids.add(event[caseid]) 19 | return ids 20 | else: 21 | # should be an event log 22 | ids = [] 23 | for trace in log: 24 | if (trace.attributes[xes.DEFAULT_TRACEID_KEY] != None): 25 | ids.append(trace.attributes[xes.DEFAULT_TRACEID_KEY]) 26 | return ids 27 | 28 | 29 | -------------------------------------------------------------------------------- /man/parameters.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/parameters.R 3 | \name{parameters} 4 | \alias{parameters} 5 | \alias{param_activity_key} 6 | \alias{param_attribute_key} 7 | \alias{param_timestamp_key} 8 | \alias{param_caseid_key} 9 | \alias{param_resource_key} 10 | \alias{default_parameters} 11 | \title{PM4Py parameter keys} 12 | \usage{ 13 | param_activity_key(value) 14 | 15 | param_attribute_key(value) 16 | 17 | param_timestamp_key(value) 18 | 19 | param_caseid_key(value) 20 | 21 | param_resource_key(value) 22 | 23 | default_parameters(eventlog) 24 | } 25 | \arguments{ 26 | \item{value}{The value to add to the list.} 27 | 28 | \item{eventlog}{A bupaR or PM4PY event log.} 29 | } 30 | \value{ 31 | a list with the parameter key/value pair 32 | } 33 | \description{ 34 | Convenience methods to use as PM4Py parameter keys. 35 | } 36 | \examples{ 37 | param_activity_key("activity") 38 | 39 | library(eventdataR) 40 | data(patients) 41 | default_parameters(patients) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | 5 | dist: xenial 6 | r: 7 | - oldrel 8 | - release 9 | - devel 10 | latex: false 11 | 12 | cache: 13 | packages: true 14 | directories: 15 | - $HOME/.cache/pip 16 | 17 | before_install: 18 | - sudo apt-get update 19 | - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; 20 | - bash miniconda.sh -b -p $HOME/miniconda 21 | - rm miniconda.sh 22 | - export PATH="$HOME/miniconda/bin:$PATH" 23 | - hash -r 24 | - conda config --set always_yes yes --set changeps1 no 25 | - conda update -q conda 26 | - conda info -a 27 | - conda create -q -n r-reticulate python=3.7 pip numpy 28 | 29 | before_script: 30 | - source activate r-reticulate 31 | - ls 32 | - set -a 33 | - source R/version.R 34 | - set +a 35 | - if [ "$PM4PY_DEVELOPMENT" == "TRUE" ]; then pip install -r $PM4PY_DEVELOPMENT_DEPENDENCIES; else echo "Skipping"; fi 36 | - pip install $PM4PY_PIP 37 | -------------------------------------------------------------------------------- /man/petrinet_synchronous_product.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/petrinet.R 3 | \name{petrinet_synchronous_product} 4 | \alias{petrinet_synchronous_product} 5 | \title{Synchronous product Petri net} 6 | \usage{ 7 | petrinet_synchronous_product( 8 | pn1, 9 | im1, 10 | fm1, 11 | pn2, 12 | im2, 13 | fm2, 14 | skip = ">>", 15 | convert = TRUE 16 | ) 17 | } 18 | \arguments{ 19 | \item{pn1}{First Petri net} 20 | 21 | \item{im1}{Initial marking of the first Petri net} 22 | 23 | \item{fm1}{Final marking of the first Petri net} 24 | 25 | \item{pn2}{Second Petri net} 26 | 27 | \item{im2}{Initial marking of the second Petri net} 28 | 29 | \item{fm2}{Final marking of the second Petri net} 30 | 31 | \item{skip}{Symbol to be used as skip} 32 | 33 | \item{convert}{\code{TRUE} to automatically convert Python objects to their R equivalent. If you pass \code{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 34 | } 35 | \value{ 36 | A Petri net. 37 | } 38 | \description{ 39 | Constructs the synchronous product net of two given Petri nets. 40 | } 41 | -------------------------------------------------------------------------------- /inst/tmp/testing.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "test" 3 | author: "Ivan" 4 | date: "2023-01-17" 5 | output: html_document 6 | --- 7 | 8 | ```{r setup, include=FALSE} 9 | knitr::opts_chunk$set(echo = TRUE) 10 | ``` 11 | 12 | ```{r} 13 | library(eventdataR) 14 | library(magrittr) 15 | library(tidyverse) 16 | library(reticulate) 17 | ``` 18 | 19 | ```{r} 20 | library(pm4py) 21 | library(dplyr) 22 | library(eventdataR) 23 | patients_completes <- patients[patients$registration_type == "complete", ] 24 | patients_completes %>% mutate(handling = as.character(handling)) -> patients_completes 25 | 26 | pm4py::discovery_inductive(patients_completes) -> tmp 27 | 28 | # doesnt work 29 | conformance_diagnostics_alignments(eventlog = patients_completes, 30 | tmp) 31 | 32 | # works 33 | fitness_alignments(eventlog = patients_completes, 34 | tmp) 35 | # works 36 | precision_alignments(eventlog = patients_completes, 37 | tmp) 38 | # works 39 | conformance_token_based_replay(eventlog = patients_completes, 40 | tmp) 41 | 42 | ``` 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /man/diagnostics_token_based_replay.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/diagnostics_token_based_replay.R 3 | \name{diagnostics_token_based_replay} 4 | \alias{diagnostics_token_based_replay} 5 | \title{Apply \strong{the token-based replay} algorithm between a log and a process model} 6 | \usage{ 7 | diagnostics_token_based_replay(log, marked_petrinet, convert = TRUE) 8 | } 9 | \arguments{ 10 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 11 | 12 | \item{marked_petrinet}{A Marked Petrinet as defined by petrinetR, e.g. the output of \link{discover_inductive} or \link{discover_alpha}.} 13 | 14 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 15 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 16 | } 17 | \value{ 18 | Token-based-replay diagnostics. 19 | } 20 | \description{ 21 | Apply token-based replay for conformance checking analysis. The methods return the full token-based-replay diagnostics. 22 | } 23 | -------------------------------------------------------------------------------- /R/io.R: -------------------------------------------------------------------------------- 1 | #' Write Petri net as PNML 2 | #' 3 | #' @param petrinet A bupaR or PM4PY Petri net. 4 | #' @param file File name of the PNML file 5 | #' @param initial_marking A R vector with the place identifiers of the initial marking or a PM4PY marking. 6 | #' By default the initial marking of the bupaR Petri net will be used if available. 7 | #' @param final_marking A R vector with the place identifiers of the final marking or a PM4PY marking. 8 | #' @return .pnml file written to system 9 | #' @export 10 | write_pnml <- function(petrinet, 11 | file, 12 | initial_marking = NULL, 13 | final_marking = NULL) { 14 | pm4py_export <- import("pm4py", convert = FALSE) 15 | 16 | py_pn <- as_py_value(petrinet) 17 | 18 | if (!is.null(final_marking)) { 19 | py_final <- as_pm4py_marking(final_marking, py_pn) 20 | } else { 21 | py_final <- NULL 22 | } 23 | 24 | pm4py_export$write_pnml(petri_net = py_pn, 25 | file_path = file, 26 | initial_marking = as_pm4py_marking(initial_marking, py_pn), 27 | final_marking = py_final) 28 | invisible(petrinet) 29 | } 30 | -------------------------------------------------------------------------------- /man/deprecated.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deprecated.R 3 | \name{deprecated} 4 | \alias{deprecated} 5 | \alias{discovery_alpha} 6 | \alias{variant_alpha_classic} 7 | \alias{variant_alpha_plus} 8 | \alias{variant_inductive_imdfb} 9 | \alias{variant_inductive_only_dfg} 10 | \title{Deprecated functions} 11 | \usage{ 12 | discovery_alpha( 13 | eventlog, 14 | parameters = default_parameters(eventlog), 15 | variant = variant_alpha_classic(), 16 | convert = TRUE 17 | ) 18 | 19 | variant_alpha_classic() 20 | 21 | variant_alpha_plus() 22 | 23 | variant_inductive_imdfb() 24 | 25 | variant_inductive_only_dfg() 26 | } 27 | \arguments{ 28 | \item{eventlog}{Eventlog object} 29 | 30 | \item{parameters}{Parameters} 31 | 32 | \item{variant}{Variant} 33 | 34 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 35 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 36 | } 37 | \value{ 38 | Marked petrinet for discovery_alpha. No return value for variant_ functions 39 | } 40 | \description{ 41 | Deprecated functions 42 | } 43 | -------------------------------------------------------------------------------- /man/fitness_token_based_replay.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitness_token_based_replay.R 3 | \name{fitness_token_based_replay} 4 | \alias{fitness_token_based_replay} 5 | \title{Measure Token-Based Replay Fitness} 6 | \usage{ 7 | fitness_token_based_replay(log, marked_petrinet, convert = TRUE) 8 | } 9 | \arguments{ 10 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 11 | 12 | \item{marked_petrinet}{A Marked Petrinet as defined by petrinetR, e.g. the output of \link{discover_inductive} or \link{discover_alpha}.} 13 | 14 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 15 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 16 | } 17 | \value{ 18 | List with fitness measures. 19 | } 20 | \description{ 21 | Measure Token-Based Replay Fitness 22 | } 23 | \examples{ 24 | \dontrun{ 25 | library(pm4py) 26 | library(eventdataR) 27 | 28 | model <- discover_alpha(patients) 29 | fitness_token_based_replay(patients, model) 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /man/discover_alpha_plus.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/discover_alpha_plus.R 3 | \name{discover_alpha_plus} 4 | \alias{discover_alpha_plus} 5 | \alias{discover_alpha_plus.eventlog} 6 | \title{Discover petrinet using Alpha plus Algorithm} 7 | \usage{ 8 | discover_alpha_plus(log, convert = TRUE) 9 | 10 | \method{discover_alpha_plus}{eventlog}(log, convert = TRUE) 11 | } 12 | \arguments{ 13 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 14 | 15 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 16 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 17 | } 18 | \value{ 19 | Marked petri net, i.e. a named list with elements \code{petrinet}, \code{initial_marking}, and \code{final_marking} or the original Python object. 20 | } 21 | \description{ 22 | Discover petrinet using Alpha plus Algorithm 23 | } 24 | \section{Methods (by class)}{ 25 | \itemize{ 26 | \item \code{discover_alpha_plus(eventlog)}: Discovery petri net based on event log 27 | 28 | }} 29 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | 2 | #' @importFrom dplyr tibble 3 | #' @importFrom stringr str_remove 4 | #' @importFrom purrr map map_chr 5 | 6 | prepare_pn_with_markings <- function(model, convert = FALSE) { 7 | if (convert) { 8 | if (inherits(model, "python.builtin.object")) { 9 | model <- py_to_r(model) # Python tuple was not auto-converted 10 | } 11 | names(model) <- c("petrinet","initial_marking", "final_marking") 12 | model$petrinet$marking <- model$initial_marking 13 | } 14 | model 15 | } 16 | 17 | ensure_str <- function(x) { 18 | # Taking the Python string representation, but bupaR does not like ' in names 19 | # TODO fix in bupaR 20 | if (is.null(x)) { 21 | NA_character_ 22 | } else { 23 | stringr::str_remove_all(py_str(as_py_value(x)), stringr::fixed("'")) 24 | } 25 | } 26 | 27 | as_r_value <- function(x) { 28 | 29 | if (inherits(x, "python.builtin.object")) 30 | py_to_r(x) 31 | else 32 | x 33 | } 34 | 35 | as_py_value <- function(x) { 36 | 37 | if (inherits(x, "python.builtin.object")) 38 | x 39 | else 40 | r_to_py(x) 41 | } 42 | 43 | skip_if_no_pm4py <- function() { 44 | if (!pm4py_available()) 45 | testthat::skip("pm4py not available for testing") 46 | } 47 | -------------------------------------------------------------------------------- /man/precision_token_based_replay.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/precision_token_based_replay.R 3 | \name{precision_token_based_replay} 4 | \alias{precision_token_based_replay} 5 | \title{Measure Token-Based Replay Precision} 6 | \usage{ 7 | precision_token_based_replay(log, marked_petrinet, convert = TRUE) 8 | } 9 | \arguments{ 10 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 11 | 12 | \item{marked_petrinet}{A Marked Petrinet as defined by petrinetR, e.g. the output of \link{discover_inductive} or \link{discover_alpha}.} 13 | 14 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 15 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 16 | } 17 | \value{ 18 | Numeric vector of length one containing precision measure. 19 | } 20 | \description{ 21 | Measure Token-Based Replay Precision 22 | } 23 | \examples{ 24 | \dontrun{ 25 | library(pm4py) 26 | library(eventdataR) 27 | 28 | model <- discover_alpha(patients) 29 | precision_token_based_replay(patients, model) 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /man/discover_inductive_bpmn.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/discover_inductive_bpmn.R 3 | \name{discover_inductive_bpmn} 4 | \alias{discover_inductive_bpmn} 5 | \title{Discover BPMN models using Inductive Miner} 6 | \usage{ 7 | discover_inductive_bpmn( 8 | log, 9 | multi_processing = FALSE, 10 | noise_threshold = 0, 11 | convert = TRUE 12 | ) 13 | } 14 | \arguments{ 15 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 16 | 17 | \item{multi_processing}{\link{logical} (default \code{\link{FALSE}}): Disables if \code{FALSE}, enables if \code{TRUE} multiprocessing in inductive miner.} 18 | 19 | \item{noise_threshold}{\link{numeric} (default: 0): noise threshold. 20 | For Inductive Miner currently only \code{variant_inductive_imdfb} is supported.} 21 | 22 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 23 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 24 | } 25 | \value{ 26 | A BPMN object as defined by BPMNR 27 | } 28 | \description{ 29 | Discover BPMN models using Inductive Miner 30 | } 31 | -------------------------------------------------------------------------------- /tests/testthat/test_bridge.R: -------------------------------------------------------------------------------- 1 | context("Bridge") 2 | 3 | library(bupaR) 4 | data("patients") 5 | data("sepsis") 6 | data("traffic_fines") 7 | data("hospital_billing") 8 | 9 | patrick::with_parameters_test_that("Event Log", { 10 | skip("skip bridge test") 11 | pm4py:::skip_if_no_pm4py() 12 | 13 | log <- log[[1]] 14 | 15 | py_log <- r_to_py(log) 16 | expect_true("pandas.core.frame.DataFrame" %in% class(py_log)) 17 | 18 | r_log <- pm4py$objects$conversion$log$converter$apply(py_log, 19 | parameters = default_parameters(log), 20 | variant = "to_event_log") 21 | 22 | expect_true(nrow(r_log) == nrow(log)) 23 | expect_equal(n_cases(log), length(unique(r_log[[bupaR::case_id(log)]]))) 24 | expect_equal(n_activities(log), length(unique(r_log[[bupaR::activity_id(log)]]))) 25 | expect_equal(n_resources(log), length(unique(r_log[[bupaR::resource_id(log)]]))) 26 | expect_equal(length(log), length(r_log)) 27 | 28 | }, patrick::cases( 29 | "patients" = list(log = list(patients)), 30 | "sepsis" = list(log = list(sepsis)), 31 | "traffic_fines" = list(log = list(traffic_fines)), 32 | "hospital_billing" = list(log = list(hospital_billing)) 33 | ) 34 | ) 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /man/diagnostics_alignments.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/diagnostics_alignments.R 3 | \name{diagnostics_alignments} 4 | \alias{diagnostics_alignments} 5 | \title{Apply the alignments algorithm between a log and a process model} 6 | \usage{ 7 | diagnostics_alignments( 8 | log, 9 | marked_petrinet, 10 | multi_processing = FALSE, 11 | convert = TRUE 12 | ) 13 | } 14 | \arguments{ 15 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 16 | 17 | \item{marked_petrinet}{A Marked Petrinet as defined by petrinetR, e.g. the output of \link{discover_inductive} or \link{discover_alpha}.} 18 | 19 | \item{multi_processing}{\link{logical} (default \code{\link{FALSE}}): Disables if \code{FALSE}, enables if \code{TRUE} multiprocessing in inductive miner.} 20 | 21 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 22 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 23 | } 24 | \value{ 25 | alignment diagnostics. 26 | } 27 | \description{ 28 | Alignment-based replay aims to find one of the best alignment between the trace and the model. 29 | } 30 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: pm4py 2 | Type: Package 3 | Title: Interface to the 'PM4py' Process Mining Library 4 | Version: 2.0.0 5 | Authors@R: c(person("Felix", "Mannhardt", email = "f.mannhardt@tue.nl", role = c("aut", "cre")), 6 | person("Gert","Janssenswillen", email = "gert.janssenswillen@uhasselt.be", role = c("ctb")), 7 | person("Ivan","Esin", email = "ivan.esin99@gmail.com", role = c("ctb"))) 8 | Description: Interface to 'PM4py', a process mining library 9 | in 'Python'. This package uses the 'reticulate' 10 | package to act as a bridge 11 | between 'PM4Py' (Berti et al. (2019) ) 12 | and the 'R' package 'bupaR' (Janssenswillen et al. (2019) ). 13 | It provides several process 14 | discovery algorithms, 15 | evaluation measures, and alignments. 16 | License: GPL-3 17 | Encoding: UTF-8 18 | SystemRequirements: Python (>= 3.6) 19 | Imports: 20 | reticulate (>= 1.11), 21 | bupaR, 22 | petrinetR, 23 | purrr, 24 | stringr, 25 | dplyr, 26 | bpmnR, 27 | lifecycle, 28 | tidyr, 29 | cli, 30 | forcats 31 | Roxygen: list(markdown = TRUE) 32 | RoxygenNote: 7.2.3 33 | Suggests: 34 | testthat, 35 | patrick, 36 | eventdataR 37 | URL: https://github.com/bupaverse/pm4py 38 | BugReports: https://github.com/bupaverse/pm4py/issues 39 | -------------------------------------------------------------------------------- /man/pm4py.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/pm4py.R 3 | \docType{data} 4 | \name{pm4py} 5 | \alias{pm4py} 6 | \title{PM4PY for R} 7 | \format{ 8 | An object of class \code{python.builtin.module} (inherits from \code{python.builtin.object}) of length 1. 9 | } 10 | \usage{ 11 | pm4py 12 | } 13 | \description{ 14 | This package provides access to the Python Process Mining library PM4PY in R 15 | and provides conversion between bupaR and PM4PY data structures. 16 | } 17 | \details{ 18 | To use this package, you need to have a Python environment (Conda or virtualenv) 19 | installed and install the PM4PY package and its dependencies. You can use 20 | the convenience function \link{install_pm4py} to let \code{reticulate} take care of 21 | install the right version. See the documentation of this function for further 22 | information. 23 | 24 | When loaded, the object \code{pm4py} provides the low-level interface to the main 25 | PM4PY module. Use \code{$} to access sub modules of PM4PY as described in the 26 | \code{reticulate} documentation: 27 | 28 | \code{vignette("calling_python", package = "reticulate")} 29 | 30 | For parts of PM4PY wrapper functions are provided to transparently convert 31 | parameters and results to and from the corresponding bupaR S3 classes. 32 | } 33 | \keyword{datasets} 34 | -------------------------------------------------------------------------------- /R/discover_alpha_plus.R: -------------------------------------------------------------------------------- 1 | 2 | #' @title Discover petrinet using Alpha plus Algorithm 3 | #' @inheritParams discover_alpha 4 | #' @return Marked petri net, i.e. a named list with elements `petrinet`, `initial_marking`, and `final_marking` or the original Python object. 5 | #' @export 6 | #' 7 | discover_alpha_plus <- function(log, convert = TRUE) { 8 | UseMethod("discover_alpha_plus") 9 | } 10 | 11 | #' @describeIn discover_alpha_plus Discovery petri net based on event log 12 | #' @export 13 | 14 | discover_alpha_plus.eventlog <- function(log, 15 | convert = TRUE) { 16 | pm4py_discovery <- reticulate::import("pm4py", convert = convert) 17 | 18 | if(n_events(log) > n_activity_instances(log)) { 19 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 20 | 21 | log %>% 22 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 23 | } 24 | log %>% 25 | mutate(across(activity_id(log), as.character)) -> log 26 | 27 | model <- pm4py_discovery$discover_petri_net_alpha_plus(as_py_value(log), 28 | activity_key = activity_id(log), 29 | timestamp_key = timestamp(log), 30 | case_id_key = case_id(log)) 31 | create_marked_PN(model[[1]], model[[2]], model[[3]]) 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/testthat/test_discovery.R: -------------------------------------------------------------------------------- 1 | context("Discovery") 2 | 3 | library(bupaR) 4 | 5 | test_that("Inductive miner", { 6 | pm4py:::skip_if_no_pm4py() 7 | 8 | data("patients") 9 | patients %>% filter(registration_type == "complete") -> patients 10 | # net <- expect_silent(discover_inductive(patients, convert = FALSE)) 11 | # expect_true("pm4py.objects.petri.petrinet.PetriNet" %in% class(net[0])) 12 | # expect_true("pm4py.objects.petri.petrinet.Marking" %in% class(net[1])) 13 | # expect_true("pm4py.objects.petri.petrinet.Marking" %in% class(net[2])) 14 | 15 | net <- discover_inductive(patients, convert = TRUE) 16 | expect_s3_class(net$petrinet, "petrinet") 17 | expect_length(net$initial_marking, 1) 18 | expect_length(net$final_marking, 1) 19 | 20 | }) 21 | 22 | 23 | test_that("Alpha miner", { 24 | pm4py:::skip_if_no_pm4py() 25 | 26 | data("patients") 27 | patients %>% filter(registration_type == "complete") -> patients 28 | 29 | # net <- expect_silent(discover_alpha(patients, convert = FALSE)) 30 | # expect_true("pm4py.objects.petri.petrinet.PetriNet" %in% class(net[0])) 31 | # expect_true("pm4py.objects.petri.petrinet.Marking" %in% class(net[1])) 32 | # expect_true("pm4py.objects.petri.petrinet.Marking" %in% class(net[2])) 33 | 34 | net <- discover_alpha(patients, convert = TRUE) 35 | expect_s3_class(net$petrinet, "petrinet") 36 | expect_length(net$initial_marking, 1) 37 | expect_length(net$final_marking, 1) 38 | 39 | }) 40 | -------------------------------------------------------------------------------- /man/precision_alignments.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/precision_alignments.R 3 | \name{precision_alignments} 4 | \alias{precision_alignments} 5 | \title{Apply the \strong{precision} alignments algorithm between a log and a process model} 6 | \usage{ 7 | precision_alignments( 8 | log, 9 | marked_petrinet, 10 | multi_processing = FALSE, 11 | convert = TRUE 12 | ) 13 | } 14 | \arguments{ 15 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 16 | 17 | \item{marked_petrinet}{A Marked Petrinet as defined by petrinetR, e.g. the output of \link{discover_inductive} or \link{discover_alpha}.} 18 | 19 | \item{multi_processing}{\link{logical} (default \code{\link{FALSE}}): Disables if \code{FALSE}, enables if \code{TRUE} multiprocessing in inductive miner.} 20 | 21 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 22 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 23 | } 24 | \value{ 25 | precision metric. 26 | } 27 | \description{ 28 | Calculates the precision of the model w.r.t. the event log using alignments. 29 | } 30 | \examples{ 31 | \dontrun{ 32 | library(pm4py) 33 | library(eventdataR) 34 | 35 | model <- discover_alpha(patients) 36 | precision_alignments(patients, model) 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /man/discover_alpha.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/discover_alpha.R 3 | \name{discover_alpha} 4 | \alias{discover_alpha} 5 | \alias{discover_alpha.eventlog} 6 | \alias{discover_alpha.activitylog} 7 | \title{Discover petrinet using Alpha Algorithm} 8 | \usage{ 9 | discover_alpha(log, convert = TRUE) 10 | 11 | \method{discover_alpha}{eventlog}(log, convert = TRUE) 12 | 13 | \method{discover_alpha}{activitylog}(log, convert = TRUE) 14 | } 15 | \arguments{ 16 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 17 | 18 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 19 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 20 | } 21 | \value{ 22 | Marked petri net, i.e. a named list with elements \code{petrinet}, \code{initial_marking}, and \code{final_marking} or the original Python object. 23 | } 24 | \description{ 25 | Discover petrinet using Alpha Algorithm 26 | } 27 | \section{Methods (by class)}{ 28 | \itemize{ 29 | \item \code{discover_alpha(eventlog)}: Discovery petri net based on event log 30 | 31 | \item \code{discover_alpha(activitylog)}: Discovery petri net based on activity log 32 | 33 | }} 34 | \examples{ 35 | \dontrun{ 36 | library(pm4py) 37 | library(eventdataR) 38 | 39 | discover_alpha(patients) 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /man/fitness_alignments.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitness_alignments.R 3 | \name{fitness_alignments} 4 | \alias{fitness_alignments} 5 | \title{Apply the \strong{fitness} alignments algorithm between a log and a process model} 6 | \usage{ 7 | fitness_alignments( 8 | log, 9 | marked_petrinet, 10 | multi_processing = FALSE, 11 | convert = TRUE 12 | ) 13 | } 14 | \arguments{ 15 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 16 | 17 | \item{marked_petrinet}{A Marked Petrinet as defined by petrinetR, e.g. the output of \link{discover_inductive} or \link{discover_alpha}.} 18 | 19 | \item{multi_processing}{\link{logical} (default \code{\link{FALSE}}): Disables if \code{FALSE}, enables if \code{TRUE} multiprocessing in inductive miner.} 20 | 21 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 22 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 23 | } 24 | \value{ 25 | fitness alignments. 26 | } 27 | \description{ 28 | The calculation of the replay fitness aim to calculate how much of the behavior in the log is admitted by the process model. 29 | } 30 | \examples{ 31 | \dontrun{ 32 | library(pm4py) 33 | library(eventdataR) 34 | 35 | model <- discover_alpha(patients) 36 | fitness_alignments(patients, model) 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /man/install_pm4py.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/install.R 3 | \name{install_pm4py} 4 | \alias{install_pm4py} 5 | \title{Install PM4PY library} 6 | \usage{ 7 | install_pm4py(method = "auto", conda = "auto", version = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{method}{Installation method. By default, "auto" automatically finds a method that will work in the local environment. 11 | Change the default to force a specific installation method. Note that the "virtualenv" method is not available on Windows.} 12 | 13 | \item{conda}{Path to conda executable (or "auto" to find conda using the PATH and other conventional install locations).} 14 | 15 | \item{version}{Optional parameter overriding the PM4Py version that will be installed. Note that the R pm4py package was only tested against the default PM4Py version that will be installed.} 16 | 17 | \item{...}{Additional arguments passed to py_install().} 18 | } 19 | \value{ 20 | No return value, called for side effects. 21 | } 22 | \description{ 23 | Installs the \code{pm4py} package and its dependencies using \code{pip} since no 24 | Conda package is available. Further information on the parameters can 25 | be found in the \code{reticulate} package documentation: 26 | https://rstudio.github.io/reticulate/ 27 | In some cases (multiple Python versions installed) it might be useful to specify the exact path to the \code{conda} binary. 28 | } 29 | \details{ 30 | Additional requirements, for example, a C++ compiler and GraphViz might 31 | need to be installed to leverage all functionality. Please refer to the PM4Py documentation for details: 32 | https://pm4py.fit.fraunhofer.de/install 33 | } 34 | -------------------------------------------------------------------------------- /R/deprecated.R: -------------------------------------------------------------------------------- 1 | #' @title Deprecated functions 2 | #' 3 | #' @param eventlog Eventlog object 4 | #' @param parameters Parameters 5 | #' @param variant Variant 6 | #' @inheritParams discover_inductive 7 | #' @return Marked petrinet for discovery_alpha. No return value for variant_ functions 8 | #' @name deprecated 9 | #' @export 10 | discovery_alpha <- function(eventlog, 11 | parameters = default_parameters(eventlog), 12 | variant = variant_alpha_classic(), 13 | convert = TRUE) { 14 | lifecycle::deprecate_warn(when = "2.0.0", what = "discovery_alpha()", with = "discover_alpha()") 15 | 16 | 17 | discover_alpha(eventlog, convert = convert) 18 | 19 | } 20 | 21 | #' @rdname deprecated 22 | #' @export 23 | variant_alpha_classic <- function() { 24 | lifecycle::deprecate_warn(when = "2.0.0", what = "variant_alpha_classic()", with = "discover_alpha()") 25 | pm4py$algo$discovery$alpha$factory$ALPHA_VERSION_CLASSIC 26 | } 27 | 28 | #' @rdname deprecated 29 | #' @export 30 | variant_alpha_plus <- function() { 31 | lifecycle::deprecate_warn(when = "2.0.0", what = "variant_alpha_plus()", with = "discover_alpha_plus()") 32 | pm4py$algo$discovery$alpha$factory$ALPHA_VERSION_PLUS 33 | } 34 | 35 | #' @rdname deprecated 36 | #' @export 37 | variant_inductive_imdfb <- function() { 38 | lifecycle::deprecate_stop("2.0.0", "variant_inductive_only_dfg()") 39 | pm4py$algo$discovery$inductive$factory$IMDFB 40 | } 41 | 42 | 43 | #' @rdname deprecated 44 | #' @export 45 | variant_inductive_only_dfg <- function() { 46 | lifecycle::deprecate_stop("2.0.0", "variant_inductive_only_dfg()") 47 | pm4py$algo$discovery$inductive$factory$IMDFB 48 | } 49 | -------------------------------------------------------------------------------- /man/conformance.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conformance.R 3 | \name{conformance} 4 | \alias{conformance} 5 | \alias{conformance_alignment} 6 | \alias{variant_state_equation_a_star} 7 | \alias{variant_dijkstra_no_heuristics} 8 | \title{Conformance between an Event Log and a Petri net} 9 | \usage{ 10 | conformance_alignment( 11 | eventlog, 12 | petrinet, 13 | initial_marking, 14 | final_marking, 15 | parameters = default_parameters(eventlog), 16 | variant = variant_state_equation_a_star(), 17 | convert = TRUE 18 | ) 19 | 20 | variant_state_equation_a_star() 21 | 22 | variant_dijkstra_no_heuristics() 23 | } 24 | \arguments{ 25 | \item{eventlog}{A bupaR or PM4PY event log.} 26 | 27 | \item{petrinet}{A bupaR or PM4PY Petri net.} 28 | 29 | \item{initial_marking}{A R vector with the place identifiers of the initial marking or a PM4PY marking. 30 | By default the initial marking of the bupaR Petri net will be used if available.} 31 | 32 | \item{final_marking}{A R vector with the place identifiers of the final marking or a PM4PY marking.} 33 | 34 | \item{parameters}{PM4PY conformance parameter. 35 | By default the \code{activity_key} from the bupaR event log is specified using \link{param_activity_key}.} 36 | 37 | \item{variant}{The conformance variant to be used.} 38 | 39 | \item{convert}{\code{TRUE} to automatically convert Python objects to their R equivalent. 40 | If you pass \code{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 41 | } 42 | \value{ 43 | A data frame describing the conformance result. 44 | In case of \code{conformance_alignment} a data frame of log and model moves. 45 | } 46 | \description{ 47 | Conformance between an Event Log and a Petri net 48 | } 49 | -------------------------------------------------------------------------------- /man/discover_inductive.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/discover_inductive.R 3 | \name{discover_inductive} 4 | \alias{discover_inductive} 5 | \alias{discover_inductive.log} 6 | \title{Petri net discovery algorithms} 7 | \usage{ 8 | discover_inductive( 9 | log, 10 | multi_processing = FALSE, 11 | noise_threshold = 0, 12 | convert = TRUE 13 | ) 14 | 15 | \method{discover_inductive}{log}( 16 | log, 17 | multi_processing = FALSE, 18 | noise_threshold = 0, 19 | convert = TRUE 20 | ) 21 | } 22 | \arguments{ 23 | \item{log}{\code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}},} 24 | 25 | \item{multi_processing}{\link{logical} (default \code{\link{FALSE}}): Disables if \code{FALSE}, enables if \code{TRUE} multiprocessing in inductive miner.} 26 | 27 | \item{noise_threshold}{\link{numeric} (default: 0): noise threshold. 28 | For Inductive Miner currently only \code{variant_inductive_imdfb} is supported.} 29 | 30 | \item{convert}{\link{logical} (default: \link{TRUE}): \link{TRUE} to automatically convert Python objects to their R equivalent. 31 | If you pass \link{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 32 | } 33 | \value{ 34 | A named list with elements \code{petrinet}, \code{initial_marking}, and \code{final_marking} or the original Python object. 35 | } 36 | \description{ 37 | PM4PY discovery algorithms that discover a Petri net and its initial and final marking. Currently the Inductive Miner and the Alpha Miner are implemented. 38 | } 39 | \section{Methods (by class)}{ 40 | \itemize{ 41 | \item \code{discover_inductive(log)}: Discover Inductive Miner model based on event log 42 | 43 | }} 44 | \examples{ 45 | \dontrun{ 46 | library(pm4py) 47 | library(eventdataR) 48 | 49 | discover_inductive(patients) 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /R/parameters.R: -------------------------------------------------------------------------------- 1 | #' PM4Py parameter keys 2 | #' 3 | #' Convenience methods to use as PM4Py parameter keys. 4 | #' 5 | #' @param value The value to add to the list. 6 | #' @return a list with the parameter key/value pair 7 | #' @examples 8 | #' param_activity_key("activity") 9 | #' 10 | #' library(eventdataR) 11 | #' data(patients) 12 | #' default_parameters(patients) 13 | #' 14 | #' @name parameters 15 | NULL 16 | 17 | #' @rdname parameters 18 | #' @export 19 | param_activity_key <- function(value) { 20 | l <- list(value) 21 | names(l) <- pm4py_parameters$activity_key 22 | l 23 | } 24 | 25 | #' @rdname parameters 26 | #' @export 27 | param_attribute_key <- function(value) { 28 | l <- list(value) 29 | names(l) <- pm4py_parameters$attribute_key 30 | l 31 | } 32 | 33 | #' @rdname parameters 34 | #' @export 35 | param_timestamp_key <- function(value) { 36 | l <- list(value) 37 | names(l) <- pm4py_parameters$timestamp_key 38 | l 39 | } 40 | 41 | #' @rdname parameters 42 | #' @export 43 | param_caseid_key <- function(value) { 44 | l <- list(value) 45 | names(l) <- pm4py_parameters$caseid_key 46 | l 47 | } 48 | 49 | #' @rdname parameters 50 | #' @export 51 | param_resource_key <- function(value) { 52 | l <- list(value) 53 | names(l) <- pm4py_parameters$resource_key 54 | l 55 | } 56 | 57 | #' @param eventlog A bupaR or PM4PY event log. 58 | #' 59 | #' @rdname parameters 60 | #' @export 61 | default_parameters <- function(eventlog) { 62 | if ("eventlog" %in% class(eventlog)) { 63 | c(param_activity_key(bupaR::activity_id(eventlog)), 64 | param_timestamp_key(bupaR::timestamp(eventlog)), 65 | param_caseid_key(bupaR::case_id(eventlog))) 66 | } else { 67 | list() 68 | } 69 | } 70 | 71 | pm4py_parameters <- list( 72 | activity_key = "pm4py:param:activity_key", 73 | attribute_key = "pm4py:param:attribute_key", 74 | timestamp_key = "pm4py:param:timestamp_key", 75 | caseid_key = "case_id_glue", 76 | resource_key = "pm4py:param:resource_key" 77 | ) 78 | -------------------------------------------------------------------------------- /R/pm4py.R: -------------------------------------------------------------------------------- 1 | #' PM4PY for R 2 | #' 3 | #' This package provides access to the Python Process Mining library PM4PY in R 4 | #' and provides conversion between bupaR and PM4PY data structures. 5 | #' 6 | #' To use this package, you need to have a Python environment (Conda or virtualenv) 7 | #' installed and install the PM4PY package and its dependencies. You can use 8 | #' the convenience function \link{install_pm4py} to let `reticulate` take care of 9 | #' install the right version. See the documentation of this function for further 10 | #' information. 11 | #' 12 | #' When loaded, the object `pm4py` provides the low-level interface to the main 13 | #' PM4PY module. Use `$` to access sub modules of PM4PY as described in the 14 | #' `reticulate` documentation: 15 | #' 16 | #' \code{vignette("calling_python", package = "reticulate")} 17 | #' 18 | #' For parts of PM4PY wrapper functions are provided to transparently convert 19 | #' parameters and results to and from the corresponding bupaR S3 classes. 20 | #' 21 | #' 22 | #' @import reticulate 23 | #' @export 24 | pm4py <- NULL 25 | 26 | .onLoad <- function(libname, pkgname) { 27 | if (pm4py_available()) { 28 | # use superassignment to update global reference to pm4py 29 | pm4py <<- import("pm4py", delay_load = TRUE) 30 | } 31 | } 32 | 33 | pm4py_tools <- function() { 34 | python_path <- system.file("python", package = "pm4py") 35 | pm4py_tools <- import_from_path("pm4pytools", path = python_path) 36 | } 37 | 38 | constant_xes_traceid_key <- function() { 39 | pm4py$objects$log$util$xes$DEFAULT_TRACEID_KEY 40 | } 41 | 42 | constant_xes_name_key <- function() { 43 | pm4py$objects$log$util$xes$DEFAULT_NAME_KEY 44 | } 45 | 46 | constant_xes_timestamp_key <- function() { 47 | pm4py$objects$log$util$xes$DEFAULT_TIMESTAMP_KEY 48 | } 49 | 50 | constant_xes_resource_key <- function() { 51 | pm4py$objects$log$util$xes$DEFAULT_RESOURCE_KEY 52 | } 53 | 54 | constant_xes_transition_key <- function() { 55 | pm4py$objects$log$util$xes$DEFAULT_RESOURCE_KEY 56 | } 57 | -------------------------------------------------------------------------------- /R/fitness_token_based_replay.R: -------------------------------------------------------------------------------- 1 | #' Measure Token-Based Replay Fitness 2 | #' 3 | #' @inheritParams discover_inductive 4 | #' @inheritParams fitness_alignments 5 | #' 6 | #' @return List with fitness measures. 7 | #' 8 | #' @examples 9 | #' \dontrun{ 10 | #' library(pm4py) 11 | #' library(eventdataR) 12 | #' 13 | #' model <- discover_alpha(patients) 14 | #' fitness_token_based_replay(patients, model) 15 | #' 16 | #' } 17 | #' @export 18 | #' 19 | 20 | fitness_token_based_replay <- function(log, 21 | marked_petrinet, 22 | convert = TRUE) { 23 | UseMethod("fitness_token_based_replay") 24 | } 25 | 26 | #' @export 27 | fitness_token_based_replay.eventlog <- function(log, 28 | marked_petrinet, 29 | convert = TRUE) { 30 | pm4py_conformance <- reticulate::import("pm4py.conformance", convert = convert) 31 | 32 | if(n_events(log) > n_activity_instances(log)) { 33 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 34 | 35 | 36 | if("eventlog" %in% class(log)) { 37 | log %>% 38 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 39 | } 40 | } 41 | 42 | log %>% 43 | mutate(across(activity_id(log), as.character)) -> log 44 | 45 | # prepare arguments for pm4py module 46 | py_log <- r_to_py(log) 47 | py_pn <- as_py_value(marked_petrinet$petrinet) 48 | im <- as_pm4py_marking(marked_petrinet$initial_marking, py_pn) 49 | fm <- as_pm4py_marking(marked_petrinet$final_marking, py_pn) 50 | activity_key <- bupaR::activity_id(log) 51 | timestamp_key <- bupaR::timestamp(log) 52 | case_id_key <- bupaR::case_id(log) 53 | 54 | 55 | pm4py_conformance$fitness_token_based_replay(log = py_log, petri_net = py_pn, 56 | initial_marking = im, 57 | final_marking = fm, 58 | activity_key = activity_key, 59 | timestamp_key = timestamp_key, 60 | case_id_key = case_id_key) 61 | } 62 | -------------------------------------------------------------------------------- /R/install.R: -------------------------------------------------------------------------------- 1 | #' Install PM4PY library 2 | #' 3 | #' Installs the `pm4py` package and its dependencies using `pip` since no 4 | #' Conda package is available. Further information on the parameters can 5 | #' be found in the `reticulate` package documentation: 6 | #' https://rstudio.github.io/reticulate/ 7 | #' In some cases (multiple Python versions installed) it might be useful to specify the exact path to the `conda` binary. 8 | #' 9 | #' Additional requirements, for example, a C++ compiler and GraphViz might 10 | #' need to be installed to leverage all functionality. Please refer to the PM4Py documentation for details: 11 | #' https://pm4py.fit.fraunhofer.de/install 12 | #' 13 | #' @param method Installation method. By default, "auto" automatically finds a method that will work in the local environment. 14 | #' Change the default to force a specific installation method. Note that the "virtualenv" method is not available on Windows. 15 | #' @param conda Path to conda executable (or "auto" to find conda using the PATH and other conventional install locations). 16 | #' @param version Optional parameter overriding the PM4Py version that will be installed. Note that the R pm4py package was only tested against the default PM4Py version that will be installed. 17 | #' @param ... Additional arguments passed to py_install(). 18 | #' @return No return value, called for side effects. 19 | #' @export 20 | #' 21 | #' 22 | install_pm4py <- function(method = "auto", conda = "auto", version = NULL, ...) { 23 | 24 | if (!is.null(version)) { 25 | version <- paste0("pm4py==", version) 26 | } else { 27 | version <- PM4PY_PIP 28 | } 29 | 30 | tryCatch({ 31 | if (PM4PY_DEVELOPMENT) { 32 | reticulate::py_install(paste0("-r", PM4PY_DEVELOPMENT_DEPENDENCIES), method = method, conda = conda, pip = TRUE, ...) 33 | } 34 | reticulate::py_install(version, method = method, conda = conda, pip = TRUE, ...) 35 | }, 36 | error = function(e) { 37 | # workaround for virtualenv not supporting `pip` parameter 38 | warning("Could not find `conda` environment, falling back to virtualenv ... ") 39 | reticulate::py_install(version, method = method, conda = conda, ...) 40 | } 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /man/evaluation.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/evaluation.R 3 | \name{evaluation} 4 | \alias{evaluation} 5 | \alias{evaluation_all} 6 | \alias{evaluation_precision} 7 | \alias{variant_precision_etconformance} 8 | \alias{evaluation_fitness} 9 | \alias{variant_fitness_token_based} 10 | \alias{variant_fitness_alignment_based} 11 | \title{Calculates evaluation measures for a Petri nets and an Event Log} 12 | \usage{ 13 | evaluation_all( 14 | eventlog, 15 | petrinet, 16 | initial_marking, 17 | final_marking, 18 | parameters = default_parameters(eventlog), 19 | convert = TRUE 20 | ) 21 | 22 | evaluation_precision( 23 | eventlog, 24 | petrinet, 25 | initial_marking, 26 | final_marking, 27 | parameters = default_parameters(eventlog), 28 | variant = variant_precision_etconformance(), 29 | convert = TRUE 30 | ) 31 | 32 | variant_precision_etconformance() 33 | 34 | evaluation_fitness( 35 | eventlog, 36 | petrinet, 37 | initial_marking, 38 | final_marking, 39 | parameters = default_parameters(eventlog), 40 | variant = variant_fitness_token_based(), 41 | convert = TRUE 42 | ) 43 | 44 | variant_fitness_token_based() 45 | 46 | variant_fitness_alignment_based() 47 | } 48 | \arguments{ 49 | \item{eventlog}{A bupaR or PM4PY event log.} 50 | 51 | \item{petrinet}{A bupaR or PM4PY Petri net.} 52 | 53 | \item{initial_marking}{A R vector with the place identifiers of the initial marking or a PM4PY marking. 54 | By default the initial marking of the bupaR Petri net will be used if available.} 55 | 56 | \item{final_marking}{A R vector with the place identifiers of the final marking or a PM4PY marking.} 57 | 58 | \item{parameters}{PM4PY alignment parameter.} 59 | 60 | \item{convert}{\code{TRUE} to automatically convert Python objects to their R equivalent. If you pass \code{FALSE} you can do manual conversion using the \link[reticulate]{r-py-conversion} function.} 61 | 62 | \item{variant}{Variant used 63 | By default the \code{activity_key} from the bupaR event log is specified using \link{param_activity_key}.} 64 | } 65 | \value{ 66 | A \code{list} with all available evaluation measures. 67 | } 68 | \description{ 69 | Calculates evaluation measures for a Petri nets and an Event Log 70 | } 71 | -------------------------------------------------------------------------------- /R/precision_token_based_replay.R: -------------------------------------------------------------------------------- 1 | #' Measure Token-Based Replay Precision 2 | #' 3 | #' @inheritParams discover_inductive 4 | #' @inheritParams fitness_alignments 5 | #' 6 | #' @return Numeric vector of length one containing precision measure. 7 | #' @examples 8 | #' \dontrun{ 9 | #' library(pm4py) 10 | #' library(eventdataR) 11 | #' 12 | #' model <- discover_alpha(patients) 13 | #' precision_token_based_replay(patients, model) 14 | #' 15 | #' } 16 | #' @export 17 | 18 | precision_token_based_replay <- function(log, 19 | marked_petrinet, 20 | convert = TRUE) { 21 | UseMethod("precision_token_based_replay") 22 | } 23 | 24 | #' @export 25 | precision_token_based_replay.log <- function(log, 26 | marked_petrinet, 27 | convert = TRUE) { 28 | pm4py_conformance <- reticulate::import("pm4py.conformance", convert = convert) 29 | 30 | if(n_events(log) > n_activity_instances(log)) { 31 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 32 | 33 | 34 | if("eventlog" %in% class(log)) { 35 | log %>% 36 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 37 | } 38 | } 39 | 40 | log %>% 41 | mutate(across(activity_id(log), as.character)) -> log 42 | 43 | # prepare arguments for pm4py module 44 | py_log <- r_to_py(log) 45 | py_pn <- as_py_value(marked_petrinet$petrinet) 46 | im <- as_pm4py_marking(marked_petrinet$initial_marking, py_pn) 47 | fm <- as_pm4py_marking(marked_petrinet$final_marking, py_pn) 48 | activity_key <- bupaR::activity_id(log) 49 | timestamp_key <- bupaR::timestamp(log) 50 | case_id_key <- bupaR::case_id(log) 51 | 52 | 53 | pm4py_conformance$precision_token_based_replay(log = py_log, petri_net = py_pn, 54 | initial_marking = im, 55 | final_marking = fm, 56 | activity_key = activity_key, 57 | timestamp_key = timestamp_key, 58 | case_id_key = case_id_key) 59 | } 60 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /R/diagnostics_token_based_replay.R: -------------------------------------------------------------------------------- 1 | # Diagnostics token-based replay ------------------------------------------ 2 | #' @title Apply __the token-based replay__ algorithm between a log and a process model 3 | #' 4 | #' @description Apply token-based replay for conformance checking analysis. The methods return the full token-based-replay diagnostics. 5 | #' 6 | #' @inheritParams discover_inductive 7 | #' @inheritParams fitness_alignments 8 | #' 9 | #' @return Token-based-replay diagnostics. 10 | #' 11 | #' @export 12 | diagnostics_token_based_replay <- function(log, 13 | marked_petrinet, 14 | convert = TRUE) { 15 | UseMethod("diagnostics_token_based_replay") 16 | } 17 | 18 | #' @export 19 | diagnostics_token_based_replay.log <- function(log, 20 | marked_petrinet, 21 | convert = TRUE) { 22 | pm4py_conformance <- reticulate::import("pm4py.conformance", convert = convert) 23 | 24 | if(n_events(log) > n_activity_instances(log)) { 25 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 26 | 27 | 28 | if("eventlog" %in% class(log)) { 29 | log %>% 30 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 31 | } 32 | } 33 | 34 | log %>% 35 | mutate(across(activity_id(log), as.character)) -> log 36 | # prepare arguments for pm4py module 37 | py_log <- r_to_py(log) 38 | py_pn <- as_py_value(marked_petrinet$petrinet) 39 | im <- as_pm4py_marking(marked_petrinet$initial_marking, py_pn) 40 | fm <- as_pm4py_marking(marked_petrinet$final_marking, py_pn) 41 | activity_key <- bupaR::activity_id(log) 42 | timestamp_key <- bupaR::timestamp(log) 43 | case_id_key <- bupaR::case_id(log) 44 | 45 | pm4py_conformance$conformance_diagnostics_token_based_replay(log = py_log, petri_net = py_pn, 46 | initial_marking = im, 47 | final_marking = fm, 48 | activity_key = activity_key, 49 | timestamp_key = timestamp_key, 50 | case_id_key = case_id_key) 51 | } 52 | 53 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(diagnostics_alignments,log) 4 | S3method(diagnostics_token_based_replay,log) 5 | S3method(discover_alpha,activitylog) 6 | S3method(discover_alpha,eventlog) 7 | S3method(discover_alpha_plus,eventlog) 8 | S3method(discover_inductive,log) 9 | S3method(discover_inductive_bpmn,log) 10 | S3method(fitness_alignments,log) 11 | S3method(fitness_token_based_replay,eventlog) 12 | S3method(precision_alignments,log) 13 | S3method(precision_token_based_replay,log) 14 | S3method(py_to_r,pm4py.objects.log.log.EventLog) 15 | S3method(py_to_r,pm4py.objects.log.log.TraceLog) 16 | S3method(py_to_r,pm4py.objects.petri_net.obj.Marking) 17 | S3method(py_to_r,pm4py.objects.petri_net.obj.PetriNet) 18 | S3method(r_to_py,bpmn) 19 | S3method(r_to_py,log) 20 | S3method(r_to_py,petrinet) 21 | export(as_pm4py_marking) 22 | export(conformance_alignment) 23 | export(default_parameters) 24 | export(diagnostics_alignments) 25 | export(diagnostics_token_based_replay) 26 | export(discover_alpha) 27 | export(discover_alpha_plus) 28 | export(discover_inductive) 29 | export(discover_inductive_bpmn) 30 | export(discovery_alpha) 31 | export(evaluation_all) 32 | export(evaluation_fitness) 33 | export(evaluation_precision) 34 | export(fitness_alignments) 35 | export(fitness_token_based_replay) 36 | export(install_pm4py) 37 | export(param_activity_key) 38 | export(param_attribute_key) 39 | export(param_caseid_key) 40 | export(param_resource_key) 41 | export(param_timestamp_key) 42 | export(petrinet_check_relaxed_soundness) 43 | export(petrinet_check_wfnet) 44 | export(petrinet_synchronous_product) 45 | export(pm4py) 46 | export(pm4py_available) 47 | export(pm4py_version) 48 | export(precision_alignments) 49 | export(precision_token_based_replay) 50 | export(py_to_r) 51 | export(r_to_py) 52 | export(variant_alpha_classic) 53 | export(variant_alpha_plus) 54 | export(variant_dijkstra_no_heuristics) 55 | export(variant_fitness_alignment_based) 56 | export(variant_fitness_token_based) 57 | export(variant_inductive_imdfb) 58 | export(variant_inductive_only_dfg) 59 | export(variant_precision_etconformance) 60 | export(variant_state_equation_a_star) 61 | export(version) 62 | export(write_pnml) 63 | import(bupaR) 64 | import(dplyr) 65 | import(reticulate) 66 | importFrom(dplyr,tibble) 67 | importFrom(petrinetR,create_marked_PN) 68 | importFrom(purrr,map) 69 | importFrom(purrr,map_chr) 70 | importFrom(reticulate,iterate) 71 | importFrom(reticulate,py_to_r) 72 | importFrom(reticulate,r_to_py) 73 | importFrom(stringr,str_remove) 74 | -------------------------------------------------------------------------------- /R/discover_inductive.R: -------------------------------------------------------------------------------- 1 | #' Petri net discovery algorithms 2 | #' 3 | #' PM4PY discovery algorithms that discover a Petri net and its initial and final marking. Currently the Inductive Miner and the Alpha Miner are implemented. 4 | #' 5 | #' @param multi_processing [logical] (default [`FALSE`]): Disables if `FALSE`, enables if `TRUE` multiprocessing in inductive miner. 6 | #' @param noise_threshold [numeric] (default: 0): noise threshold. 7 | #' For Inductive Miner currently only `variant_inductive_imdfb` is supported. 8 | #' @inheritParams discover_alpha 9 | #' @return A named list with elements `petrinet`, `initial_marking`, and `final_marking` or the original Python object. 10 | #' 11 | #' @examples 12 | #' \dontrun{ 13 | #' library(pm4py) 14 | #' library(eventdataR) 15 | #' 16 | #' discover_inductive(patients) 17 | #' 18 | #' } 19 | #' @export 20 | discover_inductive <- function(log, # currently not for activitylog 21 | multi_processing = FALSE, 22 | noise_threshold = 0, 23 | convert = TRUE) { 24 | UseMethod("discover_inductive") 25 | } 26 | 27 | #' @describeIn discover_inductive Discover Inductive Miner model based on event log 28 | #' @export 29 | discover_inductive.log <- function(log, 30 | multi_processing = FALSE, 31 | noise_threshold = 0, 32 | convert = TRUE) { 33 | pm4py_inductive <- reticulate::import("pm4py.discovery", convert = convert) 34 | 35 | 36 | if(n_events(log) > n_activity_instances(log)) { 37 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 38 | 39 | log %>% 40 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 41 | } 42 | log %>% 43 | mutate(across(activity_id(log), as.character)) -> log 44 | 45 | 46 | multi_processing <- r_to_py(multi_processing) 47 | noise_threshold <- r_to_py(noise_threshold) 48 | model <- pm4py_inductive$discover_petri_net_inductive(r_to_py(log), 49 | multi_processing = multi_processing, 50 | noise_threshold = noise_threshold, 51 | activity_key = activity_id(log), 52 | timestamp_key = timestamp(log), 53 | case_id_key = case_id(log)) 54 | 55 | 56 | 57 | 58 | create_marked_PN(model[[1]], model[[2]], model[[3]]) 59 | } 60 | -------------------------------------------------------------------------------- /R/fitness_alignments.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | # FITNESS ALIGNMENTS ------------------------------------------------------ 4 | #' @title Apply the __fitness__ alignments algorithm between a log and a process model 5 | #' 6 | #' @description The calculation of the replay fitness aim to calculate how much of the behavior in the log is admitted by the process model. 7 | #' @param marked_petrinet A Marked Petrinet as defined by petrinetR, e.g. the output of [discover_inductive] or [discover_alpha]. 8 | #' @inheritParams discover_inductive 9 | #' @return fitness alignments. 10 | #' @examples 11 | #' \dontrun{ 12 | #' library(pm4py) 13 | #' library(eventdataR) 14 | #' 15 | #' model <- discover_alpha(patients) 16 | #' fitness_alignments(patients, model) 17 | #' 18 | #' } 19 | #' @export 20 | fitness_alignments <- function(log, 21 | marked_petrinet, 22 | multi_processing = FALSE, 23 | convert = TRUE) { 24 | UseMethod("fitness_alignments") 25 | } 26 | 27 | #' @export 28 | fitness_alignments.log <- function(log, 29 | marked_petrinet, 30 | multi_processing = FALSE, 31 | convert = TRUE) { 32 | pm4py_conformance <- reticulate::import("pm4py.conformance", convert = convert) 33 | 34 | if(n_events(log) > n_activity_instances(log)) { 35 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 36 | 37 | 38 | if("eventlog" %in% class(log)) { 39 | log %>% 40 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 41 | } 42 | } 43 | 44 | log %>% 45 | mutate(across(activity_id(log), as.character)) -> log 46 | # prepare arguments for pm4py module 47 | py_log <- r_to_py(log) 48 | py_pn <- as_py_value(marked_petrinet$petrinet) 49 | im <- as_pm4py_marking(marked_petrinet$initial_marking, py_pn) 50 | fm <- as_pm4py_marking(marked_petrinet$final_marking, py_pn) 51 | activity_key <- bupaR::activity_id(log) 52 | timestamp_key <- bupaR::timestamp(log) 53 | case_id_key <- bupaR::case_id(log) 54 | multi_processing <- r_to_py(multi_processing) 55 | 56 | pm4py_conformance$fitness_alignments(log = py_log, petri_net = py_pn, 57 | initial_marking = im, 58 | final_marking = fm, 59 | activity_key = activity_key, 60 | timestamp_key = timestamp_key, 61 | case_id_key = case_id_key, 62 | multi_processing = multi_processing) 63 | } 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /R/discover_alpha.R: -------------------------------------------------------------------------------- 1 | 2 | #' @title Discover petrinet using Alpha Algorithm 3 | #' @param log \code{\link{log}}: Object of class \code{\link{log}} or derivatives (\code{\link{grouped_log}}, \code{\link{eventlog}}, 4 | #' @param convert [logical] (default: [TRUE]): [TRUE] to automatically convert Python objects to their R equivalent. 5 | #' If you pass [FALSE] you can do manual conversion using the \link[reticulate]{r-py-conversion} function. 6 | #' @return Marked petri net, i.e. a named list with elements `petrinet`, `initial_marking`, and `final_marking` or the original Python object. 7 | #' @export 8 | #' @examples 9 | #' \dontrun{ 10 | #' library(pm4py) 11 | #' library(eventdataR) 12 | #' 13 | #' discover_alpha(patients) 14 | #' 15 | #' } 16 | #' @importFrom petrinetR create_marked_PN 17 | #' 18 | discover_alpha <- function(log, convert = TRUE) { 19 | UseMethod("discover_alpha") 20 | } 21 | 22 | #' @describeIn discover_alpha Discovery petri net based on event log 23 | #' @export 24 | 25 | discover_alpha.eventlog <- function(log, 26 | convert = TRUE) { 27 | pm4py_discovery <- reticulate::import("pm4py", convert = convert) 28 | 29 | if(n_events(log) > n_activity_instances(log)) { 30 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 31 | 32 | log %>% 33 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 34 | } 35 | log %>% 36 | mutate(across(activity_id(log), as.character)) -> log 37 | 38 | model <- pm4py_discovery$discover_petri_net_alpha(as_py_value(log), 39 | activity_key = activity_id(log), 40 | timestamp_key = timestamp(log), 41 | case_id_key = case_id(log)) 42 | create_marked_PN(model[[1]], model[[2]], model[[3]]) 43 | } 44 | 45 | #' @describeIn discover_alpha Discovery petri net based on activity log 46 | #' @export 47 | 48 | discover_alpha.activitylog <- function(log, 49 | convert = TRUE) { 50 | pm4py_discovery <- reticulate::import("pm4py", convert = convert) 51 | 52 | if(n_events(log) > n_activity_instances(log)) { 53 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 54 | } 55 | log %>% 56 | mutate(across(activity_id(log), as.character)) -> log 57 | 58 | model <- pm4py_discovery$discover_petri_net_alpha(as_py_value(log), 59 | activity_key = activity_id(log), 60 | timestamp_key = "complete", 61 | case_id_key = case_id(log)) 62 | create_marked_PN(model[[1]], model[[2]], model[[3]]) 63 | } 64 | -------------------------------------------------------------------------------- /R/precision_alignments.R: -------------------------------------------------------------------------------- 1 | # PRECISION ALIGNMENTS ---------------------------------------------------- 2 | #' @title Apply the __precision__ alignments algorithm between a log and a process model 3 | #' 4 | #' @description Calculates the precision of the model w.r.t. the event log using alignments. 5 | #' 6 | #' @inheritParams discover_inductive 7 | #' @inheritParams fitness_alignments 8 | #' @return precision metric. 9 | #' @examples 10 | #' \dontrun{ 11 | #' library(pm4py) 12 | #' library(eventdataR) 13 | #' 14 | #' model <- discover_alpha(patients) 15 | #' precision_alignments(patients, model) 16 | #' 17 | #' } 18 | #' @export 19 | precision_alignments <- function(log, 20 | marked_petrinet, 21 | multi_processing = FALSE, 22 | convert = TRUE) { 23 | UseMethod("precision_alignments")} 24 | #' @export 25 | precision_alignments.log <- function(log, 26 | marked_petrinet, 27 | multi_processing = FALSE, 28 | # petrinet, 29 | # initial_marking, 30 | # final_marking, 31 | # activity_key, 32 | # timestamp_key, 33 | # case_id_key, 34 | convert = TRUE) { 35 | pm4py_conformance <- reticulate::import("pm4py.conformance", convert = convert) 36 | if(n_events(log) > n_activity_instances(log)) { 37 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 38 | 39 | 40 | if("eventlog" %in% class(log)) { 41 | log %>% 42 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 43 | } 44 | } 45 | 46 | log %>% 47 | mutate(across(activity_id(log), as.character)) -> log 48 | # prepare arguments for pm4py module 49 | py_log <- r_to_py(log) 50 | py_pn <- as_py_value(marked_petrinet$petrinet) 51 | im <- as_pm4py_marking(marked_petrinet$initial_marking, py_pn) 52 | fm <- as_pm4py_marking(marked_petrinet$final_marking, py_pn) 53 | activity_key <- bupaR::activity_id(log) 54 | timestamp_key <- bupaR::timestamp(log) 55 | case_id_key <- bupaR::case_id(log) 56 | multi_processing <- r_to_py(multi_processing) 57 | 58 | 59 | pm4py_conformance$precision_alignments(log = py_log, petri_net = py_pn, 60 | initial_marking = im, 61 | final_marking = fm, 62 | activity_key = activity_key, 63 | timestamp_key = timestamp_key, 64 | case_id_key = case_id_key, 65 | multi_processing = multi_processing) 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # R Interface to the PM4Py Process Mining Library 2 | 3 | [![](https://cranlogs.r-pkg.org/badges/pm4py)](https://cran.r-project.org/package=pm4py) 4 | [![CRAN\_Status\_Badge](https://www.r-pkg.org/badges/version/pm4py)](https://cran.r-project.org/package=pm4py) 5 | 6 | The goal of the R package 'pm4py' is to provide a bridge between [bupaR](https://bupar.net/) and the Python library 'PM4Py'. 7 | 8 | ## Installation 9 | 10 | You can install the released CRAN version of pm4py with: 11 | ``` r 12 | install.packages("pm4py") 13 | ``` 14 | 15 | You can install the development version of pm4py from the `dev` branch with: 16 | 17 | ``` r 18 | remotes::install_github("bupaverse/pm4py@dev") 19 | ``` 20 | 21 | Then, automatically install the pm4py package in a virtual or Conda environment: 22 | ``` r 23 | pm4py::install_pm4py() 24 | ``` 25 | 26 | See the 'reticulate' documentation for more information on the available options or how to specify an existing Python environment: 27 | https://rstudio.github.io/reticulate/ 28 | 29 | ## PM4Py Version 30 | 31 | To facilitate getting stable results and to reduce the number of regressions due to API changes in PM4Py, this package is built against a fixed PM4Py version that is defined in the file `R/version.R`. We also adopt the versioning schema of the PM4Py project for this R package. So, the R package version `1.1.19` will install the PM4Py version `1.1.19`. 32 | 33 | In case of fixes required to the R package itself, for example, for bugs or adopting new features, we will add a suffix `-rev` to the version to indicate the change. Of course, nothing prevents you from manually overriding the synchronisation between the PM4Py version and the R PM4Py package version using the parameter `version` as follows: 34 | ``` r 35 | pm4py::install_pm4py(version = "1.2.7") 36 | ``` 37 | 38 | ## Example 39 | 40 | ``` r 41 | library(pm4py) 42 | 43 | # Most of the data structures are converted in their bupaR equivalents 44 | library(bupaR) 45 | 46 | # As Inductive Miner of PM4PY is not life-cycle aware, keep only `complete` events: 47 | patients_completes <- patients[patients$registration_type == "complete", ] 48 | 49 | # Discovery with Inductive Miner 50 | pn <- discovery_inductive(patients_completes) 51 | 52 | # This results in an auto-converted bupaR Petri net and markings 53 | str(pn) 54 | class(pn$petrinet) 55 | 56 | # Render with bupaR 57 | render_PN(pn$petrinet) 58 | 59 | # Render with PM4PY and DiagrammeR 60 | library(DiagrammeR) 61 | viz <- reticulate::import("pm4py.visualization.petrinet") 62 | 63 | # Convert back to Python 64 | py_pn <- r_to_py(pn$petrinet) 65 | class(py_pn) 66 | 67 | # Render to DOT with PMP4Y 68 | dot <- viz$factory$apply(py_pn)$source 69 | grViz(diagram = dot) 70 | 71 | # Compute alignment 72 | alignment <- conformance_alignment(patients_completes, pn$petrinet, pn$initial_marking, pn$final_marking) 73 | 74 | # # Alignment is returned in long format as data frame 75 | head(alignment) 76 | 77 | # Evaluate model quality 78 | quality <- evaluation_all(patients_completes, pn$petrinet, pn$initial_marking, pn$final_marking) 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('body').scrollspy({ 13 | target: '#sidebar', 14 | offset: 60 15 | }); 16 | 17 | $('[data-toggle="tooltip"]').tooltip(); 18 | 19 | var cur_path = paths(location.pathname); 20 | var links = $("#navbar ul li a"); 21 | var max_length = -1; 22 | var pos = -1; 23 | for (var i = 0; i < links.length; i++) { 24 | if (links[i].getAttribute("href") === "#") 25 | continue; 26 | // Ignore external links 27 | if (links[i].host !== location.host) 28 | continue; 29 | 30 | var nav_path = paths(links[i].pathname); 31 | 32 | var length = prefix_length(nav_path, cur_path); 33 | if (length > max_length) { 34 | max_length = length; 35 | pos = i; 36 | } 37 | } 38 | 39 | // Add class to parent
  • , and enclosing
  • if in dropdown 40 | if (pos >= 0) { 41 | var menu_anchor = $(links[pos]); 42 | menu_anchor.parent().addClass("active"); 43 | menu_anchor.closest("li.dropdown").addClass("active"); 44 | } 45 | }); 46 | 47 | function paths(pathname) { 48 | var pieces = pathname.split("/"); 49 | pieces.shift(); // always starts with / 50 | 51 | var end = pieces[pieces.length - 1]; 52 | if (end === "index.html" || end === "") 53 | pieces.pop(); 54 | return(pieces); 55 | } 56 | 57 | // Returns -1 if not found 58 | function prefix_length(needle, haystack) { 59 | if (needle.length > haystack.length) 60 | return(-1); 61 | 62 | // Special case for length-0 haystack, since for loop won't run 63 | if (haystack.length === 0) { 64 | return(needle.length === 0 ? 0 : -1); 65 | } 66 | 67 | for (var i = 0; i < haystack.length; i++) { 68 | if (needle[i] != haystack[i]) 69 | return(i); 70 | } 71 | 72 | return(haystack.length); 73 | } 74 | 75 | /* Clipboard --------------------------*/ 76 | 77 | function changeTooltipMessage(element, msg) { 78 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 79 | element.setAttribute('data-original-title', msg); 80 | $(element).tooltip('show'); 81 | element.setAttribute('data-original-title', tooltipOriginalTitle); 82 | } 83 | 84 | if(ClipboardJS.isSupported()) { 85 | $(document).ready(function() { 86 | var copyButton = ""; 87 | 88 | $(".examples, div.sourceCode").addClass("hasCopyButton"); 89 | 90 | // Insert copy buttons: 91 | $(copyButton).prependTo(".hasCopyButton"); 92 | 93 | // Initialize tooltips: 94 | $('.btn-copy-ex').tooltip({container: 'body'}); 95 | 96 | // Initialize clipboard: 97 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 98 | text: function(trigger) { 99 | return trigger.parentNode.textContent; 100 | } 101 | }); 102 | 103 | clipboardBtnCopies.on('success', function(e) { 104 | changeTooltipMessage(e.trigger, 'Copied!'); 105 | e.clearSelection(); 106 | }); 107 | 108 | clipboardBtnCopies.on('error', function() { 109 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 110 | }); 111 | }); 112 | } 113 | })(window.jQuery || window.$) 114 | -------------------------------------------------------------------------------- /R/diagnostics_alignments.R: -------------------------------------------------------------------------------- 1 | #' Apply the alignments algorithm between a log and a process model 2 | #' 3 | #' @description Alignment-based replay aims to find one of the best alignment between the trace and the model. 4 | #' 5 | #' @inheritParams discover_inductive 6 | #' @inheritParams fitness_alignments 7 | #' @return alignment diagnostics. 8 | #' 9 | #' @import reticulate 10 | #' 11 | #' @export 12 | diagnostics_alignments <- function(log, 13 | marked_petrinet, 14 | multi_processing = FALSE, 15 | convert = TRUE) { 16 | UseMethod("diagnostics_alignments") 17 | } 18 | #' @describeIn diagnostics_alignment Compute aligments 19 | #' @export 20 | diagnostics_alignments.log <- function(log, 21 | marked_petrinet, 22 | multi_processing = FALSE, 23 | # petrinet, 24 | # initial_marking, 25 | # final_marking, 26 | # activity_key, 27 | # timestamp_key, 28 | # case_id_key, 29 | convert = TRUE) { 30 | 31 | pm4py_conformance <- reticulate::import("pm4py.conformance", convert = convert) 32 | 33 | 34 | if(n_events(log) > n_activity_instances(log)) { 35 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 36 | 37 | 38 | if("eventlog" %in% class(log)) { 39 | log %>% 40 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 41 | } 42 | } 43 | 44 | log %>% 45 | mutate(across(activity_id(log), as.character)) -> log 46 | 47 | # prepare arguments for pm4py module 48 | py_log <- r_to_py(log) 49 | py_pn <- as_py_value(marked_petrinet$petrinet) 50 | im <- as_pm4py_marking(marked_petrinet$initial_marking, py_pn) 51 | fm <- as_pm4py_marking(marked_petrinet$final_marking, py_pn) 52 | activity_key <- bupaR::activity_id(log) 53 | timestamp_key <- bupaR::timestamp(log) 54 | case_id_key <- bupaR::case_id(log) 55 | multi_processing <- r_to_py(multi_processing) 56 | 57 | # specs_list <- list(...) 58 | # specs <- list() 59 | # for (i in specs_list) { 60 | # if (class(i) == "petrinet") { 61 | # i <- as_py_value(i) 62 | # } 63 | # else { 64 | # i <- as_pm4py_marking(i, py_pn) 65 | # } 66 | # specs <- specs %>% append(i) 67 | # } 68 | # 69 | # names(specs) <- c("py_pn", "im", "fm") 70 | 71 | 72 | alignments <- pm4py_conformance$conformance_diagnostics_alignments(py_log, py_pn, im, fm, # specs 73 | activity_key = activity_key, 74 | timestamp_key = timestamp_key, 75 | case_id_key = case_id_key, 76 | multi_processing = multi_processing) 77 | # py_pn, im, fm) 78 | cases <- cases(log) 79 | 80 | if(convert) { 81 | alignments %>% 82 | map(~.x[names(.x) != "alignment"]) %>% 83 | map(as_tibble) %>% 84 | bind_rows() -> costs 85 | 86 | move_to_tibble <- function(x) { 87 | x %>% 88 | map(~if(is.null(.x)) NA else .x) %>% 89 | as_tibble(.name_repair = function(names) c("log","model")) 90 | } 91 | 92 | alignments %>% 93 | map(~.x$alignment) %>% map(~ 94 | map(.x, move_to_tibble) %>% 95 | bind_rows()) -> alignments 96 | costs[[case_id(log)]] <- cases[[case_id(log)]] 97 | 98 | costs %>% 99 | select(case_id(log), everything()) %>% 100 | mutate(alignment = alignments) 101 | } 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /R/petrinet.R: -------------------------------------------------------------------------------- 1 | #' Synchronous product Petri net 2 | #' 3 | #' Constructs the synchronous product net of two given Petri nets. 4 | #' 5 | #' @param pn1 First Petri net 6 | #' @param im1 Initial marking of the first Petri net 7 | #' @param fm1 Final marking of the first Petri net 8 | #' @param pn2 Second Petri net 9 | #' @param im2 Initial marking of the second Petri net 10 | #' @param fm2 Final marking of the second Petri net 11 | #' @param skip Symbol to be used as skip 12 | #' @param convert `TRUE` to automatically convert Python objects to their R equivalent. If you pass `FALSE` you can do manual conversion using the \link[reticulate]{r-py-conversion} function. 13 | #' 14 | #' @return A Petri net. 15 | #' 16 | #' @import reticulate 17 | #' @export 18 | petrinet_synchronous_product <- function(pn1, 19 | im1, 20 | fm1, 21 | pn2, 22 | im2, 23 | fm2, 24 | skip = ">>", 25 | convert = TRUE) { 26 | 27 | lifecycle::deprecate_stop("2.0.0", "petrinet_synchronous_product") 28 | pm4py_sync <- import("pm4py.objects.petri.synchronous_product", convert = convert) 29 | 30 | pn1 <- as_py_value(pn1) 31 | pn2 <- as_py_value(pn2) 32 | 33 | sync_net <- pm4py_sync$construct(pn1, 34 | as_pm4py_marking(im1, pn1), 35 | as_pm4py_marking(fm1, pn1), 36 | pn2, 37 | as_pm4py_marking(im2, pn2), 38 | as_pm4py_marking(fm2, pn2), 39 | skip) 40 | 41 | prepare_pn_with_markings(sync_net, convert) 42 | } 43 | 44 | #' Check Workflow net property 45 | #' 46 | #' Checks if the Petri net is a Workflow net 47 | #' 48 | #' 49 | #' @param pn Petri net 50 | #' @param convert `TRUE` to automatically convert Python objects to their R equivalent. If you pass `FALSE` you can do manual conversion using the \link[reticulate]{r-py-conversion} function. 51 | #' 52 | #' @return A single logical 53 | #' 54 | #' @import reticulate 55 | #' @export 56 | petrinet_check_wfnet <- function(pn, 57 | convert = TRUE) { 58 | lifecycle::deprecate_stop("2.0.0", "petrinet_check_wfnet") 59 | 60 | pm4py_soundness <- import("pm4py.objects.petri.check_soundness", convert = convert) 61 | 62 | pn <- as_py_value(pn) 63 | 64 | pm4py_soundness$check_wfnet(pn) 65 | } 66 | 67 | #' Check Relaxed soundness property 68 | #' 69 | #' Checks if the Petri net is relaxed sound 70 | #' 71 | #' @param pn Petri net 72 | #' @param im Initial marking of the Petri net (optional for workflow nets) 73 | #' @param fm Final marking of the Petri net (optional for workflow nets) 74 | #' @param convert `TRUE` to automatically convert Python objects to their R equivalent. If you pass `FALSE` you can do manual conversion using the \link[reticulate]{r-py-conversion} function. 75 | #' 76 | #' @return A single logical 77 | #' 78 | #' @import reticulate 79 | #' @export 80 | petrinet_check_relaxed_soundness <- function(pn, 81 | im = NULL, 82 | fm = NULL, 83 | convert = TRUE) { 84 | 85 | lifecycle::deprecate_stop("2.0.0", "petrinet_check_relaxed_soundness") 86 | 87 | pm4py_soundness <- import("pm4py.objects.petri.check_soundness", convert = convert) 88 | 89 | pn <- as_py_value(pn) 90 | 91 | if (petrinet_check_wfnet(pn)) { 92 | pm4py_soundness$check_relaxed_soundness_of_wfnet(pn) 93 | } else { 94 | stopifnot(!is.null(im)) 95 | stopifnot(!is.null(fm)) 96 | pm4py_soundness$check_relaxed_soundness_net_in_fin_marking(pn, 97 | as_pm4py_marking(im, pn), 98 | as_pm4py_marking(fm, pn)) 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /R/conformance.R: -------------------------------------------------------------------------------- 1 | #' Conformance between an Event Log and a Petri net 2 | #' 3 | #' @param eventlog A bupaR or PM4PY event log. 4 | #' @param petrinet A bupaR or PM4PY Petri net. 5 | #' @param initial_marking A R vector with the place identifiers of the initial marking or a PM4PY marking. 6 | #' By default the initial marking of the bupaR Petri net will be used if available. 7 | #' @param final_marking A R vector with the place identifiers of the final marking or a PM4PY marking. 8 | #' @param parameters PM4PY conformance parameter. 9 | #' By default the `activity_key` from the bupaR event log is specified using \link{param_activity_key}. 10 | #' @param variant The conformance variant to be used. 11 | #' @param convert `TRUE` to automatically convert Python objects to their R equivalent. 12 | #' If you pass `FALSE` you can do manual conversion using the \link[reticulate]{r-py-conversion} function. 13 | #' 14 | #' @return A data frame describing the conformance result. 15 | #' In case of `conformance_alignment` a data frame of log and model moves. 16 | #' 17 | #' @name conformance 18 | #' 19 | #' 20 | NULL 21 | 22 | 23 | #' @rdname conformance 24 | #' @export 25 | #' @import reticulate 26 | conformance_alignment <- function(eventlog, 27 | petrinet, 28 | initial_marking, 29 | final_marking, 30 | parameters = default_parameters(eventlog), 31 | variant = variant_state_equation_a_star(), 32 | convert = TRUE) { 33 | 34 | lifecycle::deprecate_warn(when = "2.0.0", "conformance_alignment()",with = "fitness_alignments()") 35 | 36 | pm4py_alignments <- import("pm4py.algo.conformance.alignments.factory", convert = convert) 37 | if (is.null(initial_marking) && inherits(petrinet, "petrinet")) { 38 | initial_marking <- petrinet$marking 39 | } 40 | 41 | py_pn <- as_py_value(petrinet) 42 | py_log <- as_py_value(eventlog) 43 | 44 | if (variant == variant_state_equation_a_star()) { 45 | param_syncaware <- list(TRUE) 46 | names(param_syncaware) <- pm4py$algo$conformance$alignments$versions$ 47 | state_equation_a_star$PARAM_ALIGNMENT_RESULT_IS_SYNC_PROD_AWARE 48 | parameters <- c(parameters, param_syncaware) 49 | } 50 | 51 | alignment <- pm4py_alignments$apply(obj = py_log, 52 | petri_net = py_pn, 53 | initial_marking = as_pm4py_marking(initial_marking, py_pn), 54 | final_marking = as_pm4py_marking(final_marking, py_pn), 55 | parameters = parameters, 56 | version = variant) 57 | 58 | if (convert) { 59 | 60 | case_ids <- pm4py_tools()$log$get_trace_ids(py_log, parameters) 61 | 62 | df_alignment <- purrr::map2_dfr(alignment, case_ids, function(trace, case_id) { 63 | 64 | align_mat <- t(sapply(trace$alignment, function(x) { 65 | c(x[[1]], x[[2]]) 66 | })) # convert nested lists to matrix 67 | align_mat[vapply(align_mat, is.null, TRUE)] <- NA_character_ 68 | align_lst <- apply(align_mat, 2, unlist) # remove wrapper lists from elements 69 | 70 | trace_df <- data.frame(align_lst, stringsAsFactors = FALSE) 71 | names(trace_df) <- c("log_id", "model_id", "log_label", "model_label") 72 | 73 | # add meta information by duplicating it since we don't have a trace object 74 | cbind(case_id, 75 | trace_df, 76 | trace[-1], stringsAsFactors = FALSE) 77 | 78 | }) 79 | 80 | class(df_alignment) <- c("alignment", class(df_alignment)) 81 | 82 | df_alignment 83 | 84 | } else { 85 | alignment 86 | } 87 | } 88 | 89 | #' @rdname conformance 90 | #' @export 91 | variant_state_equation_a_star <- function() { 92 | pm4py$algo$conformance$alignments$factory$VERSION_STATE_EQUATION_A_STAR 93 | } 94 | 95 | #' @rdname conformance 96 | #' @export 97 | variant_dijkstra_no_heuristics <- function() { 98 | pm4py$algo$conformance$alignments$factory$VERSION_DIJKSTRA_NO_HEURISTICS 99 | } 100 | -------------------------------------------------------------------------------- /R/evaluation.R: -------------------------------------------------------------------------------- 1 | #' Calculates evaluation measures for a Petri nets and an Event Log 2 | #' 3 | #' @param eventlog A bupaR or PM4PY event log. 4 | #' @param petrinet A bupaR or PM4PY Petri net. 5 | #' @param initial_marking A R vector with the place identifiers of the initial marking or a PM4PY marking. 6 | #' By default the initial marking of the bupaR Petri net will be used if available. 7 | #' @param final_marking A R vector with the place identifiers of the final marking or a PM4PY marking. 8 | #' @param parameters PM4PY alignment parameter. 9 | #' @param variant Variant used 10 | #' By default the `activity_key` from the bupaR event log is specified using \link{param_activity_key}. 11 | #' @param convert `TRUE` to automatically convert Python objects to their R equivalent. If you pass `FALSE` you can do manual conversion using the \link[reticulate]{r-py-conversion} function. 12 | #' 13 | #' @return A `list` with all available evaluation measures. 14 | #' 15 | #' @name evaluation 16 | #' @import reticulate 17 | #' @export 18 | evaluation_all <- function(eventlog, 19 | petrinet, 20 | initial_marking, 21 | final_marking, 22 | parameters = default_parameters(eventlog), 23 | convert = TRUE) { 24 | pm4py_evaluation <- import("pm4py.evaluation.factory", convert = convert) 25 | lifecycle::deprecate_warn(when = "2.0.0", "evaluation_all()") 26 | 27 | py_pn <- as_py_value(petrinet) 28 | py_log <- as_py_value(eventlog) 29 | 30 | m <- pm4py_evaluation$apply(log = py_log, 31 | net = py_pn, 32 | initial_marking = as_pm4py_marking(initial_marking, py_pn), 33 | final_marking = as_pm4py_marking(final_marking, py_pn), 34 | parameters = parameters) 35 | m 36 | } 37 | 38 | #' @rdname evaluation 39 | #' @import reticulate 40 | #' @export 41 | evaluation_precision <- function(eventlog, 42 | petrinet, 43 | initial_marking, 44 | final_marking, 45 | parameters = default_parameters(eventlog), 46 | variant = variant_precision_etconformance(), 47 | convert = TRUE) { 48 | pm4py_evaluation <- import("pm4py.evaluation.precision.factory", convert = convert) 49 | lifecycle::deprecate_warn(when = "2.0.0", "evaluation_precision()") 50 | 51 | py_pn <- as_py_value(petrinet) 52 | py_log <- as_py_value(eventlog) 53 | 54 | m <- pm4py_evaluation$apply(log = py_log, 55 | net = py_pn, 56 | marking = as_pm4py_marking(initial_marking, py_pn), # TODO inconsistent naming upstream 57 | final_marking = as_pm4py_marking(final_marking, py_pn), 58 | parameters = parameters, 59 | variant = variant) 60 | m 61 | } 62 | 63 | #' @rdname evaluation 64 | #' @export 65 | variant_precision_etconformance <- function() { 66 | pm4py$evaluation$precision$factory$ETCONFORMANCE_TOKEN 67 | } 68 | 69 | #' @rdname evaluation 70 | #' @import reticulate 71 | #' @export 72 | evaluation_fitness <- function(eventlog, 73 | petrinet, 74 | initial_marking, 75 | final_marking, 76 | parameters = default_parameters(eventlog), 77 | variant = variant_fitness_token_based(), 78 | convert = TRUE) { 79 | pm4py_evaluation <- import("pm4py.evaluation.replay_fitness.factory", convert = convert) 80 | lifecycle::deprecate_warn(when = "2.0.0", "evaluation_fitness()") 81 | 82 | py_pn <- as_py_value(petrinet) 83 | py_log <- as_py_value(eventlog) 84 | 85 | m <- pm4py_evaluation$apply(log = py_log, 86 | petri_net = py_pn, # TODO inconsistent naming upstream 87 | initial_marking = as_pm4py_marking(initial_marking, py_pn), 88 | final_marking = as_pm4py_marking(final_marking, py_pn), 89 | parameters = parameters, 90 | variant = variant) 91 | m 92 | } 93 | 94 | #' @rdname evaluation 95 | #' @export 96 | variant_fitness_token_based <- function() { 97 | pm4py$evaluation$replay_fitness$factory$TOKEN_BASED 98 | } 99 | 100 | #' @rdname evaluation 101 | #' @export 102 | variant_fitness_alignment_based <- function() { 103 | pm4py$evaluation$replay_fitness$factory$ALIGNMENT_BASED 104 | } 105 | -------------------------------------------------------------------------------- /docs/reference/test.pnml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | p_8 8 | 9 | 10 | 11 | 12 | p_6 13 | 14 | 15 | 16 | 17 | p_3 18 | 19 | 20 | 21 | 22 | sink 23 | 24 | 25 | 26 | 27 | source 28 | 29 | 30 | 1 31 | 32 | 33 | 34 | 35 | p_4 36 | 37 | 38 | 39 | 40 | p_5 41 | 42 | 43 | 44 | 45 | p_7 46 | 47 | 48 | 49 | 50 | skip_1 51 | 52 | 53 | 54 | 55 | 56 | MRI SCAN 57 | 58 | 59 | 60 | 61 | Registration 62 | 63 | 64 | 65 | 66 | X-Ray 67 | 68 | 69 | 70 | 71 | Discuss Results 72 | 73 | 74 | 75 | 76 | skip_2 77 | 78 | 79 | 80 | 81 | 82 | skip_3 83 | 84 | 85 | 86 | 87 | 88 | Check-out 89 | 90 | 91 | 92 | 93 | Triage and Assessment 94 | 95 | 96 | 97 | 98 | Blood test 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 1 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Page not found (404) • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
    58 |
    59 | 101 | 102 | 103 | 104 |
    105 | 106 |
    107 |
    108 | 111 | 112 | Content not found. Please use links in the navbar. 113 | 114 |
    115 | 116 |
    117 | 118 | 119 | 120 |
    121 | 124 | 125 |
    126 |

    Site built with pkgdown 1.4.1.

    127 |
    128 | 129 |
    130 |
    131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Authors • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
    58 |
    59 | 101 | 102 | 103 | 104 |
    105 | 106 |
    107 |
    108 | 111 | 112 |
      113 |
    • 114 |

      Felix Mannhardt. Author, maintainer. 115 |

      116 |
    • 117 |
    118 | 119 |
    120 | 121 |
    122 | 123 | 124 | 125 |
    126 | 129 | 130 |
    131 |

    Site built with pkgdown 1.4.1.

    132 |
    133 | 134 |
    135 |
    136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /R/discover_inductive_bpmn.R: -------------------------------------------------------------------------------- 1 | 2 | #' Discover BPMN models using Inductive Miner 3 | #' @inheritParams discover_inductive 4 | #' @return A BPMN object as defined by BPMNR 5 | #' @export 6 | #' 7 | #' @import dplyr 8 | #' @import bupaR 9 | 10 | discover_inductive_bpmn <- function(log, 11 | multi_processing = FALSE, 12 | noise_threshold = 0, 13 | convert = TRUE) { 14 | UseMethod("discover_inductive_bpmn") 15 | } 16 | #' @export 17 | discover_inductive_bpmn.log <- function(log, 18 | multi_processing = FALSE, 19 | noise_threshold = 0, 20 | convert = TRUE) { 21 | 22 | gatewayDirection <- NULL 23 | gatewayType <- NULL 24 | 25 | pm4py_inductive <- reticulate::import("pm4py.discovery", convert = convert) 26 | 27 | if(n_events(log) > n_activity_instances(log)) { 28 | cli::cli_warn("Activity instances with multiple events found in log: using only complete events.") 29 | 30 | 31 | if("eventlog" %in% class(log)) { 32 | log %>% 33 | filter(.data[[lifecycle_id(log)]] == "complete") -> log 34 | } 35 | } 36 | log %>% 37 | mutate(across(activity_id(log), as.character)) -> log 38 | 39 | 40 | multi_processing <- r_to_py(multi_processing) 41 | noise_threshold <- r_to_py(noise_threshold) 42 | activity_key <- bupaR::activity_id(log) 43 | timestamp_key <- bupaR::timestamp(log) 44 | case_id_key <- bupaR::case_id(log) 45 | 46 | model <- pm4py_inductive$discover_bpmn_inductive(r_to_py(log), 47 | multi_processing = multi_processing, 48 | noise_threshold = noise_threshold, 49 | activity_key = activity_key, 50 | timestamp_key = timestamp_key, 51 | case_id_key = case_id_key) -> bpmntmp 52 | 53 | bpmntmp$get_nodes() # tasks/nodes: data.frame of all tasks and their attributes 54 | bpmntmp$get_flows() # sequencyFlows: data.frame of all sequence flows and their attributes 55 | bpmntmp$Gateway() # gateways: data.frame of all gateways and their attributes 56 | bpmntmp$StartEvent() # startEvent: data.frame containing the start event and its attributes 57 | bpmntmp$EndEvent() # endEvent: data.frame containing the end event and its attributes 58 | 59 | # unlist(iterate(q, function(a) ensure_str(a$source$name))) 60 | # 61 | # 62 | # reticulate::dict(q, convert = F) 63 | # 64 | # py_builtins$set(unname(unlist(a_list))) 65 | 66 | bpmntmp$get_nodes() 67 | 68 | reticulate::iterate(bpmntmp$get_nodes(), function(p) ensure_str(p$name)) -> nodes_name 69 | reticulate::iterate(bpmntmp$get_nodes(), function(p) ensure_str(p$id)) -> nodes_id 70 | reticulate::iterate(bpmntmp$get_nodes(), function(p) (class(p))) -> nodes_class 71 | reticulate::iterate(bpmntmp$get_nodes(), function(p) if(stringr::str_detect(ensure_str(class(p)), "Gateway")) 72 | p$get_gateway_direction()) -> nodes_directions 73 | 74 | tibble(name = nodes_name, id = nodes_id, nodes_class, gatewayDirection = nodes_directions) %>% 75 | mutate(class = str_remove(map_chr(nodes_class, ~.x[[1]]), "pm4py.objects.bpmn.obj."), 76 | gatewayDirection = map(gatewayDirection, as.character)) %>% 77 | tidyr::unnest(gatewayDirection, keep_empty = T) %>% 78 | mutate(gatewayDirection = str_remove(gatewayDirection, "Direction."), 79 | gatewayType = class) %>% 80 | select(-nodes_class) -> nodes 81 | 82 | 83 | # reticulate::iterate(bpmntmp$get_nodes(), function(p) if(stringr::str_detect(pm4py:::ensure_str(class(p)), "Gateway")) 84 | # p$get_out_arcs()) -> nodes_id 85 | 86 | reticulate::iterate(bpmntmp$get_flows(), function(p) ensure_str(p$get_name())) -> arcs_name 87 | reticulate::iterate(bpmntmp$get_flows(), function(p) ensure_str(p$get_id())) -> arcs_id 88 | reticulate::iterate(bpmntmp$get_flows(), function(p) ensure_str(p$source$name)) -> arcs_from_name 89 | reticulate::iterate(bpmntmp$get_flows(), function(p) ensure_str(p$source$id)) -> arcs_from_id 90 | 91 | reticulate::iterate(bpmntmp$get_flows(), function(p) ensure_str(p$target$name)) -> arcs_to_name 92 | reticulate::iterate(bpmntmp$get_flows(), function(p) ensure_str(p$target$id)) -> arcs_to_id 93 | 94 | tibble(id = arcs_id, name = arcs_name, sourceRef = arcs_from_id, targetRef = arcs_to_id) -> flows 95 | 96 | 97 | suppressWarnings({ 98 | nodes %>% 99 | mutate(class = forcats::fct_recode(class, "startEvent" = "StartEvent", 100 | "endEvent" = "EndEvent", 101 | "endEvent" = "NormalEndEvent", 102 | "task" = "Task", 103 | "exclusiveGateway" = "ExclusiveGateway", 104 | "inclusiveGateway" = "InclusiveGateway", 105 | "parallelGateway" = "ParallelGateway")) %>% 106 | mutate(gatewayDirection = forcats::fct_recode(gatewayDirection, 107 | "Converging" = "CONVERGING", 108 | "Diverging" = "DIVERGING")) %>% 109 | select(-gatewayType) -> nodes 110 | 111 | }) 112 | 113 | events <- dplyr::filter(nodes, class %in% c("startEvent", "endEvent")) %>% dplyr::rename(objectType = class) 114 | nodes <- dplyr::filter(nodes, !class %in% c("startEvent", "endEvent")) %>% dplyr::rename(objectType = class) 115 | flows <- dplyr::mutate(flows, objectType = "sequenceFlow") %>% 116 | mutate(id = paste0("id_", id)) 117 | 118 | bpmnR::create_bpmn(nodes, flows, events) 119 | } 120 | -------------------------------------------------------------------------------- /docs/pkgdown.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer */ 2 | 3 | /** 4 | * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/ 5 | * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css 6 | * 7 | * .Site -> body > .container 8 | * .Site-content -> body > .container .row 9 | * .footer -> footer 10 | * 11 | * Key idea seems to be to ensure that .container and __all its parents__ 12 | * have height set to 100% 13 | * 14 | */ 15 | 16 | html, body { 17 | height: 100%; 18 | } 19 | 20 | body > .container { 21 | display: flex; 22 | height: 100%; 23 | flex-direction: column; 24 | } 25 | 26 | body > .container .row { 27 | flex: 1 0 auto; 28 | } 29 | 30 | footer { 31 | margin-top: 45px; 32 | padding: 35px 0 36px; 33 | border-top: 1px solid #e5e5e5; 34 | color: #666; 35 | display: flex; 36 | flex-shrink: 0; 37 | } 38 | footer p { 39 | margin-bottom: 0; 40 | } 41 | footer div { 42 | flex: 1; 43 | } 44 | footer .pkgdown { 45 | text-align: right; 46 | } 47 | footer p { 48 | margin-bottom: 0; 49 | } 50 | 51 | img.icon { 52 | float: right; 53 | } 54 | 55 | img { 56 | max-width: 100%; 57 | } 58 | 59 | /* Fix bug in bootstrap (only seen in firefox) */ 60 | summary { 61 | display: list-item; 62 | } 63 | 64 | /* Typographic tweaking ---------------------------------*/ 65 | 66 | .contents .page-header { 67 | margin-top: calc(-60px + 1em); 68 | } 69 | 70 | /* Section anchors ---------------------------------*/ 71 | 72 | a.anchor { 73 | margin-left: -30px; 74 | display:inline-block; 75 | width: 30px; 76 | height: 30px; 77 | visibility: hidden; 78 | 79 | background-image: url(./link.svg); 80 | background-repeat: no-repeat; 81 | background-size: 20px 20px; 82 | background-position: center center; 83 | } 84 | 85 | .hasAnchor:hover a.anchor { 86 | visibility: visible; 87 | } 88 | 89 | @media (max-width: 767px) { 90 | .hasAnchor:hover a.anchor { 91 | visibility: hidden; 92 | } 93 | } 94 | 95 | 96 | /* Fixes for fixed navbar --------------------------*/ 97 | 98 | .contents h1, .contents h2, .contents h3, .contents h4 { 99 | padding-top: 60px; 100 | margin-top: -40px; 101 | } 102 | 103 | /* Sidebar --------------------------*/ 104 | 105 | #sidebar { 106 | margin-top: 30px; 107 | position: -webkit-sticky; 108 | position: sticky; 109 | top: 70px; 110 | } 111 | #sidebar h2 { 112 | font-size: 1.5em; 113 | margin-top: 1em; 114 | } 115 | 116 | #sidebar h2:first-child { 117 | margin-top: 0; 118 | } 119 | 120 | #sidebar .list-unstyled li { 121 | margin-bottom: 0.5em; 122 | } 123 | 124 | .orcid { 125 | height: 16px; 126 | /* margins are required by official ORCID trademark and display guidelines */ 127 | margin-left:4px; 128 | margin-right:4px; 129 | vertical-align: middle; 130 | } 131 | 132 | /* Reference index & topics ----------------------------------------------- */ 133 | 134 | .ref-index th {font-weight: normal;} 135 | 136 | .ref-index td {vertical-align: top;} 137 | .ref-index .icon {width: 40px;} 138 | .ref-index .alias {width: 40%;} 139 | .ref-index-icons .alias {width: calc(40% - 40px);} 140 | .ref-index .title {width: 60%;} 141 | 142 | .ref-arguments th {text-align: right; padding-right: 10px;} 143 | .ref-arguments th, .ref-arguments td {vertical-align: top;} 144 | .ref-arguments .name {width: 20%;} 145 | .ref-arguments .desc {width: 80%;} 146 | 147 | /* Nice scrolling for wide elements --------------------------------------- */ 148 | 149 | table { 150 | display: block; 151 | overflow: auto; 152 | } 153 | 154 | /* Syntax highlighting ---------------------------------------------------- */ 155 | 156 | pre { 157 | word-wrap: normal; 158 | word-break: normal; 159 | border: 1px solid #eee; 160 | } 161 | 162 | pre, code { 163 | background-color: #f8f8f8; 164 | color: #333; 165 | } 166 | 167 | pre code { 168 | overflow: auto; 169 | word-wrap: normal; 170 | white-space: pre; 171 | } 172 | 173 | pre .img { 174 | margin: 5px 0; 175 | } 176 | 177 | pre .img img { 178 | background-color: #fff; 179 | display: block; 180 | height: auto; 181 | } 182 | 183 | code a, pre a { 184 | color: #375f84; 185 | } 186 | 187 | a.sourceLine:hover { 188 | text-decoration: none; 189 | } 190 | 191 | .fl {color: #1514b5;} 192 | .fu {color: #000000;} /* function */ 193 | .ch,.st {color: #036a07;} /* string */ 194 | .kw {color: #264D66;} /* keyword */ 195 | .co {color: #888888;} /* comment */ 196 | 197 | .message { color: black; font-weight: bolder;} 198 | .error { color: orange; font-weight: bolder;} 199 | .warning { color: #6A0366; font-weight: bolder;} 200 | 201 | /* Clipboard --------------------------*/ 202 | 203 | .hasCopyButton { 204 | position: relative; 205 | } 206 | 207 | .btn-copy-ex { 208 | position: absolute; 209 | right: 0; 210 | top: 0; 211 | visibility: hidden; 212 | } 213 | 214 | .hasCopyButton:hover button.btn-copy-ex { 215 | visibility: visible; 216 | } 217 | 218 | /* headroom.js ------------------------ */ 219 | 220 | .headroom { 221 | will-change: transform; 222 | transition: transform 200ms linear; 223 | } 224 | .headroom--pinned { 225 | transform: translateY(0%); 226 | } 227 | .headroom--unpinned { 228 | transform: translateY(-100%); 229 | } 230 | 231 | /* mark.js ----------------------------*/ 232 | 233 | mark { 234 | background-color: rgba(255, 255, 51, 0.5); 235 | border-bottom: 2px solid rgba(255, 153, 51, 0.3); 236 | padding: 1px; 237 | } 238 | 239 | /* vertical spacing after htmlwidgets */ 240 | .html-widget { 241 | margin-bottom: 10px; 242 | } 243 | 244 | /* fontawesome ------------------------ */ 245 | 246 | .fab { 247 | font-family: "Font Awesome 5 Brands" !important; 248 | } 249 | 250 | /* don't display links in code chunks when printing */ 251 | /* source: https://stackoverflow.com/a/10781533 */ 252 | @media print { 253 | code a:link:after, code a:visited:after { 254 | content: ""; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /docs/reference/reexports.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Objects exported from other packages — reexports • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    65 |
    66 | 108 | 109 | 110 | 111 |
    112 | 113 |
    114 |
    115 | 120 | 121 |
    122 |

    These objects are imported from other packages. Follow the links 123 | below to see their documentation.

    124 |
    125 |
    reticulate

    py_to_r, r_to_py

    126 | 127 |
    128 |
    129 | 130 | 131 | 132 | 133 |
    134 | 140 |
    141 | 142 | 143 |
    144 | 147 | 148 |
    149 |

    Site built with pkgdown 1.4.1.

    150 |
    151 | 152 |
    153 |
    154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /docs/reference/version.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | This function is deprecated, please use <code><a href='pm4py_version.html'>pm4py_version</a></code>. — version • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 | 103 | 104 | 105 | 106 |
    107 | 108 |
    109 |
    110 | 115 | 116 |
    117 |

    This function is deprecated, please use pm4py_version.

    118 |
    119 | 120 |
    version()
    121 | 122 | 123 |

    Value

    124 | 125 |

    package_version S3 class

    126 | 127 |
    128 | 135 |
    136 | 137 | 138 |
    139 | 142 | 143 |
    144 |

    Site built with pkgdown 1.4.1.

    145 |
    146 | 147 |
    148 |
    149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /docs/news/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Changelog • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
    58 |
    59 | 101 | 102 | 103 | 104 |
    105 | 106 |
    107 |
    108 | 112 | 113 |
    114 |

    115 | pm4py 1.2.8.9000 Unreleased 116 |

    117 |
      118 |
    • Update to PM4Py v1.2.8
    • 119 |
    120 |
    121 |
    122 |

    123 | pm4py 1.2.7 2020-01-07 124 |

    125 |
      126 |
    • Update to PM4Py v1.2.7
    • 127 |
    • Some Petrinet functions added
    • 128 |
    129 |
    130 |
    131 |

    132 | pm4py 1.0.1 2019-03-10 133 |

    134 |
      135 |
    • Initial release compatible with PM4Py v1.1.1
    • 136 |
    137 |
    138 |
    139 | 140 | 150 | 151 |
    152 | 153 | 154 |
    155 | 158 | 159 |
    160 |

    Site built with pkgdown 1.4.1.

    161 |
    162 | 163 |
    164 |
    165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /docs/reference/pm4py_version.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Returns PM4Py version used — pm4py_version • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 | 103 | 104 | 105 | 106 |
    107 | 108 |
    109 |
    110 | 115 | 116 |
    117 |

    Returns PM4Py version used

    118 |
    119 | 120 |
    pm4py_version()
    121 | 122 | 123 |

    Value

    124 | 125 |

    package_version S3 class

    126 | 127 |

    Examples

    128 |
    if (pm4py_available()) { 129 | print(pm4py_version()) 130 | }
    #> [1] '1.2.7'
    131 |
    132 | 140 |
    141 | 142 | 143 |
    144 | 147 | 148 |
    149 |

    Site built with pkgdown 1.4.1.

    150 |
    151 | 152 |
    153 |
    154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /docs/reference/pm4py_available.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Is the PM4Py module available — pm4py_available • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 | 103 | 104 | 105 | 106 |
    107 | 108 |
    109 |
    110 | 115 | 116 |
    117 |

    Is the PM4Py module available

    118 |
    119 | 120 |
    pm4py_available()
    121 | 122 | 123 |

    Value

    124 | 125 |

    TRUE is PM4Py is installed

    126 | 127 |

    Examples

    128 |
    if (pm4py_available()) { 129 | print(pm4py_version()) 130 | }
    #> [1] '1.2.7'
    131 |
    132 |
    133 | 141 |
    142 | 143 | 144 |
    145 | 148 | 149 |
    150 |

    Site built with pkgdown 1.4.1.

    151 |
    152 | 153 |
    154 |
    155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /R/bridge.R: -------------------------------------------------------------------------------- 1 | #' @importFrom reticulate r_to_py 2 | #' @export 3 | reticulate::r_to_py 4 | 5 | #' @importFrom reticulate py_to_r 6 | #' @export 7 | reticulate::py_to_r 8 | 9 | #' @export 10 | #' @importFrom reticulate r_to_py 11 | r_to_py.log <- function(x, convert = FALSE) { 12 | df <- as.data.frame(x) 13 | # fix for https://github.com/rstudio/reticulate/issues/389 14 | #i <- sapply(df, is.factor) 15 | #df[i] <- lapply(df[i], as.character) 16 | r_to_py(df, convert = convert) 17 | } 18 | 19 | #' @export 20 | #' @importFrom reticulate py_to_r 21 | py_to_r.pm4py.objects.log.log.EventLog <- function(x) { 22 | variant_to_dataframe <- pm4py$objects$conversion$log$constants$TO_DATAFRAME 23 | df <- pm4py$objects$conversion$log$factory$apply(x, variant = variant_to_dataframe) 24 | df[ , !names(df) %in% c("case:concept:name")] 25 | } 26 | 27 | #' @export 28 | #' @importFrom reticulate py_to_r 29 | py_to_r.pm4py.objects.log.log.TraceLog <- function(x) { 30 | #TODO: recover the complete bupaR event log which requires: 31 | # - case identifier (could come from 'case:concept:name') 32 | # - activity identifier (classifier? but which one?) 33 | # - activity instance identifier (auto generate?) 34 | # .... 35 | variant_to_dataframe <- pm4py$objects$conversion$log$constants$TO_DATAFRAME 36 | df <- pm4py$objects$conversion$log$factory$apply(x, variant = variant_to_dataframe) 37 | df[ , !names(df) %in% c("case:concept:name")] 38 | } 39 | 40 | #' @export 41 | #' @importFrom reticulate r_to_py 42 | r_to_py.petrinet <- function(x, convert = FALSE) { 43 | 44 | pm4py_petrinet <- import("pm4py.objects.petri_net.obj", convert = convert) 45 | py_builtins <- import_builtins(convert = convert) 46 | 47 | t_list <- Map(function(name, label) { 48 | if (is.na(label)) { 49 | label <- NULL 50 | } 51 | pm4py_petrinet$PetriNet$Transition(name, label) 52 | }, x$transitions$id, x$transitions$label) 53 | 54 | p_list <- Map(function(p) pm4py_petrinet$PetriNet$Place(p), x$places$id) 55 | 56 | a_list <- Map(function(from, to) { 57 | if (from %in% names(t_list)) { 58 | source <- t_list[[from]] 59 | target <- p_list[[to]] 60 | } else { 61 | source <- p_list[[from]] 62 | target <- t_list[[to]] 63 | } 64 | 65 | a <- pm4py_petrinet$PetriNet$Arc(source, target) 66 | 67 | source$out_arcs$add(a) 68 | target$in_arcs$add(a) 69 | 70 | return(a) 71 | 72 | }, x$flows$from, x$flows$to) 73 | 74 | pm4py_petrinet$PetriNet( 75 | places = unname(unlist(p_list)), 76 | transitions = unname(unlist(t_list)), 77 | arcs = py_builtins$set(unname(unlist(a_list))) 78 | ) 79 | } 80 | 81 | #' @export 82 | #' @importFrom reticulate r_to_py 83 | r_to_py.bpmn <- function(x, convert = F) { 84 | 85 | pm4py_bpmn <- reticulate::import("pm4py.objects.bpmn.obj", convert = convert) 86 | 87 | # gateways 88 | Map(function(id, name, gateway_direction) { 89 | pm4py_bpmn$BPMN$Gateway(id, name, gateway_direction) 90 | }, x$gateways$id, x$gateways$name, x$gateways$gatewayDirection) -> gateways 91 | 92 | # tasks 93 | Map(function(id, name) { 94 | pm4py_bpmn$BPMN$Task(id, name) 95 | }, x$tasks$id, x$task$name) -> tasks 96 | 97 | # startEvent 98 | Map(function(id, name) { 99 | pm4py_bpmn$BPMN$StartEvent(id, name) 100 | }, x$startEvent$id, x$startEvent$name) -> startEvent 101 | 102 | # endEvent 103 | Map(function(id, name) { 104 | pm4py_bpmn$BPMN$EndEvent(id, name) 105 | }, x$endEvent$id, x$endEvent$name) -> endEvent 106 | 107 | # sequenceFlows 108 | Map(function(sourceRef, targetRef, id, name) { 109 | 110 | # find source 111 | if(sourceRef %in% names(gateways)) 112 | source <- gateways[[sourceRef]] 113 | else if (sourceRef %in% names(startEvent)) { 114 | source <- startEvent[[sourceRef]] 115 | } 116 | else if(sourceRef %in% names(endEvent)) { 117 | source <- endEvent[[sourceRef]] 118 | } 119 | else { 120 | source <- tasks[[sourceRef]] 121 | } 122 | 123 | # find target 124 | if(targetRef %in% names(gateways)) 125 | target <- gateways[[targetRef]] 126 | else if (targetRef %in% names(startEvent)) { 127 | target <- startEvent[[targetRef]] 128 | } 129 | else if(targetRef %in% names(endEvent)) { 130 | target <- endEvent[[targetRef]] 131 | } 132 | else { 133 | target <- tasks[[targetRef]] 134 | } 135 | 136 | # map 137 | pm4py_bpmn$BPMN$SequenceFlow(source = source, target = target, id = id, name = name) 138 | }, x$sequenceFlows$sourceRef, x$sequenceFlows$targetRef, x$sequenceFlows$id, x$sequenceFlows$name) -> sequenceFlows 139 | 140 | unname(unlist(tasks)) -> tasks 141 | unname(unlist(sequenceFlows)) -> flows 142 | unname(unlist(gateways)) -> gateways 143 | unname(unlist(startEvent)) -> startEvent 144 | unname(unlist(endEvent)) -> endEvent 145 | c(tasks, gateways, startEvent, endEvent) -> nodes 146 | 147 | pm4py_bpmn$BPMN(#process_id = "1", name = "A", 148 | nodes = nodes, 149 | flows = flows) -> py_x 150 | 151 | return(py_x) 152 | # return(list(tasks = tasks, gateways = gateways, sequenceFlows = sequence_flows, startEvent = start_events, endEvent = end_events)) 153 | } 154 | 155 | #' @export 156 | #' @importFrom reticulate iterate 157 | #' @importFrom reticulate py_to_r 158 | py_to_r.pm4py.objects.petri_net.obj.PetriNet <- function(x) { 159 | 160 | # Make sure that labels are strings since PM4PY sometimes uses tuples or other objects 161 | # Also, replace NULL by NA to avoid issues with R removing elements 162 | 163 | place_ids <- unlist(iterate(x$places, function(p) ensure_str(p$name))) 164 | transition_ids <- unlist(iterate(x$transitions, function(t) ensure_str(t$name))) 165 | transition_labels <- unlist(iterate(x$transitions, function(t) ensure_str(t$label))) 166 | 167 | arcs_from <- unlist(iterate(x$arcs, function(a) ensure_str(a$source$name))) 168 | arcs_to <- unlist(iterate(x$arcs,function(a) ensure_str(a$target$name))) 169 | 170 | flows <- data.frame(from = arcs_from, to = arcs_to, stringsAsFactors = F) 171 | transitions <- data.frame(label = transition_labels, id = transition_ids) 172 | places <- data.frame(id = place_ids, label = place_ids) 173 | 174 | pn <- petrinetR::create_PN(places, transitions, flows) 175 | 176 | pn 177 | } 178 | 179 | #' @export 180 | #' @importFrom reticulate iterate 181 | #' @importFrom reticulate py_to_r 182 | py_to_r.pm4py.objects.petri_net.obj.Marking <- function(x) { 183 | iterate(x$elements(), function(p) ensure_str(p$name)) 184 | } 185 | 186 | #' Convert to a PM4Py marking 187 | #' 188 | #' Converts a character vector of place identifiers to a PM4Py marking object. 189 | #' 190 | #' @param x A character vector with (possible duplicate) place identifiers. 191 | #' @param petrinet A PM4Py Petri net. 192 | #' @return PM4Py marking object 193 | #' @export 194 | as_pm4py_marking <- function(x, petrinet) { 195 | 196 | if (inherits(x, "pm4py.objects.petri_net.data_petri_nets.data_marking.DataMarking")) { 197 | return(x) 198 | } 199 | 200 | pm4py_petrinet <- import("pm4py.objects.petri_net.obj", convert = FALSE) 201 | marking <- pm4py_petrinet$Marking() 202 | 203 | found <- rep(FALSE, length(x)) 204 | py <- import_builtins() 205 | iter <- py$iter(petrinet$places) 206 | 207 | while (TRUE) { 208 | item <- iter_next(iter) 209 | if (is.null(item)) 210 | break 211 | 212 | loc <- which(x == item$name) 213 | if (any(loc)) { 214 | found[loc] <- TRUE 215 | marking[item] = length(loc) 216 | } 217 | } 218 | 219 | if (all(found)) { 220 | return(marking) 221 | } else { 222 | stop(paste0("Places ", 223 | paste0(x[!found], collapse = ","), 224 | " not found in ", 225 | py_str(petrinet$places))) 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /docs/reference/pm4py.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | PM4PY for R — pm4py • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
    61 |
    62 | 104 | 105 | 106 | 107 |
    108 | 109 |
    110 |
    111 | 116 | 117 |
    118 |

    This package provides access to the Python Process Mining library PM4PY in R 119 | and provides conversion between bupaR and PM4PY data structures.

    120 |
    121 | 122 |
    pm4py
    123 | 124 | 125 |

    Format

    126 | 127 |

    An object of class python.builtin.module (inherits from python.builtin.object) of length 2.

    128 |

    Details

    129 | 130 |

    To use this package, you need to have a Python environment (Conda or virtualenv) 131 | installed and install the PM4PY package and its dependencies. You can use 132 | the convenience function install_pm4py to let reticulate take care of 133 | install the right version. See the documentation of this function for further 134 | information.

    135 |

    When loaded, the object pm4py provides the low-level interface to the main 136 | PM4PY module. Use $ to access sub modules of PM4PY as described in the 137 | reticulate documentation:

    138 |

    vignette("calling_python", package = "reticulate")

    139 |

    For parts of PM4PY wrapper functions are provided to transparently convert 140 | parameters and results to and from the corresponding bupaR S3 classes.

    141 | 142 |

    Examples

    143 |
    # Print the PM4PY version loaded 144 | if (pm4py_available()) { 145 | print(pm4py$`__version__`) 146 | }
    #> [1] "1.2.7"
    147 |
    148 |
    149 | 158 |
    159 | 160 | 161 |
    162 | 165 | 166 |
    167 |

    Site built with pkgdown 1.4.1.

    168 |
    169 | 170 |
    171 |
    172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /docs/reference/parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | PM4Py parameter keys — parameters • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 | 103 | 104 | 105 | 106 |
    107 | 108 |
    109 |
    110 | 115 | 116 |
    117 |

    Convenience methods to use as PM4Py parameter keys.

    118 |
    119 | 120 |
    param_activity_key(value)
    121 | 
    122 | param_attribute_key(value)
    123 | 
    124 | param_timestamp_key(value)
    125 | 
    126 | param_caseid_key(value)
    127 | 
    128 | param_resource_key(value)
    129 | 
    130 | default_parameters(eventlog)
    131 | 132 |

    Arguments

    133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 |
    value

    The value to add to the list.

    eventlog

    A bupaR or PM4PY event log.

    144 | 145 |

    Value

    146 | 147 |

    a list with the parameter key/value pair

    148 | 149 |

    Examples

    150 |
    param_activity_key("activity")
    #> $`pm4py:param:activity_key` 151 | #> [1] "activity" 152 | #>
    153 | library(eventdataR) 154 | data(patients) 155 | default_parameters(patients)
    #> $`pm4py:param:activity_key` 156 | #> [1] "handling" 157 | #> 158 | #> $`pm4py:param:timestamp_key` 159 | #> [1] "time" 160 | #> 161 | #> $case_id_glue 162 | #> [1] "patient" 163 | #>
    164 |
    165 |
    166 | 175 |
    176 | 177 | 178 |
    179 | 182 | 183 |
    184 |

    Site built with pkgdown 1.4.1.

    185 |
    186 | 187 |
    188 |
    189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /docs/reference/as_pm4py_marking.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Convert to a PM4Py marking — as_pm4py_marking • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 | 103 | 104 | 105 | 106 |
    107 | 108 |
    109 |
    110 | 115 | 116 |
    117 |

    Converts a character vector of place identifiers to a PM4Py marking object.

    118 |
    119 | 120 |
    as_pm4py_marking(x, petrinet)
    121 | 122 |

    Arguments

    123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
    x

    A character vector with (possible duplicate) place identifiers.

    petrinet

    A PM4Py Petri net.

    134 | 135 | 136 |

    Examples

    137 |
    if (pm4py_available()) { 138 | library(eventdataR) 139 | data(patients) 140 | 141 | # As Inductive Miner of PM4PY is not life-cycle aware, keep only `complete` events: 142 | patients_completes <- patients[patients$registration_type == "complete", ] 143 | 144 | net <- discovery_inductive(patients_completes) 145 | as_pm4py_marking(c("sink"), r_to_py(net$petrinet)) 146 | }
    #> ['sink:1']
    147 |
    148 |
    149 | 157 |
    158 | 159 | 160 |
    161 | 164 | 165 |
    166 |

    Site built with pkgdown 1.4.1.

    167 |
    168 | 169 |
    170 |
    171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /docs/reference/petrinet_check_wfnet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Check Workflow net property — petrinet_check_wfnet • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 | 103 | 104 | 105 | 106 |
    107 | 108 |
    109 |
    110 | 115 | 116 |
    117 |

    Checks if the Petri net is a Workflow net

    118 |
    119 | 120 |
    petrinet_check_wfnet(pn, convert = TRUE)
    121 | 122 |

    Arguments

    123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
    pn

    Petri net

    convert

    TRUE to automatically convert Python objects to their R equivalent. If you pass FALSE you can do manual conversion using the r-py-conversion function.

    134 | 135 |

    Value

    136 | 137 |

    A single logical

    138 | 139 |

    Examples

    140 |
    if (pm4py_available()) { 141 | library(eventdataR) 142 | data(patients) 143 | 144 | # As Inductive Miner of PM4PY is not life-cycle aware, keep only `complete` events: 145 | patients_completes <- patients[patients$registration_type == "complete", ] 146 | 147 | net <- discovery_inductive(patients_completes) 148 | petrinet_check_wfnet(net$petrinet) 149 | 150 | }
    #> [1] TRUE
    151 |
    152 | 161 |
    162 | 163 | 164 |
    165 | 168 | 169 |
    170 |

    Site built with pkgdown 1.4.1.

    171 |
    172 | 173 |
    174 |
    175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /docs/reference/write_pnml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Write Petri net as PNML — write_pnml • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 | 103 | 104 | 105 | 106 |
    107 | 108 |
    109 |
    110 | 115 | 116 |
    117 |

    Write Petri net as PNML

    118 |
    119 | 120 |
    write_pnml(petrinet, file, initial_marking = NULL, final_marking = NULL)
    121 | 122 |

    Arguments

    123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 137 | 138 | 139 | 140 | 141 | 142 |
    petrinet

    A bupaR or PM4PY Petri net.

    file

    File name of the PNML file

    initial_marking

    A R vector with the place identifiers of the initial marking or a PM4PY marking. 136 | By default the initial marking of the bupaR Petri net will be used if available.

    final_marking

    A R vector with the place identifiers of the final marking or a PM4PY marking.

    143 | 144 | 145 |

    Examples

    146 |
    # don't test automatically since this writes a file 147 | # \donttest{ 148 | if (pm4py_available()) { 149 | library(eventdataR) 150 | data(patients) 151 | 152 | # As Inductive Miner of PM4PY is not life-cycle aware, keep only `complete` events: 153 | patients_completes <- patients[patients$registration_type == "complete", ] 154 | 155 | net <- discovery_inductive(patients_completes) 156 | write_pnml(net$petrinet, 157 | "test.pnml", 158 | net$initial_marking, 159 | net$final_marking) 160 | } 161 | # }
    162 |
    163 | 171 |
    172 | 173 | 174 |
    175 | 178 | 179 |
    180 |

    Site built with pkgdown 1.4.1.

    181 |
    182 | 183 |
    184 |
    185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /docs/reference/petrinet_check_relaxed_soundness.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Check Relaxed soundness property — petrinet_check_relaxed_soundness • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 | 103 | 104 | 105 | 106 |
    107 | 108 |
    109 |
    110 | 115 | 116 |
    117 |

    Checks if the Petri net is relaxed sound

    118 |
    119 | 120 |
    petrinet_check_relaxed_soundness(pn, im = NULL, fm = NULL, convert = TRUE)
    121 | 122 |

    Arguments

    123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |
    pn

    Petri net

    im

    Initial marking of the Petri net (optional for workflow nets)

    fm

    Final marking of the Petri net (optional for workflow nets)

    convert

    TRUE to automatically convert Python objects to their R equivalent. If you pass FALSE you can do manual conversion using the r-py-conversion function.

    142 | 143 |

    Value

    144 | 145 |

    A single logical

    146 | 147 |

    Examples

    148 |
    if (pm4py_available()) { 149 | library(eventdataR) 150 | data(patients) 151 | 152 | # As Inductive Miner of PM4PY is not life-cycle aware, keep only `complete` events: 153 | patients_completes <- patients[patients$registration_type == "complete", ] 154 | 155 | net <- discovery_inductive(patients_completes) 156 | petrinet_check_relaxed_soundness(net$petrinet) 157 | 158 | }
    #> [1] TRUE
    159 |
    160 | 169 |
    170 | 171 | 172 |
    173 | 176 | 177 |
    178 |

    Site built with pkgdown 1.4.1.

    179 |
    180 | 181 |
    182 |
    183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /docs/reference/install_pm4py.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Install PM4PY library — install_pm4py • pm4py 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
    64 |
    65 | 107 | 108 | 109 | 110 |
    111 | 112 |
    113 |
    114 | 119 | 120 |
    121 |

    Installs the pm4py package and its dependencies using pip since no 122 | Conda package is available. Further information on the parameters can 123 | be found in the reticulate package documentation: 124 | https://rstudio.github.io/reticulate/ 125 | In some cases (multiple Python versions installed) it might be useful to specify the exact path to the conda binary.

    126 |
    127 | 128 |
    install_pm4py(method = "auto", conda = "auto", version = NULL, ...)
    129 | 130 |

    Arguments

    131 | 132 | 133 | 134 | 135 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |
    method

    Installation method. By default, "auto" automatically finds a method that will work in the local environment. 136 | Change the default to force a specific installation method. Note that the "virtualenv" method is not available on Windows.

    conda

    Path to conda executable (or "auto" to find conda using the PATH and other conventional install locations).

    version

    Optional parameter overriding the PM4Py version that will be installed. Note that the R pm4py package was only tested against the default PM4Py version that will be installed.

    ...

    Additional arguments passed to py_install().

    151 | 152 |

    Details

    153 | 154 |

    Additional requirements, for example, a C++ compiler and GraphViz might 155 | need to be installed to leverage all functionality. Please refer to the PM4Py documentation for details: 156 | https://pm4py.fit.fraunhofer.de/install

    157 | 158 |

    Examples

    159 |
    # \donttest{ 160 | pm4py::install_pm4py() 161 | 162 | # Specify path to conda 163 | pm4py::install_pm4py(method = "conda", conda = "/home/user/miniconda3/bin/conda") 164 | 165 | # Install specific version of PM4Py 166 | pm4py::install_pm4py(version = "1.2.7") 167 | # }
    168 |
    169 | 178 |
    179 | 180 | 181 | 191 |
    192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | --------------------------------------------------------------------------------