This vignette demonstrates how to use the iqtigfunctions
package and a current QSKH QIDB in the Microsoft Access format to evaluate selected indicators on a dataset.
It is not the goal to cover all edge cases or to be particularly efficient, but to provide a starting point for your own analyses.
The following packages are being used:
iqtigfunctions
DBI
odbc
dplyr
stringr
withr
In addition you need a Microsoft Access ODBC driver on your computer.
At first we need to download an IQTIG Access QIDB (e.g. for 2018)
file_path_zip <- tempfile()
qidb_url <- "https://iqtig.org/dateien/qs-instrumente/qidb/QIDB_2018_INDIREKT_V05_2019-05-17.zip"
download.file(qidb_url, destfile = file_path_zip)
expected_path <- file.path(
"Indirekte Verfahren",
"QIDB_Access_Datenbank",
"QSKH_2018_QIDB-INDIR_V05_2019-05-17.ACCDB"
)
temp_dir <- tempdir()
unzip(file_path_zip, files = expected_path, exdir = temp_dir)
qidb_path <- file.path(temp_dir, expected_path)
After that we can use the DBI
and odbc
packages to read in the data.
connection_string <- paste0(
"Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=", qidb_path)
con <- DBI::dbConnect(
odbc::odbc(), .connection_string = connection_string, encoding = "latin1")
module <- "17/1"
# Metadata für HEP
Listenwert <- DBI::dbReadTable(con, "Listenwert")
Listenwert <- group_by(Listenwert, LSTW_LST_Name) %>%
summarise(LSTW_Werte = list(LSTW_Wert))
Vorberechnung <- DBI::dbReadTable(con, "Vorberechnung") %>%
filter(VB_AM_Bezeichnung == module)
Funktion <- DBI::dbReadTable(con, "Funktion") %>%
filter(FN_AM_Bezeichnung == module)
Indikator_Kennzahl <- DBI::dbReadTable(con, "Indikator_Kennzahl") %>%
filter(QIK_AM_Bezeichnung == module)
DBI::dbDisconnect(con)
We prepared a dummy dataset that matches the structure needed to apply indicators. The dataset should be a join of all sub-datasets of the respective module, these sub-datasets are identified by artificial columns of the pattern TDS_X
(e. g. TDS_B
, TDS_O
, TDS_PROZ
).
path <- system.file("extdata", "dummy_17_1_B.rds", package = "iqtigfunctions")
data <- readRDS(path)
Certain fields can have multiple values that are often encoded in a sequence of columns, typically named FIELD_1
, FIELD_2
, FIELD_3
, etc. The function gather_composite_fields()
identifies these columns and collapses them into a single list column FIELD
. This is in particular necessary for %any_like%
, %all_like%
etc., which expect list columns as input.
At last, we need to ensure that all time related columns have proper R types.
Lists are accessed using a global variable LST
:
LST <- list()
for (i in seq_len(nrow(Listenwert))) {
value <- Listenwert[i, ]
LST[[value$LSTW_LST_Name]] <- unlist(value$LSTW_Werte)
}
Precomputations are accessed using the global variable VB
:
iqtigfunctions
Before we can compute the indicators we need to evaluate all computed fields (Funktionen) and add them as columns to our central dataset.
Precomputations are not considered here, because all precomputations in the example are on the “Gesamt” dimension. In other cases, this requires more work.
There are dependencies among the computed fields, as a lot of computed fields reference other computed fields. The following code evalues all computed fields in a loop until no error occurs or the iteration limit has been reached. The proper way to do it would be to construct a dependency graph and sort the fields topologically, but for this toy example this naive method works fine.
row_ids <- seq_len(nrow(Funktion))
iteration_count <- 1L
errors <- TRUE
while(errors && iteration_count <= sum(row_ids)) {
errors <- FALSE
for (row_id in row_ids) {
funktion <- Funktion[row_id, ]
if (funktion$FN_Feldname %in% colnames(data)) {
next
}
tryCatch({
result <- evaluate_code(data, funktion$FN_Script)
data[[funktion$FN_Feldname]] <- result
message("Added computed field ", funktion$FN_Feldname)
}, error = function(e) {
errors <<- TRUE
})
}
iteration_count <- iteration_count + 1L
}
#> Added computed field fn_alter_begrenzt
#> Added computed field fn_ANTITHROMBMITTELH_max_risiko
#> Added computed field fn_M17N1Score_54033
#> Added computed field fn_M17N1Score_54040
#> Added computed field fn_M17N1Score_54042
#> Added computed field fn_M17N1Score_54046
#> Added computed field fn_Schwellenwert_praeopminuten
Now we have all information needed to evaluate our quality indicators. In order to keep the computation concise we only cover rate indicators.
Indikator_Kennzahl %>%
filter(
QIK_Bewertungsart == "Ratenbasiert",
!grepl("(.berdoku|Unterdoku|MDS)", QIK_Bezeichnung)
) %>%
rowwise() %>%
do({
indikator <- .
tds <- strsplit(
indikator$QIK_Teildatensatzbezug, split = ":", fixed = TRUE
)[[1L]][[2L]]
tryCatch({
O_result <- evaluate_code(data, indikator$QIK_Formel_Zaehler)
N_result <- evaluate_code(data, indikator$QIK_Formel_Nenner)
data.frame(
IKNRKH = data$IKNRKH, STANDORT = data$STANDORT,
O = O_result & N_result,
N = N_result,
TDS = data[[paste0("TDS_", tds)]]
) %>%
group_by(TDS) %>%
# In case results are needed per unit:
# group_by(IKNRKH, STANDORT, TDS) %>%
summarise(O = max(O), N = max(N)) %>%
summarise(
QIK_ID = indikator$QIK_ID,
O = sum(O),
N = sum(N),
Result = O / N
)
}, error = function(e) {
tibble(
QIK_ID = indikator$QIK_ID,
O = NA_real_,
N = NA_real_,
Result = O / N
)
})
})
#> Source: local data frame [4 x 4]
#> Groups: <by row>
#>
#> # A tibble: 4 x 4
#> QIK_ID O N Result
#> * <chr> <int> <int> <dbl>
#> 1 54050 2 26 0.0769
#> 2 54030 66 100 0.66
#> 3 850149 24 24 1
#> 4 54029 88 100 0.88