| Title: | Audit and Analysis Dashboard for Haematological Oncology Cohorts |
|---|---|
| Description: | Reads tumour-documentation spreadsheets from haematological tumour centres and computes quality and coverage indicators, OPS-8-544 complex chemotherapy block counts, OPS-1-941 complex diagnostics counts, Kaplan-Meier analyses, oncoprint and cytogenetics summaries, plus an interactive Shiny dashboard for auditor live evaluation. Includes a fully synthetic example cohort for demonstration and testing. |
| Authors: | Hanno Witte [aut], Raban Heller [aut, cre] (ORCID: <https://orcid.org/0000-0001-8006-9742>) |
| Maintainer: | Raban Heller <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-11-04 22:07:32 UTC |
| Source: | https://github.com/CTTIR/zhncommandR |
A thin S3 wrapper around a data frame returned by zhn_read_cohort().
Inherits from data.frame so all base subscripting and dplyr verbs work;
the class is preserved across [-subsetting but stripped by most dplyr
verbs (use tibble::as_tibble() to opt out cleanly).
new_cohort_df(x = data.frame(), sheet_to_use = NA_character_)new_cohort_df(x = data.frame(), sheet_to_use = NA_character_)
x |
A data frame. |
sheet_to_use |
Name of the workbook sheet the data came from (carried as an attribute). |
A cohort_df (S3 class), inheriting from data.frame.
Other classes:
new_diagnostic_blocks(),
new_therapy_blocks()
S3 wrapper for the output of zhn_prepare_diagnostic_blocks().
new_diagnostic_blocks(x = data.frame(), component_cols = "")new_diagnostic_blocks(x = data.frame(), component_cols = "")
x |
A data frame. |
component_cols |
Character; comma-joined list of detected component columns (Morphologie / Immunphaenotypisierung / Zytogenetik / Molekulargenetik). Carried as an attribute. |
Other classes:
new_cohort_df(),
new_therapy_blocks()
S3 wrapper for the output of zhn_prepare_therapy_blocks().
new_therapy_blocks(x = data.frame(), patient_cols_used = "")new_therapy_blocks(x = data.frame(), patient_cols_used = "")
x |
A data frame. |
patient_cols_used |
Character; pipe-joined list of raw patient
columns coalesced into |
Other classes:
new_cohort_df(),
new_diagnostic_blocks()
Domain rules for separating true mutations/variants from structural and cytogenetic findings, and from negative/missing entries. The categories returned drive the oncoprint vs. cytogenetics-table split in the dashboard.
zhn_alteration_type(x)zhn_alteration_type(x)
x |
Character vector of free-text alteration strings. |
The classification regex is clinical-domain knowledge — do not "improve" it without a corresponding update to the test fixtures.
Character vector the same length as x, drawn from:
"Nicht verwertbar/NA",
"negativ/kein Nachweis",
"Strukturell/Zytogenetik: Deletion/Loss",
"Strukturell/Zytogenetik: Zugewinn/Amplifikation",
"Strukturell/Zytogenetik: Translokation/Rearrangement/Bruch",
"Strukturell/Zytogenetik: Komplexer Karyotyp",
"Mutation/Variante".
Other alteration:
zhn_is_mutation(),
zhn_normalize_alteration()
zhn_alteration_type(c( "TP53 Mutation", "del(17p)", "Trisomie 12", "t(11;14)", "komplexer Karyotyp", "negativ", NA ))zhn_alteration_type(c( "TP53 Mutation", "del(17p)", "Trisomie 12", "t(11;14)", "komplexer Karyotyp", "negativ", NA ))
Returns the on-disk path of the synthetic example .xlsx shipped with
the package. The example is 100% fake data — every name, diagnosis
and finding is invented for demonstration and testing.
zhn_example_path()zhn_example_path()
Used as the data source when the dashboard's "Beispieldaten laden" button is clicked, and in the documentation examples.
Length-1 character. The path to zhn_example.xlsx.
zhn_example_path() if (interactive()) zhn_read_cohort(zhn_example_path())zhn_example_path() if (interactive()) zhn_read_cohort(zhn_example_path())
Convenience predicate. Only true mutations/variants populate the oncoprint tile plot; structural/cytogenetic findings are reported in their own tabular and cytogenetics-tab views.
zhn_is_mutation(alteration_class)zhn_is_mutation(alteration_class)
alteration_class |
Character vector as returned by
|
Logical vector the same length as alteration_class.
Other alteration:
zhn_alteration_type(),
zhn_normalize_alteration()
zhn_is_mutation(zhn_alteration_type(c("TP53", "del(17p)")))zhn_is_mutation(zhn_alteration_type(c("TP53", "del(17p)")))
For a grouping with more than two levels, computes a log-rank test for every pair of groups and adjusts the raw p-values with the BH method.
zhn_km_pairwise(data, time_col, event_col, group_col)zhn_km_pairwise(data, time_col, event_col, group_col)
data |
A data frame / cohort with the time and event columns. |
time_col, event_col
|
Column names (character) for follow-up time and event; the event is parsed to 0/1 via the package's hardened coercion. |
group_col |
Optional grouping column (character), or |
A data frame with Gruppe_1, Gruppe_2, p_roh, p_BH.
Other figures:
zhn_pal,
zhn_plot_cases_by_year(),
zhn_plot_diagnoses(),
zhn_plot_km(),
zhn_register_fonts(),
zhn_save_plot(),
zhn_theme()
Collapses whitespace and strips trailing "Mutation"/"Mut" suffixes used by clinicians documenting variants in free text.
zhn_normalize_alteration(x)zhn_normalize_alteration(x)
x |
Character vector. |
Character vector the same length as x.
Other alteration:
zhn_alteration_type(),
zhn_is_mutation()
zhn_normalize_alteration(c("TP53 Mutation", " NRAS Mut", "del(17p)"))zhn_normalize_alteration(c("TP53 Mutation", " NRAS Mut", "del(17p)"))
Single source of truth for the figure colours. Hugo Coder blue is the
data-ink accent (suite purple stays the app chrome accent); revert the
figures to suite purple by changing accent to "#5E2C8E" here.
zhn_palzhn_pal
A named list with accent, accent_light, accent_dark, grid.
Other figures:
zhn_km_pairwise(),
zhn_plot_cases_by_year(),
zhn_plot_diagnoses(),
zhn_plot_km(),
zhn_register_fonts(),
zhn_save_plot(),
zhn_theme()
zhn_parse_cytogenetics(df, remove_negative = TRUE)zhn_parse_cytogenetics(df, remove_negative = TRUE)
df |
Cohort data frame from |
remove_negative |
Logical; drop |
Same logic as zhn_parse_oncoprint() but consumes the separate
zytogenetik/karyotyp/fish column. Used by the cytogenetics tab.
Data frame with patient_label, diagnose_label, alteration,
alteration_class, zytogenetik_raw. Empty input returns a 0-row
tibble.
Other parsers:
zhn_parse_oncoprint(),
zhn_prepare_diagnostic_blocks(),
zhn_prepare_therapy_blocks()
zhn_parse_oncoprint(df, remove_negative = TRUE)zhn_parse_oncoprint(df, remove_negative = TRUE)
df |
Cohort data frame from |
remove_negative |
Logical; drop |
Splits the free-text mutation column into per-alteration rows, classifies
each entry via zhn_alteration_type(), and flags genuine mutations for
the oncoprint tile plot.
Unlike the legacy v5 implementation, this function does not call
shiny::validate() – packaged functions must work outside Shiny. If the
required source column is missing, the function raises a cli::cli_abort
so the server layer can wrap the message with validate(need(...)).
A data frame with one row per (patient, alteration) pair:
patient_label, diagnose_label, alteration, alteration_class,
alteration_raw, oncoprint_mutation. Empty input returns a 0-row
tibble with this column set.
Other parsers:
zhn_parse_cytogenetics(),
zhn_prepare_diagnostic_blocks(),
zhn_prepare_therapy_blocks()
Figure: cases per treatment year (vertical bar)
zhn_plot_cases_by_year( data, year_col = "behandlungsjahr", transparent = FALSE, xlab = "Jahr", ylab = "Anzahl" )zhn_plot_cases_by_year( data, year_col = "behandlungsjahr", transparent = FALSE, xlab = "Jahr", ylab = "Anzahl" )
data |
A data frame with a treatment-year column. |
year_col |
Name of the year column. |
transparent |
Passed to |
xlab, ylab
|
Axis labels. |
A ggplot2::ggplot object.
Other figures:
zhn_km_pairwise(),
zhn_pal,
zhn_plot_diagnoses(),
zhn_plot_km(),
zhn_register_fonts(),
zhn_save_plot(),
zhn_theme()
Counts the most frequent values of a diagnosis/coding column and draws a horizontal bar chart in the Hugo Coder look. Title-less by design – the name lives in the card header and the export filename, not in the plot.
zhn_plot_diagnoses(data, col, n = 20, transparent = FALSE, ylab = "Anzahl")zhn_plot_diagnoses(data, col, n = 20, transparent = FALSE, ylab = "Anzahl")
data |
A data frame (typically the filtered cohort). |
col |
Name of the diagnosis/coding column to count. |
n |
Number of top categories to show. |
transparent |
Passed to |
ylab |
Count-axis label. |
A ggplot2::ggplot object.
Other figures:
zhn_km_pairwise(),
zhn_pal,
zhn_plot_cases_by_year(),
zhn_plot_km(),
zhn_register_fonts(),
zhn_save_plot(),
zhn_theme()
Builds a themed Kaplan-Meier figure with ggsurvfit. Every publication
element is an argument; the same call drives the live app and the PNG/PDF
export. With a risk table the return value is the aligned plot+table
composite (ggsurvfit::ggsurvfit_build() output) so zhn_save_plot() writes
the full figure. This is the one figure that keeps an (editable) title.
zhn_plot_km( data, time_col, event_col, group_col = NULL, conf_int = TRUE, conf_level = 0.95, conf_type = "log-log", risktable = TRUE, risktable_stats = "n.risk", censor_marks = TRUE, show_pvalue = TRUE, show_hr = FALSE, show_median = FALSE, y_scale = c("survival", "percent", "cuminc"), x_break = NULL, x_max = NULL, legend_pos = "top", title = NULL, subtitle = NULL, caption = NULL, transparent = FALSE )zhn_plot_km( data, time_col, event_col, group_col = NULL, conf_int = TRUE, conf_level = 0.95, conf_type = "log-log", risktable = TRUE, risktable_stats = "n.risk", censor_marks = TRUE, show_pvalue = TRUE, show_hr = FALSE, show_median = FALSE, y_scale = c("survival", "percent", "cuminc"), x_break = NULL, x_max = NULL, legend_pos = "top", title = NULL, subtitle = NULL, caption = NULL, transparent = FALSE )
data |
A data frame / cohort with the time and event columns. |
time_col, event_col
|
Column names (character) for follow-up time and event; the event is parsed to 0/1 via the package's hardened coercion. |
group_col |
Optional grouping column (character), or |
conf_int |
Logical; show confidence-interval ribbons. |
conf_level |
CI level (e.g. 0.90/0.95/0.99). |
conf_type |
CI method: |
risktable |
Logical; show the numbers-at-risk table below the plot. |
risktable_stats |
Which rows: any of |
censor_marks |
Logical; show censoring tick marks. |
show_pvalue |
Logical; annotate the log-rank p-value (>= 2 groups). |
show_hr |
Logical; annotate the Cox HR with 95% CI (exactly 2 groups),
always with a |
show_median |
Logical; draw reference lines at the median survival. |
y_scale |
|
x_break |
Numeric x-axis break interval, or |
x_max |
Numeric x-axis truncation, or |
legend_pos |
|
title, subtitle, caption
|
Character or |
transparent |
Logical; transparent background for display/export. |
A ggsurvfit/ggplot object; a patchwork composite when
risktable = TRUE. Carries an "n_groups" attribute.
Other figures:
zhn_km_pairwise(),
zhn_pal,
zhn_plot_cases_by_year(),
zhn_plot_diagnoses(),
zhn_register_fonts(),
zhn_save_plot(),
zhn_theme()
if (requireNamespace("ggsurvfit", quietly = TRUE)) { co <- zhn_read_cohort(zhn_example_path(), verbose = FALSE) zhn_plot_km(co, "os", "death_event") # default single curve zhn_plot_km(co, "os", "death_event", group_col = "geschlecht", show_pvalue = TRUE) # grouped + log-rank }if (requireNamespace("ggsurvfit", quietly = TRUE)) { co <- zhn_read_cohort(zhn_example_path(), verbose = FALSE) zhn_plot_km(co, "os", "death_event") # default single curve zhn_plot_km(co, "os", "death_event", group_col = "geschlecht", show_pvalue = TRUE) # grouped + log-rank }
zhn_prepare_diagnostic_blocks(df)zhn_prepare_diagnostic_blocks(df)
df |
Data frame returned by |
A diagnostic_blocks S3 object with
patient, diagnose, datum, primaerfall, patientenfall, jahr,
monat_sort, plus the component columns (Morphologie,
Immunphaenotypisierung, Zytogenetik, Molekulargenetik). Empty input
returns a 0-row object.
Other parsers:
zhn_parse_cytogenetics(),
zhn_parse_oncoprint(),
zhn_prepare_therapy_blocks()
zhn_prepare_therapy_blocks(df)zhn_prepare_therapy_blocks(df)
df |
Data frame returned by |
Reshapes the raw therapy sheet (as returned by zhn_read_therapy()) into
the per-block tibble the dashboard counts. Counts every numeric
OPS-8-544 entry > 0 as a positive block; falls back to deduplicating
Patient/Datum/Zyklus/Protokoll combinations when no OPS column exists.
A therapy_blocks S3 object (inheriting from
data.frame) with at minimum: therapieprotokoll, diagnose,
patient, datum, zyklus, jahr, monat_sort. Empty input returns
a 0-row object with this column set; the patient_cols_used attribute
records which raw columns were coalesced into patient.
Other parsers:
zhn_parse_cytogenetics(),
zhn_parse_oncoprint(),
zhn_prepare_diagnostic_blocks()
zhn_read_cohort(path, sheet = NULL, verbose = TRUE)zhn_read_cohort(path, sheet = NULL, verbose = TRUE)
path |
Path to the |
sheet |
Optional sheet name to override the canonical/regex resolver. |
verbose |
Logical; emit a |
Reads the primary cohort sheet from a tumour-documentation workbook,
cleans column names via janitor::clean_names(), drops trailing empty
none* columns, and derives a behandlungsjahr (treatment year)
column from the first available date column.
Replaces the legacy read_main_data() from v5 which scanned ~/Desktop
for Daten.xlsx. The package never reads ~/Desktop.
A cohort_df S3 object (inheriting from
data.frame) with cleaned names, an added integer behandlungsjahr
column, and a "sheet_to_use" attribute. Empty input workbooks return
a 0-row object.
Other readers:
zhn_read_diagnostics(),
zhn_read_therapy(),
zhn_read_tumorboard()
if (interactive()) { df <- zhn_read_cohort(zhn_example_path()) head(df) }if (interactive()) { df <- zhn_read_cohort(zhn_example_path()) head(df) }
Reads the diagnostics sheet — canonical name Komplexe Diagnostik.
If missing, returns a 0-row data frame with a "source_label" attribute.
zhn_read_diagnostics(path, sheet = NULL, verbose = TRUE)zhn_read_diagnostics(path, sheet = NULL, verbose = TRUE)
path |
Path to the |
sheet |
Optional sheet name to override the canonical/regex resolver. |
verbose |
Logical; emit a |
A data frame with cleaned names and a "source_label" attribute.
0-row if no diagnostics sheet exists.
Other readers:
zhn_read_cohort(),
zhn_read_therapy(),
zhn_read_tumorboard()
Reads the therapy-block sheet — typically the canonical name
Komplexe Chemotherapie — from the workbook. If the sheet is genuinely
missing, returns a 0-row data frame with a "source_label" attribute
describing the situation; the dashboard surfaces this in the therapy tab.
zhn_read_therapy(path, sheet = NULL, verbose = TRUE)zhn_read_therapy(path, sheet = NULL, verbose = TRUE)
path |
Path to the |
sheet |
Optional sheet name to override the canonical/regex resolver. |
verbose |
Logical; emit a |
A data frame with cleaned names and a "source_label" attribute.
0-row if no therapy sheet exists.
Other readers:
zhn_read_cohort(),
zhn_read_diagnostics(),
zhn_read_tumorboard()
Reads a CSV file written by the dashboard's tumour-board download. Returns
an empty, typed data frame when the file is missing or empty — the
contract is the same in both cases so downstream bind_rows is safe.
zhn_read_tumorboard(path = NULL)zhn_read_tumorboard(path = NULL)
path |
Path to a CSV (typically from a |
A data frame with columns Patient, Board_Datum,
Tumorboardbeschluss, Verantwortlich, Erfasst_am. Always returns
this column set, even with 0 rows.
Other readers:
zhn_read_cohort(),
zhn_read_diagnostics(),
zhn_read_therapy()
Registers the OFL Inter weights shipped in inst/fonts/ with
systemfonts so the same family = "Inter" renders in the live
ragg Shiny device, in 600-dpi PNG exports (ragg::agg_png) and in
vector PDF exports (grDevices::cairo_pdf). Safe to call repeatedly. If the
fonts or systemfonts are unavailable it does nothing and figures fall
back to the default device font (see zhn_theme()); it never errors.
zhn_register_fonts()zhn_register_fonts()
Invisibly, TRUE if Inter was registered, otherwise FALSE.
Other figures:
zhn_km_pairwise(),
zhn_pal,
zhn_plot_cases_by_year(),
zhn_plot_diagnoses(),
zhn_plot_km(),
zhn_save_plot(),
zhn_theme()
Opens the auditor live-evaluation dashboard. With no data file uploaded the app starts empty; the "Beispieldaten laden" button loads the synthetic example cohort.
zhn_run_app(...)zhn_run_app(...)
... |
Passed to |
The app is copied to a session temporary directory and run from there.
shiny::runApp() sets the working directory to the app folder, so running
it directly from the package library would keep that folder open and block
updating or reinstalling the package on Windows ("device or resource busy")
while the app is running. Running from a temp copy avoids that lock; the
package's own files (translations, example data) are still read from the
installed library via system.file().
Invisible NULL; called for the side effect of starting the
Shiny app.
zhn_run_app()zhn_run_app()
One save helper for every figure so resolution, device and font rules live
in one place. PNG uses ragg::agg_png (sharp text, true 600 dpi, reliable
transparency and font rendering); PDF uses grDevices::cairo_pdf (vector
output that embeds Inter — base pdf() would not). The bundled Inter font is
registered first so exported text matches the screen.
zhn_save_plot( plot, file, format = c("png", "pdf"), width = 8, height = 5, transparent = FALSE )zhn_save_plot( plot, file, format = c("png", "pdf"), width = 8, height = 5, transparent = FALSE )
plot |
A ggplot2::ggplot (or other printable plot) object. |
file |
Output path; written as-is. |
format |
One of |
width, height
|
Dimensions in inches. |
transparent |
Logical; transparent background if |
The file path, invisibly.
Other figures:
zhn_km_pairwise(),
zhn_pal,
zhn_plot_cases_by_year(),
zhn_plot_diagnoses(),
zhn_plot_km(),
zhn_register_fonts(),
zhn_theme()
p <- ggplot2::ggplot(mtcars, ggplot2::aes(factor(cyl))) + ggplot2::geom_bar(fill = zhn_pal$accent) + zhn_theme() tmp <- tempfile(fileext = ".png") zhn_save_plot(p, tmp, format = "png", width = 6, height = 4)p <- ggplot2::ggplot(mtcars, ggplot2::aes(factor(cyl))) + ggplot2::geom_bar(fill = zhn_pal$accent) + zhn_theme() tmp <- tempfile(fileext = ".png") zhn_save_plot(p, tmp, format = "png", width = 6, height = 4)
A single ggplot2 theme used by every zhn_plot_*() function so the figure
look is identical everywhere and a restyle is a one-line edit. Minimal panel
(light major gridlines only, no minor grid, no panel border), Inter type, and
generous margins.
zhn_theme(base_size = 11, transparent = FALSE)zhn_theme(base_size = 11, transparent = FALSE)
base_size |
Base font size in points. |
transparent |
If |
A ggplot2::theme object (add it to a plot with +).
Other figures:
zhn_km_pairwise(),
zhn_pal,
zhn_plot_cases_by_year(),
zhn_plot_diagnoses(),
zhn_plot_km(),
zhn_register_fonts(),
zhn_save_plot()
# Returns a ggplot2 theme object; add it to any plot with `+`. th <- zhn_theme() th_transparent <- zhn_theme(base_size = 13, transparent = TRUE)# Returns a ggplot2 theme object; add it to any plot with `+`. th <- zhn_theme() th_transparent <- zhn_theme(base_size = 13, transparent = TRUE)