| Title: | SPC Visualization for Healthcare Quality Improvement |
|---|---|
| Description: | A modern R package for creating Statistical Process Control (SPC) charts in healthcare settings. Built on ggplot2 and qicharts2, BFHcharts provides beautiful, publication-ready SPC visualizations with configurable themes and multi-organizational branding support. Inspired by BBC's bbplot design philosophy. |
| Authors: | Johan Reventlow [aut, cre] |
| Maintainer: | Johan Reventlow <[email protected]> |
| License: | GPL-3 + file LICENSE |
| Version: | 0.23.0 |
| Built: | 2026-06-04 14:15:06 UTC |
| Source: | https://github.com/johanreventlow/BFHcharts |
Strips the S3-class and returns a plain named list suitable for JSON-serialization or downstream-tools that do not understand the class. The structure is preserved verbatim; no field-renaming or filtering occurs.
## S3 method for class 'bfh_spc_analysis' as.list(x, ...)## S3 method for class 'bfh_spc_analysis' as.list(x, ...)
x |
A |
... |
Ignored. |
Named list.
## Not run: analysis <- bfh_analyse(result) flat <- as.list(analysis) json <- jsonlite::toJSON(flat, auto_unbox = TRUE) ## End(Not run)## Not run: analysis <- bfh_analyse(result) flat <- as.list(analysis) json <- jsonlite::toJSON(flat, auto_unbox = TRUE) ## End(Not run)
Used as the event field of audit records produced by
.emit_audit_event() when AI analysis is invoked.
AUDIT_EVENT_AI_EGRESSAUDIT_EVENT_AI_EGRESS
Returnerer struktureret bfh_spc_analysis-objekt med features,
conclusions (i18n-keys), caveats, suggested_actions, og
render-context. Det er primaer canonical-output for struktureret SPC-
analyse; rentekst-rendering sker via bfh_render_analysis().
bfh_analyse(x, metadata = list(), language = c("da", "en"))bfh_analyse(x, metadata = list(), language = c("da", "en"))
x |
A |
metadata |
Optional named list. Kan indeholde:
|
language |
Character: |
Key-only model: conclusions$*_key, caveats$* og
suggested_actions indeholder i18n-noegler, ej resolverede tekst-
strenge. Tekst-resolution sker udelukkende i bfh_render_analysis()
via texts_loader. Dette bevarer language-neutralt
JSON-eksport-output + tillader audit-replay paa anden sprog uden
re-extraction.
Object of class bfh_spc_analysis. See ADR-XXX for schema.
## Not run: result <- bfh_qic(data, x = month, y = value, chart_type = "i") analysis <- bfh_analyse(result, metadata = list(target = ">= 90%")) print(analysis) # Render to text: text <- bfh_render_analysis(analysis, max_chars = 375) # Audit-trail via JSON: jsonlite::toJSON(as.list(analysis), auto_unbox = TRUE, Date = "ISO8601") ## End(Not run)## Not run: result <- bfh_qic(data, x = month, y = value, chart_type = "i") analysis <- bfh_analyse(result, metadata = list(target = ">= 90%")) print(analysis) # Render to text: text <- bfh_render_analysis(analysis, max_chars = 375) # Audit-trail via JSON: jsonlite::toJSON(as.list(analysis), auto_unbox = TRUE, Date = "ISO8601") ## End(Not run)
Collects all relevant context from a bfh_qic_result object for analysis
generation. Used internally by bfh_generate_analysis().
bfh_build_analysis_context(x, metadata = list())bfh_build_analysis_context(x, metadata = list())
x |
A |
metadata |
Optional list with additional context:
|
Named list with complete context including:
chart_title: Chart title from config
chart_type: Chart type (i, p, c, u, etc.)
y_axis_unit: Y-axis unit label
n_points: Number of data points
centerline: Centerline value
spc_stats: SPC statistics from bfh_extract_spc_stats()
has_signals: Logical indicating if signals were detected
target_value: Numeric target value (NA if absent). Percent-target
normalization: when y_axis_unit == "percent" and the parsed target
value appears to be on the 0-100 scale (display contains "%" or
value > 1), target_value is divided by 100 so downstream
comparisons are on the same 0-1 proportion scale as centerline.
target_display is always preserved unchanged for user-facing text.
target_direction: "higher", "lower", or NULL derived from
operator in metadata$target (NULL for numeric input)
target_display: Original target string for display purposes
User-provided metadata fields
## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") ctx <- bfh_build_analysis_context(result, metadata = list(hospital = "BFH")) str(ctx) ## End(Not run)## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") ctx <- bfh_build_analysis_context(result, metadata = list(hospital = "BFH")) str(ctx) ## End(Not run)
Creates a reusable export session that pre-populates Typst template assets
once and shares them across multiple bfh_export_pdf() calls, eliminating
the repeated recursive template directory copy that dominates I/O cost in
batch workflows.
bfh_create_export_session( font_path = NULL, inject_assets = NULL, strict_baseline = TRUE )bfh_create_export_session( font_path = NULL, inject_assets = NULL, strict_baseline = TRUE )
font_path |
Optional path to directory containing additional fonts.
Applied to all exports in this session. Can be overridden per export via
the |
inject_assets |
Optional callback function called once after template
assets are staged. Receives one argument: the path to the template
directory ( |
strict_baseline |
Logical. Default for the session: when TRUE
(default), every |
Usage pattern:
session <- bfh_create_export_session()
on.exit(close(session))
for (dept in departments) {
bfh_export_pdf(results[[dept]], paste0(dept, ".pdf"),
batch_session = session)
}
Limitations:
Session is single-threaded sequential only - do not share across parallel workers.
Not compatible with template_path (custom templates).
Pass inject_assets here, not to individual bfh_export_pdf() calls.
A bfh_export_session object. Close with close(session)
to remove the session tmpdir.
inject_assets is full code execution. The supplied function
runs with the same privileges as the calling R session, with full file-system
and network access. It MUST NOT come from user input (Shiny inputs, REST API
parameters, configuration files of unknown provenance).
Treat it as trusted-code-only: pass only code-reviewed,
organizationally controlled callbacks.
A runtime heuristic warns when inject_assets originates from
.GlobalEnv or a direct child environment. Suppress with
options(BFHcharts.allow_globalenv_inject = TRUE) in development.
See bfh_export_pdf for the full security rationale,
acceptable/unacceptable sources, and the parallel note for
template_path.
Recommended: companion package for proprietary branding.
Organizations that need consistent proprietary branding across batch
exports should pass a companion-package callback here rather than
hardcoding asset paths. For example:
bfh_create_export_session(inject_assets = MyAssetsPkg::inject_my_assets).
This keeps proprietary fonts and logos out of public BFHcharts
distribution while supporting full branding on Posit Connect Cloud,
RStudio Connect, and Docker deployments. The callback is still
subject to the trusted-code-only contract above. See
bfh_export_pdf Security section for full details.
bfh_export_pdf() for single exports and the full security note
covering both inject_assets and template_path
Generates a Typst document (.typ) using BFH hospital template with
chart image and metadata. Downstream packages (e.g. biSPCharts) can use
this function directly instead of accessing it via
getFromNamespace().
bfh_create_typst_document( chart_image, output, metadata, spc_stats, template = "bfh-diagram", template_path = NULL, skip_template_copy = FALSE )bfh_create_typst_document( chart_image, output, metadata, spc_stats, template = "bfh-diagram", template_path = NULL, skip_template_copy = FALSE )
chart_image |
Path to chart image (SVG or PNG) |
output |
Path for output .typ file |
metadata |
List with template parameters (hospital, department, title,
analysis, details, author, date, data_definition, footer_content).
See |
spc_stats |
List with SPC statistics (runs_expected, runs_actual,
crossings_expected, crossings_actual, outliers_expected,
outliers_actual, is_run_chart). See |
template |
Template name (default: "bfh-diagram") |
template_path |
Optional custom template path. When provided, overrides the packaged template (default: NULL uses packaged template) |
skip_template_copy |
Logical. If TRUE, skip copying the template directory (assumes it is already present in the output directory). Default: FALSE. |
Path to created .typ file (invisibly)
bfh_extract_spc_stats, bfh_merge_metadata
Other utility-functions:
bfh_extract_spc_stats(),
bfh_generate_details(),
bfh_merge_metadata()
## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") metadata <- bfh_merge_metadata( list(department = "Quality"), chart_title = result$config$chart_title ) spc_stats <- bfh_extract_spc_stats(result) tmp_dir <- tempdir() chart_png <- file.path(tmp_dir, "chart.png") ggplot2::ggsave(chart_png, result$plot, width = 8, height = 5) typ_file <- bfh_create_typst_document( chart_image = chart_png, output = file.path(tmp_dir, "document.typ"), metadata = metadata, spc_stats = spc_stats ) ## End(Not run)## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") metadata <- bfh_merge_metadata( list(department = "Quality"), chart_title = result$config$chart_title ) spc_stats <- bfh_extract_spc_stats(result) tmp_dir <- tempdir() chart_png <- file.path(tmp_dir, "chart.png") ggplot2::ggsave(chart_png, result$plot, width = 8, height = 5) typ_file <- bfh_create_typst_document( chart_image = chart_png, output = file.path(tmp_dir, "document.typ"), metadata = metadata, spc_stats = spc_stats ) ## End(Not run)
Exports an SPC chart created by bfh_qic() to a PDF document using
Typst templates for hospital branding. Requires Quarto CLI for compilation.
bfh_export_pdf( x, output, metadata = list(), template = "bfh-diagram", template_path = NULL, restrict_template = TRUE, auto_analysis = FALSE, use_ai = FALSE, data_consent = NULL, use_rag = FALSE, analysis_min_chars = 300, analysis_max_chars = 375, dpi = 150, font_path = NULL, ignore_system_fonts = TRUE, inject_assets = NULL, batch_session = NULL, strict_baseline )bfh_export_pdf( x, output, metadata = list(), template = "bfh-diagram", template_path = NULL, restrict_template = TRUE, auto_analysis = FALSE, use_ai = FALSE, data_consent = NULL, use_rag = FALSE, analysis_min_chars = 300, analysis_max_chars = 375, dpi = 150, font_path = NULL, ignore_system_fonts = TRUE, inject_assets = NULL, batch_session = NULL, strict_baseline )
x |
A |
output |
Character string specifying the output PDF file path |
metadata |
List with optional metadata fields:
|
template |
Character string specifying template name (default: "bfh-diagram") |
template_path |
Optional path to a custom Typst template file. When provided, this overrides the packaged template. The template must exist and be a valid Typst file (.typ). Default is NULL (uses packaged BFH template). |
restrict_template |
Logical. When Threat model: A custom Typst template is compiled by the Typst
binary and can read or write arbitrary paths during compilation. This is
equivalent to Default: Migration from BFHcharts <= 0.15.x: Callers passing
# Before (BFHcharts <= 0.15.x): custom template silently allowed
bfh_export_pdf(result, "out.pdf", template_path = "/my/template.typ")
# After (BFHcharts >= 0.16.0): explicit opt-out required
bfh_export_pdf(result, "out.pdf",
template_path = "/my/template.typ",
restrict_template = FALSE)
|
auto_analysis |
Logical. If TRUE and |
use_ai |
Logical. Controls AI usage for auto-analysis:
Only used when |
data_consent |
Character. Required when |
use_rag |
Logical. Controls RAG for AI analysis. Default |
analysis_min_chars |
Minimum characters for AI-generated analysis. Default 300.
Only used when |
analysis_max_chars |
Maximum characters for AI-generated analysis. Default 375.
Only used when |
dpi |
Resolution passed to |
font_path |
Optional path to directory containing additional fonts.
Passed as |
ignore_system_fonts |
Logical. If |
inject_assets |
Optional callback function called after Typst template
structure is created but before compilation. Receives one argument: the path
to the template directory (e.g., |
batch_session |
Optional
|
strict_baseline |
Logical. When TRUE (default), the function errors
before render if Rationale: PDFs from this function typically reach
quality-improvement leadership where R warnings never surface.
Anhoej & Olesen (2014) recommend >= 8 baseline points for reliable
run/crossing detection; charts with shorter baselines have tight but
statistically unreliable control limits. Strict-by-default forces
explicit acknowledgement of short-baseline output; the interactive
Inheritance: When |
Requirements:
Quarto CLI (>= 1.4.0) must be installed
Install from: https://quarto.org
PDF Generation Process:
Extract chart title from plot (removed from image for template)
Export chart to temporary PNG (without title)
Extract SPC statistics (runs, crossings, outliers)
Generate Typst document (.typ) with template
Compile to PDF via Quarto CLI
Clean up temporary files
Title Handling:
Chart title is extracted and passed to Typst template
Title appears in PDF header, NOT in chart image
This differs from PNG export where title is in the image
Plot Optimization for PDF:
Plot margins are set to 0mm for optimal fit in Typst template
Blank axis titles (NULL or empty) are removed with element_blank()
User-defined axis titles are preserved
SPC Statistics:
Automatically extracted from bfh_qic_result$summary
Displayed in SPC table on PDF
Includes: runs (serielaengde), crossings (antal kryds), outliers
Auto-Generated Details:
If metadata$details is not provided, details are auto-generated
Format: "Periode: start - slut . Gns. interval: values . Seneste interval: values . Nuvaerende niveau: cl"
p/u-charts show numerator/denominator (e.g., "58938/97266")
Other chart types show only values (e.g., "127")
Interval labels adapt to data frequency (month, week, day, etc.)
The input object x invisibly, enabling pipe chaining
inject_assets is full code execution. The supplied function
runs with the same privileges as the calling R session, with full file-system
and network access. It MUST NOT come from user input (Shiny inputs, REST API
parameters, configuration files of unknown provenance).
Acceptable sources:
A function exported from a controlled companion package installed via
pak::pkg_install("private/repo").
A function defined in your application's source code, version-controlled.
Unacceptable sources:
parse(text = input$user_code)
A function loaded from an unverified URL
A function deserialized from an untrusted RDS file
When in doubt, do not pass inject_assets.
template_path is compiled by the Typst binary. A custom
template can read and write arbitrary paths during compilation – treat
it with the same trust contract as source().
Default since 0.16.0: restrict_template = TRUE – any
non-NULL template_path is rejected at validation time. Power users
supplying a trusted in-process template MUST opt-in with
restrict_template = FALSE. The default-safe posture eliminates the
silent privilege-escalation vector that would otherwise exist if a
configuration pipeline forwarded user-controlled input to
template_path. See ADR-003 for the warning-blind-clinical-reader
risk model that drove this default.
Never forward user-supplied input (Shiny inputs,
query parameters, untrusted uploads) to either parameter – doing so
creates a privilege-escalation vector. If your application surface
needs to expose template customization to end users, validate against
a fixed allow-list of approved templates and callbacks before invoking
bfh_export_pdf().
A runtime heuristic warns when inject_assets originates from
.GlobalEnv or a direct child environment (typical of
accidental Shiny exposure). Suppress with
options(BFHcharts.allow_globalenv_inject = TRUE) in development.
The same trust requirement applies to inject_assets when passed
to bfh_create_export_session().
Recommended: companion package for proprietary branding.
Organizations deploying BFHcharts-based applications that need consistent
proprietary branding (custom fonts, hospital logos) should distribute those
assets via a private companion R package. The companion package exposes a
single function compatible with inject_assets, e.g.
inject_bfh_assets(template_dir). Consumer applications then call
bfh_export_pdf(..., inject_assets = MyAssetsPkg::inject_bfh_assets).
This keeps proprietary assets out of public BFHcharts distribution while
preserving full branding in production. The companion package is still
subject to the trusted-code-only contract above. For the BFH/Region
Hovedstaden reference deployment, the BFHchartsAssets private
companion package implements this pattern. See
vignette("organizational-deployments") (when available) or the
BFHchartsAssets repository documentation.
bfh_qic() to create SPC charts
bfh_export_png() to export as PNG
bfh_create_export_session() for batch workflows (same trust
requirement applies to its inject_assets parameter)
## Not run: library(BFHcharts) # Create sample data data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), infections = rpois(24, lambda = 15) ) # Create and export chart to PDF in one pipeline bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Hospital-Acquired Infections" ) |> bfh_export_pdf( "infections_report.pdf", metadata = list( hospital = "BFH", department = "Kvalitetsafdeling", analysis = "Signifikant fald observeret efter intervention", data_definition = "Antal hospital-erhvervede infektioner per maaned" ) ) # Multiple exports from same chart result <- bfh_qic(data, month, infections, chart_type = "i", chart_title = "Infections" ) # PNG for email/presentation bfh_export_png(result, "infections.png") # PDF for official report bfh_export_pdf(result, "infections_report.pdf", metadata = list(department = "ICU") ) # Batch export: reuse template assets across multiple exports departments <- c("ICU", "Medicine", "Surgery") session <- bfh_create_export_session() on.exit(close(session)) for (dept in departments) { bfh_export_pdf(result, paste0(dept, "_report.pdf"), metadata = list(department = dept), batch_session = session ) } ## End(Not run)## Not run: library(BFHcharts) # Create sample data data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), infections = rpois(24, lambda = 15) ) # Create and export chart to PDF in one pipeline bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Hospital-Acquired Infections" ) |> bfh_export_pdf( "infections_report.pdf", metadata = list( hospital = "BFH", department = "Kvalitetsafdeling", analysis = "Signifikant fald observeret efter intervention", data_definition = "Antal hospital-erhvervede infektioner per maaned" ) ) # Multiple exports from same chart result <- bfh_qic(data, month, infections, chart_type = "i", chart_title = "Infections" ) # PNG for email/presentation bfh_export_png(result, "infections.png") # PDF for official report bfh_export_pdf(result, "infections_report.pdf", metadata = list(department = "ICU") ) # Batch export: reuse template assets across multiple exports departments <- c("ICU", "Medicine", "Surgery") session <- bfh_create_export_session() on.exit(close(session)) for (dept in departments) { bfh_export_pdf(result, paste0(dept, "_report.pdf"), metadata = list(department = dept), batch_session = session ) } ## End(Not run)
Exports an SPC chart created by bfh_qic() to a PNG file with
configurable dimensions and resolution. Designed for pipe-compatible
workflows.
bfh_export_png(x, output, width_mm = 200, height_mm = 120, dpi = 300)bfh_export_png(x, output, width_mm = 200, height_mm = 120, dpi = 300)
x |
A |
output |
Character string specifying the output file path (e.g., "chart.png") |
width_mm |
Numeric. Width of the output image in millimeters (default: 200mm) |
height_mm |
Numeric. Height of the output image in millimeters (default: 120mm) |
dpi |
Numeric. Dots per inch resolution for the PNG (default: 300) |
Dimension Handling:
Dimensions are specified in millimeters (Danish/European standard)
Internally converted to inches for ggplot2::ggsave()
Default 200mm x 120mm ~= 7.87" x 4.72" (common presentation size)
Resolution (DPI):
300 DPI (default): High quality for print and presentations
150 DPI: Medium quality, smaller file size
96 DPI: Screen resolution, minimal file size
Title Handling:
The chart title (if present) is rendered in the PNG image
This differs from PDF export which strips the title for Typst template
Plot Optimization:
Plot already has 5mm margins from bfh_qic() via apply_spc_theme()
Blank axis titles are automatically removed when plot is created
User-defined axis titles are preserved
No additional processing needed at export time
Pipe Compatibility:
Returns input object invisibly for chaining
Example: bfh_qic(...) |> bfh_export_png("chart.png")
The input object x invisibly, enabling pipe chaining
bfh_qic() to create SPC charts
bfh_export_pdf() to export as PDF with Typst templates
## Not run: library(BFHcharts) # Create sample data data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), infections = rpois(24, lambda = 15) ) # Create and export chart in one pipeline bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Monthly Infections" ) |> bfh_export_png("monthly_infections.png", width_mm = 250, height_mm = 150, dpi = 300) # Explicit dimensions for specific use cases result <- bfh_qic(data, month, infections, chart_type = "i") # A4 width (210mm) for reports bfh_export_png(result, "report_chart.png", width_mm = 210, height_mm = 140) # PowerPoint slide (widescreen 16:9) bfh_export_png(result, "slide_chart.png", width_mm = 254, height_mm = 143) # Web display (lower DPI for smaller file size) bfh_export_png(result, "web_chart.png", width_mm = 200, height_mm = 120, dpi = 96) ## End(Not run)## Not run: library(BFHcharts) # Create sample data data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), infections = rpois(24, lambda = 15) ) # Create and export chart in one pipeline bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Monthly Infections" ) |> bfh_export_png("monthly_infections.png", width_mm = 250, height_mm = 150, dpi = 300) # Explicit dimensions for specific use cases result <- bfh_qic(data, month, infections, chart_type = "i") # A4 width (210mm) for reports bfh_export_png(result, "report_chart.png", width_mm = 210, height_mm = 140) # PowerPoint slide (widescreen 16:9) bfh_export_png(result, "slide_chart.png", width_mm = 254, height_mm = 143) # Web display (lower DPI for smaller file size) bfh_export_png(result, "web_chart.png", width_mm = 200, height_mm = 120, dpi = 96) ## End(Not run)
S3 generic that extracts statistical process control metrics. The extraction logic depends on the input type:
bfh_extract_spc_stats(x) ## Default S3 method: bfh_extract_spc_stats(x) ## S3 method for class 'data.frame' bfh_extract_spc_stats(x) ## S3 method for class 'bfh_qic_result' bfh_extract_spc_stats(x)bfh_extract_spc_stats(x) ## Default S3 method: bfh_extract_spc_stats(x) ## S3 method for class 'data.frame' bfh_extract_spc_stats(x) ## S3 method for class 'bfh_qic_result' bfh_extract_spc_stats(x)
x |
Either a data frame (typically |
data.frame (typically bfh_qic_result$summary): Returns runs and
crossings from the summary. outliers_actual and outliers_recent_count
remain NULL because outlier counts require access to qic_data.
bfh_qic_result: Returns runs, crossings, and outlier counts. Outliers are
split into two fields so that the PDF table and the analysis text can be
driven from consistent - but distinct - numbers.
NULL: Returns an empty stats list (backward compatible).
Downstream packages should prefer the bfh_qic_result method so the PDF
export and any on-screen preview agree on the outlier count.
Stats are computed on x-sorted observations; input row order is not
significant. When qic_data contains an x column, rows are sorted
ascending by x before the recency-window slice for
outliers_recent_count is applied. This ensures that reversed or
scrambled input yields identical results to chronologically ordered input.
Named list with SPC statistics:
Expected maximum run length (laengste_loeb_max)
Actual longest run length (laengste_loeb)
Expected minimum crossings (antal_kryds_min)
Actual number of crossings (antal_kryds)
Expected number of outliers (0 for non-run charts,
NULL otherwise)
Total number of points outside control limits in the
latest part (used by the PDF table). NULL for data.frame input, run
charts, or when sigma.signal is unavailable.
Number of outliers within the last 6
observations of the latest part (used by the analysis text, so stale
outliers are not discussed as if they were current). Present only for
bfh_qic_result input on non-run charts.
Logical indicating run chart. Present only for
bfh_qic_result input.
Logical. TRUE when the caller passed a
non-NULL cl argument to bfh_qic(); Anhoej run/crossing signals
in this case were computed against the user-supplied centerline,
not the data-estimated process mean. Mirrors
attr(result$summary, "cl_user_supplied") and is present in both
the bfh_qic_result and data.frame (summary) dispatch paths.
Always FALSE for NULL input or summaries without the
attribute.
bfh_qic() for creating SPC charts
Other utility-functions:
bfh_create_typst_document(),
bfh_generate_details(),
bfh_merge_metadata()
## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") # Full stats (recommended - populates outliers_actual for the table) stats <- bfh_extract_spc_stats(result) # Backward-compatible summary-only dispatch stats_summary_only <- bfh_extract_spc_stats(result$summary) ## End(Not run)## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") # Full stats (recommended - populates outliers_actual for the table) stats <- bfh_extract_spc_stats(result) # Backward-compatible summary-only dispatch stats_summary_only <- bfh_extract_spc_stats(result$summary) ## End(Not run)
Generates analysis text for PDF export using AI (BFHllm) if available, with automatic fallback to Danish standard texts.
bfh_generate_analysis( x, metadata = list(), use_ai = FALSE, data_consent = NULL, use_rag = FALSE, min_chars = 300, max_chars = 375, target_tolerance = 0.05, language = "da", texts_loader = NULL )bfh_generate_analysis( x, metadata = list(), use_ai = FALSE, data_consent = NULL, use_rag = FALSE, min_chars = 300, max_chars = 375, target_tolerance = 0.05, language = "da", texts_loader = NULL )
x |
A |
metadata |
Optional list with additional context for AI:
|
use_ai |
Logical. Should AI be used for analysis generation?
Security note: Default is
|
data_consent |
Character. Required when GDPR/HIPAA context: |
use_rag |
Logical. Controls whether Privacy implication: When |
min_chars |
Minimum characters in AI-generated output. Default 300. |
max_chars |
Maximum characters in AI-generated output. Default 375. |
target_tolerance |
Deprecated. Argument is preserved in the signature
for backward compatibility but is no longer used. The |
language |
Character string specifying output language. One of |
texts_loader |
Function that returns SPC analysis text templates.
Defaults to |
Security policy: AI analysis is opt-in only (use_ai = FALSE by
default). Setting use_ai = TRUE requires BFHllm to be installed and
data_consent = "explicit" to be supplied; an informative error is raised
if either condition is not met. This prevents accidental data exposure in
environments where BFHllm uses network calls, RAG, or third-party services.
Installing BFHllm: BFHllm is not on CRAN and must be installed
manually from GitHub before using use_ai = TRUE:
remotes::install_github("johanreventlow/BFHllm")
When use_ai = TRUE and all preconditions are met, the function:
Validates data_consent = "explicit"
Emits a structured audit event via .emit_audit_event()
Builds context from the bfh_qic_result and metadata
Calls BFHllm::bfhllm_spc_suggestion() for AI-generated analysis
Falls back to standard texts if AI call fails
When use_ai = FALSE (default):
Returns Danish standard texts based on Anhoej SPC rules
data_consent is not checked
Character string with analysis text suitable for PDF export.
When use_ai = TRUE and BFHllm is installed, a structured audit event is
emitted via .emit_audit_event() before calling
BFHllm::bfhllm_spc_suggestion(). The event includes: timestamp, event
type ("ai_egress"), package, target function, fields transmitted,
use_rag value, hostname, and user.
If options(BFHcharts.audit_log = "/path/to/audit.jsonl") is set, the
event is appended as a JSON line. Otherwise it is emitted via message()
with prefix [BFHcharts/audit].
Rationale: Hospital deployments need an audit trail when patient-context
SPC data is sent to an external LLM. The structured event is parseable and
cannot be globally suppressed unlike message().
## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") # Use standard texts (no AI) analysis <- bfh_generate_analysis(result, use_ai = FALSE) # Use AI with explicit data consent analysis <- bfh_generate_analysis(result, metadata = list( data_definition = "Antal infektioner pr. 1000 patientdage", target = 2.5 ), use_ai = TRUE, data_consent = "explicit" ) # Use AI with RAG enabled (vector-store context) analysis <- bfh_generate_analysis(result, use_ai = TRUE, data_consent = "explicit", use_rag = TRUE ) ## End(Not run)## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") # Use standard texts (no AI) analysis <- bfh_generate_analysis(result, use_ai = FALSE) # Use AI with explicit data consent analysis <- bfh_generate_analysis(result, metadata = list( data_definition = "Antal infektioner pr. 1000 patientdage", target = 2.5 ), use_ai = TRUE, data_consent = "explicit" ) # Use AI with RAG enabled (vector-store context) analysis <- bfh_generate_analysis(result, use_ai = TRUE, data_consent = "explicit", use_rag = TRUE ) ## End(Not run)
Automatically generates a details string based on chart data, including period range, averages, latest values, and current level (centerline).
bfh_generate_details(x, language = "da", x_labels = NULL)bfh_generate_details(x, language = "da", x_labels = NULL)
x |
A |
language |
Character string specifying output language. One of |
x_labels |
Optional character vector of original categorical x-axis
labels (month names, weekdays, etc.). When supplied, the period range
uses the first and last labels (e.g. "januar \u2013 december") rather than
the numeric sequence stored in |
Format:
Period range with Danish date formatting
Average values per interval (numerator/denominator for p/u-charts)
Latest period values
Current level (centerline value) with appropriate unit formatting
Chart Type Handling:
p-chart, u-chart: Shows numerator/denominator (e.g., "58938/97266")
Other chart types: Shows only the value (e.g., "127")
Interval Detection:
Uses detect_date_interval() to determine the interval type
Labels adapt: "maaned", "uge", "dag", "kvartal", "aar"
Fail-early contract:
If qic_data$x contains no finite/non-NA values (empty, all-NA, or
all-Inf for numeric), the function stops with a bfhcharts_config_error.
Calls with valid data are unaffected.
Character string with formatted details, e.g.: "Periode: feb. 2019 - mar. 2022 * Gns. maaned: 58938/97266 * Seneste maaned: 60756/88509 * Nuvaerende niveau: 64,5%"
bfh_export_pdf() for PDF export functionality
Other utility-functions:
bfh_create_typst_document(),
bfh_extract_spc_stats(),
bfh_merge_metadata()
## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") details <- bfh_generate_details(result) # "Periode: jan. 2024 - dec. 2024 * Gns. maaned: 50 * ..." ## End(Not run)## Not run: result <- bfh_qic(data, x = date, y = value, chart_type = "i") details <- bfh_generate_details(result) # "Periode: jan. 2024 - dec. 2024 * Gns. maaned: 50 * ..." ## End(Not run)
Helper function to extract the ggplot object for further customization.
Users can use result$plot directly or call this function.
bfh_get_plot(x)bfh_get_plot(x)
x |
A |
ggplot2 object
Default cap on number of categorical x-axis labels rendered horizontally
without rotation. Drives bfh_subsample_label_indices() when caller does
not override max_visible. Threshold chosen to fit standard A4 PDF export
width (200mm @ 300dpi) with horizontal Roboto Medium labels.
BFH_MAX_X_LABELS_TEXTBFH_MAX_X_LABELS_TEXT
Merges user-provided metadata with package defaults for PDF generation. This function is useful for downstream packages that need consistent metadata handling without depending on BFHcharts internal functions.
bfh_merge_metadata(metadata, chart_title)bfh_merge_metadata(metadata, chart_title)
metadata |
Named list with user-provided metadata fields. Valid fields: hospital, department, title, analysis, details, author, date, data_definition, footer_content, logo_path. Other fields are ignored. |
chart_title |
Character string with chart title. Used as default for metadata$title if not provided by user. |
Named list with merged metadata containing:
Hospital name (default: "Bispebjerg og Frederiksberg Hospital")
Department name (default: NULL)
Chart title (from chart_title or metadata)
Analysis description (default: NULL)
Additional details (default: NULL)
Author name (default: NULL)
Report date (default: Sys.Date())
Data definition (default: NULL)
Footer content below chart (default: NULL)
Path to hospital logo image (default: NULL).
When NULL, the Typst template renders without a foreground logo.
Companion packages (BFHchartsAssets) populate this via inject_assets
callback or auto-detection in compose_typst_document().
User-provided values override defaults. Fields not in the default list are silently ignored.
bfh_export_pdf() for PDF export functionality
Other utility-functions:
bfh_create_typst_document(),
bfh_extract_spc_stats(),
bfh_generate_details()
## Not run: # Basic usage metadata <- list( department = "Kvalitetsafdeling", analysis = "Signifikant fald observeret" ) merged <- bfh_merge_metadata(metadata, chart_title = "Infektioner") # merged$hospital = "Bispebjerg og Frederiksberg Hospital" (default) # merged$department = "Kvalitetsafdeling" (user override) # merged$title = "Infektioner" (from chart_title) ## End(Not run)## Not run: # Basic usage metadata <- list( department = "Kvalitetsafdeling", analysis = "Signifikant fald observeret" ) merged <- bfh_merge_metadata(metadata, chart_title = "Infektioner") # merged$hospital = "Bispebjerg og Frederiksberg Hospital" (default) # merged$department = "Kvalitetsafdeling" (user override) # merged$title = "Infektioner" (from chart_title) ## End(Not run)
One-function approach to create publication-ready SPC charts. Wraps qicharts2 calculation and BFH visualization in a single call.
Convenience function that combines qicharts2::qic() calculation with BFH-styled visualization and automatic label placement. Handles the entire workflow from raw data to finished plot with intelligent labels.
bfh_qic( data, x, y, n = NULL, chart_type = "run", y_axis_unit = "count", chart_title = NULL, target_value = NULL, target_text = NULL, notes = NULL, part = NULL, freeze = NULL, exclude = NULL, cl = NULL, ylim = NULL, multiply = 1, agg.fun = c("mean", "median", "sum", "sd"), base_size = 14, width = NULL, height = NULL, units = NULL, dpi = 96, plot_margin = NULL, ylab = "", xlab = "", subtitle = NULL, caption = NULL, return.data = FALSE, language = "da" )bfh_qic( data, x, y, n = NULL, chart_type = "run", y_axis_unit = "count", chart_title = NULL, target_value = NULL, target_text = NULL, notes = NULL, part = NULL, freeze = NULL, exclude = NULL, cl = NULL, ylim = NULL, multiply = 1, agg.fun = c("mean", "median", "sum", "sd"), base_size = 14, width = NULL, height = NULL, units = NULL, dpi = 96, plot_margin = NULL, ylab = "", xlab = "", subtitle = NULL, caption = NULL, return.data = FALSE, language = "da" )
data |
Data frame with measurements |
x |
Name of x-axis column (unquoted, NSE). Usually date/time column. |
y |
Name of y-axis column (unquoted, NSE). The measurement variable. |
n |
Name of denominator column for ratio charts (optional, unquoted, NSE) |
chart_type |
Chart type: "run", "i", "mr", "p", "pp", "u", "up", "c", "g", "xbar", "s", "t" |
y_axis_unit |
Unit type: "count", "percent", "rate", or "time" |
chart_title |
Plot title (optional) |
target_value |
Numeric target value (optional) |
target_text |
Target label text (optional) |
notes |
Character vector of annotations for data points (optional, same length as data) |
part |
Positions for phase splits (optional numeric vector). Must be
positive integers, strictly increasing, unique, and within
|
freeze |
Position to freeze baseline (optional single integer). Must
be a positive integer in |
exclude |
Integer vector of data point positions to exclude from
calculations (optional). Must be positive integers, unique, and within
|
cl |
Numeric value to set a custom centerline instead of calculating
from data (optional). When non-NULL, Anhoej run/crossing signals are
computed against the user-supplied centerline rather than the
data-estimated process mean – an R warning is emitted, and the returned
|
ylim |
Y-axis display limits as a numeric vector of length 2,
|
multiply |
Numeric multiplier for y-axis values, e.g. 100 to convert proportions to percentages (default: 1) |
agg.fun |
Aggregation function for run/I charts with multiple observations per subgroup: "mean" (default), "median", "sum", "sd" |
base_size |
Base font size in points (default: auto-calculated from width/height if provided, otherwise 14) |
width |
Plot width (optional). Supports smart unit detection or explicit units parameter. See Details. |
height |
Plot height (optional). Supports smart unit detection or explicit units parameter. See Details. |
units |
Unit type for width/height: "cm" (centimeters), "mm" (millimeters), "in" (inches), "px" (pixels), or NULL for smart auto-detection (default) |
dpi |
Dots per inch for pixel conversion (default: 96). Only used when units = "px" |
plot_margin |
Plot margins as either: (1) numeric vector c(top, right, bottom, left) in mm, or (2) ggplot2::margin() object. Default NULL uses BFHtheme defaults. |
ylab |
Y-axis label (default: "" for blank) |
xlab |
X-axis label (default: "" for blank) |
subtitle |
Plot subtitle text (default: NULL for no subtitle) |
caption |
Plot caption text (default: NULL for no caption) |
return.data |
Logical. If TRUE, return the raw qic data frame instead of bfh_qic_result object. If FALSE (default), return bfh_qic_result S3 object. Legacy parameter maintained for backwards compatibility. |
language |
Character string specifying output language. One of
Default |
Helper map (internal orchestration functions):
validate_bfh_qic_inputs() – all input validation (type, bounds, NSE, denominator, target)
build_qic_args() – constructs argument list for qicharts2::qic()
invoke_qicharts2() – calls do.call(qicharts2::qic, ...) + add_anhoej_signal()
compute_viewport_base_size() – unit conversion, responsive base_size, label normalisation
render_bfh_plot() – plot_config + viewport + bfh_spc_plot() with warning suppression
apply_spc_labels_to_export() – label_size computation + add_spc_labels() with warning suppression
build_bfh_qic_return() – return value routing (S3 vs. legacy paths)
SPC Statistics Provenance:
Every field in result$summary is derived from result$qic_data via
format_qic_summary(). The aggregation function and scope for each field:
| Field | Source column | Aggregation | Scope | Chart types |
laengste_loeb |
longest.run |
max() per phase |
per-phase | all |
laengste_loeb_max |
longest.run.max |
max() global |
global | all |
antal_kryds |
n.crossings |
max() per phase |
per-phase | all |
antal_kryds_min |
n.crossings.min |
max() global |
global | all |
anhoej_signal |
runs.signal |
any() per phase |
per-phase | all |
runs_signal |
(derived) | laengste_loeb > laengste_loeb_max per phase |
per-phase | all |
crossings_signal |
(derived) | antal_kryds < antal_kryds_min per phase |
per-phase | all |
sigma_signal |
sigma.signal |
any() per phase |
per-phase | non-run charts |
centerlinje |
cl |
first row per phase | per-phase | all |
nedre_kontrolgraense |
lcl |
first row per phase | per-phase | non-run (constant limits) |
oevre_kontrolgraense |
ucl |
first row per phase | per-phase | non-run (constant limits) |
nedre_kontrolgraense_min/max |
lcl |
min()/max() per phase |
per-phase | non-run (variable limits) |
oevre_kontrolgraense_min/max |
ucl |
min()/max() per phase |
per-phase | non-run (variable limits) |
kontrolgraenser_konstante |
lcl+ucl |
all-equal check per phase | per-phase | non-run charts |
antal_observationer |
n.obs |
first row per phase | per-phase | all |
anvendelige_observationer |
n.useful |
first row per phase | per-phase | all |
antal_outliers |
sigma.signal |
sum() per phase |
per-phase | non-run charts |
forventede_outliers |
(constant 0) | literal 0 | per-phase | non-run charts |
Notes:
longest.run is stored by qicharts2 as a per-phase constant (all rows within
a phase hold the same value). max() over those rows is equivalent to the value.
sigma.signal is per-row (varies within a phase). any() aggregation is correct.
Limit columns use scalar form when limits are constant within all phases (e.g. I-chart, C-chart). Variable-denominator charts (P, U) use min/max column pairs.
Run charts (chart_type = "run") produce no sigma_signal, lcl/ucl, or
outlier columns in result$summary.
Chart Types:
run: Run chart (no control limits)
i: I-chart (individuals)
mr: Moving Range chart – measures point-to-point variability. Typically paired with an I-chart to characterise both process level (I) and short-term variation (MR).
p: P-chart (proportions, requires n)
pp: P-prime chart (Laney-adjusted proportions) – use instead of p
when denominators are very large (n > 1000 per subgroup) and standard
control limits become artificially tight due to over-dispersion. Requires n.
u: U-chart (rates, requires n)
up: U-prime chart (Laney-adjusted rates) – same rationale as pp,
applied to count rates. Requires n.
c: C-chart (counts)
g: G-chart (geometric)
xbar: X-bar chart
s: S-chart
t: T-chart (time between events)
Y-Axis Units:
count: Integer counts with K/M notation
percent: Percentage values (0-100%)
rate: Decimal values with comma notation
time: Context-aware minutes/hours/days
Phase Configuration:
part: Vector of positions where phase splits occur (e.g., c(12, 24))
freeze: Position to freeze baseline calculation
Denominator Contract (ratio charts):
Ratio chart types (p, pp, u, up) require a denominator column
supplied via n. The content of n is validated to prevent silently
misleading rate plots:
n must be numeric and finite (no Inf/-Inf).
All non-NA values of n must be > 0 (zero/negative denominators
produce meaningless rates).
For proportion charts (p, pp): every row with both y and n
present must satisfy y <= n (proportion <= 1).
NA in individual rows of n is allowed (qicharts2 drops them).
Violations raise an error identifying the offending row number(s) so
the source data can be inspected. Pre-filter or correct invalid rows
before calling bfh_qic().
Other chart types (run, i, mr, c, g, t, xbar, s) are not
subject to denominator validation.
Unit Support (Danish-friendly): Width and height support multiple units for convenience:
Smart auto-detection (default, units = NULL):
Values > 100 -> pixels (e.g., width = 800 -> 800px)
Values 10-100 -> centimeters (e.g., width = 25 -> 25cm)
Values < 10 -> inches (e.g., width = 10 -> 10in, legacy)
Explicit units (units = "cm", "mm", "in", "px"):
Centimeters: width = 25, height = 15, units = "cm" (Danish standard)
Millimeters: width = 250, height = 150, units = "mm"
Inches: width = 10, height = 6, units = "in" (legacy)
Pixels: width = 800, height = 600, units = "px", dpi = 96 (web/Shiny)
Responsive Typography:
When width and height are provided, base_size is automatically
calculated using geometric mean: sqrt(width x height) / 3.5
This ensures fonts scale proportionally with plot size.
Override by explicitly setting base_size.
Automatic Label Placement: Labels are automatically added to the plot showing:
Current level (CL) from the most recent phase
Target value (if specified via target_value or target_text)
Intelligent collision avoidance with multi-level fallback strategy
Provide width and height for optimal label sizing and placement
Arrow Symbol Suppression:
If target_text contains arrow symbols (up down or < >), the target line will be
suppressed and only the directional indicator shown at the plot edge.
Percent Target Contract:
When y_axis_unit = "percent", target_value is validated against the scale
implied by multiply:
multiply = 1 (default): target_value must be in [0, 1.5] (proportion)
multiply = 100: target_value must be in [0, 150] (percent)
multiply = m: target_value must be in [0, m * 1.5]
The most common error is passing target_value = 2.0 to mean "2%" when
multiply = 1 (proportion scale). Use target_value = 0.02 instead, or
set multiply = 100 to pass percent values directly.
A 1.5x upper slack permits legitimate stretch targets above 100%.
Default (return.data = FALSE): bfh_qic_result S3 object with components:
$plot: ggplot2 object with the SPC chart
$summary: data.frame with SPC statistics
$qic_data: data.frame with raw qicharts2 calculations
$config: list with original function parameters
return.data = TRUE: data.frame with qic calculations (legacy behavior)
# Minimal runnable example with deterministic data df <- data.frame( maaned = seq.Date(as.Date("2023-01-01"), by = "month", length.out = 12), vaerdi = c(10, 12, 11, 13, 10, 14, 11, 12, 10, 13, 11, 12) ) result <- bfh_qic(df, x = maaned, y = vaerdi, chart_type = "i") class(result) # "bfh_qic_result" ## Not run: library(BFHcharts) # Example 1: Simple run chart with monthly data data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), infections = rpois(24, lambda = 15), surgeries = rpois(24, lambda = 100) ) plot <- bfh_qic( data = data, x = month, y = infections, chart_type = "run", y_axis_unit = "count", chart_title = "Monthly Hospital-Acquired Infections" ) plot # Example 2: P-chart with target line plot <- bfh_qic( data = data, x = month, y = infections, n = surgeries, chart_type = "p", y_axis_unit = "percent", chart_title = "Infection Rate per 100 Surgeries", target_value = 0.02, target_text = "down Maalet: 2%" ) plot # Example 3: I-chart with phase splits plot <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections with Intervention", part = c(12), # Phase split after 12 months freeze = 12 # Freeze baseline at month 12 ) plot # Example 4: Chart with annotations using notes notes_vec <- rep(NA, 24) notes_vec[3] <- "Start of intervention" notes_vec[12] <- "New protocol implemented" notes_vec[18] <- "Staff training completed" plot <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections with Annotated Events", notes = notes_vec ) plot # Example 5: Responsive typography with viewport dimensions # Small plot (6x4 inches) -> base_size ~= 14pt plot_small <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Small Plot - Auto Scaled Typography", width = 6, height = 4 # Auto: base_size ~= 14pt ) # Medium plot (10x6 inches) -> base_size ~= 22pt plot_medium <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Medium Plot - Auto Scaled Typography", width = 10, height = 6 # Auto: base_size ~= 22pt ) # Large plot (16x9 inches) -> base_size ~= 34pt plot_large <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Large Plot - Auto Scaled Typography", width = 16, height = 9 # Auto: base_size ~= 34pt ) # Override auto-scaling with explicit base_size plot_custom <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Custom Typography Override", width = 10, height = 6, base_size = 18 # Explicit override ) # Example 6: Exclude outliers from calculations plot_exclude <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "I-Chart with Excluded Outliers", exclude = c(3, 15) # Exclude data points 3 and 15 ) # Example 7: Use median instead of mean for aggregation plot_median <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "I-Chart Using Median", agg.fun = "median" ) # Example 8: Multiply y-values for unit conversion # Convert proportions (0-1) to percentages (0-100) data_prop <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), proportion = runif(24, 0.01, 0.05) # Proportions 0.01-0.05 ) plot_multiply <- bfh_qic( data = data_prop, x = month, y = proportion, chart_type = "i", y_axis_unit = "percent", chart_title = "Proportions Converted to Percentages", multiply = 100 # Convert 0.01 -> 1% ) # Example 9: Custom centerline (cl parameter) # Use a fixed benchmark or standard instead of calculating from data plot_cl <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections with Custom Centerline", cl = 10 # Set centerline to fixed benchmark of 10 ) # Example 10: Custom plot margins (numeric vector in mm) plot_tight <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Chart with Tight Margins", plot_margin = c(2, 2, 2, 2) # 2mm on all sides ) # Example 11: Custom margins with margin() object plot_custom_margin <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Chart with Custom Margins", plot_margin = ggplot2::margin(t = 5, r = 15, b = 5, l = 10, unit = "mm") ) # Example 12: Responsive margins using lines (scales with base_size) plot_responsive <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", base_size = 18, plot_margin = ggplot2::margin(t = 0.5, r = 1, b = 0.5, l = 1, unit = "lines") ) # Example 13: Custom axis labels, subtitle, and caption plot_labels <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Hospital-Acquired Infections", ylab = "Antal infektioner", xlab = "Maaned", subtitle = "Kirurgisk afdeling - 2024", caption = "Data: EPJ system | Analyse: Kvalitetsafdelingen" ) # Example 14: Add BFHtheme branding (hospital logo, custom styling) plot_branded <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Hospital-Acquired Infections - Official Report", base_size = 14 ) |> BFHtheme::add_bfh_logo() # Add hospital branding # Alternate BFHtheme styles available: # - BFHtheme::theme_bfh_dark() for dark theme # - BFHtheme::theme_bfh_print() for print-optimized theme # - BFHtheme::theme_bfh_presentation() for presentations # Example 15: Danish-friendly unit support (centimeters) plot_cm <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Plot in Centimeters (Danish Standard)", width = 25, # 25 cm (auto-detected as cm) height = 15 # 15 cm ) # Example 16: Explicit unit specification plot_explicit <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Explicit Centimeters", width = 25, height = 15, units = "cm" ) # Example 17: Pixel dimensions for web/Shiny plot_px <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Plot for Web Display", width = 800, # 800 px (auto-detected as px) height = 600, # 600 px dpi = 96 ) # Example 18: Backward compatibility (inches still work) plot_inches <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Legacy Inches Format", width = 10, # 10 inches (auto-detected as in) height = 6 # 6 inches ) # Example 19: Get raw qic data for further analysis qic_data <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", return.data = TRUE # Return data.frame instead of plot ) # Now you can access all qic calculations head(qic_data) # Available columns: cl, ucl, lcl, runs.signal, sigma.signal, etc. # Example 20: Get summary statistics with Danish column names result <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections - With Summary" ) # Access the plot result$plot # Access the summary statistics (Danish column names) via S3 object print(result$summary) # Columns: fase, antal_observationer, anvendelige_observationer, # centerlinje, nedre_kontrolgraense, oevre_kontrolgraense, # laengste_loeb, antal_kryds, anhoej_signal, # runs_signal, crossings_signal, sigma_signal # Example 21: Get both raw qic data and summary via S3 object result <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", part = c(12) # Split into phases ) # Access raw qic data (all qicharts2 calculations) result$qic_data # Access summary statistics (one row per phase) result$summary # fase 1: baseline period # fase 2: intervention period # Example 22: Use summary for reporting result <- bfh_qic( data = data, x = month, y = infections, n = surgeries, chart_type = "p", y_axis_unit = "percent", chart_title = "Infection Rate - Multi-phase Analysis", part = c(12) ) # Extract key metrics for reporting via result$summary summary_stats <- result$summary cat("Fase 1 centerlinje:", summary_stats$centerlinje[1], "%\n") cat("Fase 2 centerlinje:", summary_stats$centerlinje[2], "%\n") cat("Forbedring:", summary_stats$centerlinje[1] - summary_stats$centerlinje[2], "%-point\n") if (summary_stats$sigma_signal[2]) { cat("VIGTIG: Special cause variation detekteret i fase 2!\n") } # Example 23: P-prime chart for large denominators (Laney-adjusted) data_large_n <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), events = rpois(24, lambda = 50), population = rpois(24, lambda = 5000) # Very large denominators ) plot_pp <- bfh_qic( data = data_large_n, x = month, y = events, n = population, chart_type = "pp", # Laney-adjusted: prevents artificially tight limits y_axis_unit = "percent", chart_title = "Event Rate (Laney P-prime)" ) # Example 24: Moving Range chart paired with I-chart # Use MR-chart alongside I-chart for full process variation characterisation plot_i <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections - Process Level (I-chart)" ) plot_mr <- bfh_qic( data = data, x = month, y = infections, chart_type = "mr", # Moving Range: short-term variation y_axis_unit = "count", chart_title = "Infections - Short-term Variation (MR-chart)" ) ## End(Not run)# Minimal runnable example with deterministic data df <- data.frame( maaned = seq.Date(as.Date("2023-01-01"), by = "month", length.out = 12), vaerdi = c(10, 12, 11, 13, 10, 14, 11, 12, 10, 13, 11, 12) ) result <- bfh_qic(df, x = maaned, y = vaerdi, chart_type = "i") class(result) # "bfh_qic_result" ## Not run: library(BFHcharts) # Example 1: Simple run chart with monthly data data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), infections = rpois(24, lambda = 15), surgeries = rpois(24, lambda = 100) ) plot <- bfh_qic( data = data, x = month, y = infections, chart_type = "run", y_axis_unit = "count", chart_title = "Monthly Hospital-Acquired Infections" ) plot # Example 2: P-chart with target line plot <- bfh_qic( data = data, x = month, y = infections, n = surgeries, chart_type = "p", y_axis_unit = "percent", chart_title = "Infection Rate per 100 Surgeries", target_value = 0.02, target_text = "down Maalet: 2%" ) plot # Example 3: I-chart with phase splits plot <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections with Intervention", part = c(12), # Phase split after 12 months freeze = 12 # Freeze baseline at month 12 ) plot # Example 4: Chart with annotations using notes notes_vec <- rep(NA, 24) notes_vec[3] <- "Start of intervention" notes_vec[12] <- "New protocol implemented" notes_vec[18] <- "Staff training completed" plot <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections with Annotated Events", notes = notes_vec ) plot # Example 5: Responsive typography with viewport dimensions # Small plot (6x4 inches) -> base_size ~= 14pt plot_small <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Small Plot - Auto Scaled Typography", width = 6, height = 4 # Auto: base_size ~= 14pt ) # Medium plot (10x6 inches) -> base_size ~= 22pt plot_medium <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Medium Plot - Auto Scaled Typography", width = 10, height = 6 # Auto: base_size ~= 22pt ) # Large plot (16x9 inches) -> base_size ~= 34pt plot_large <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Large Plot - Auto Scaled Typography", width = 16, height = 9 # Auto: base_size ~= 34pt ) # Override auto-scaling with explicit base_size plot_custom <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Custom Typography Override", width = 10, height = 6, base_size = 18 # Explicit override ) # Example 6: Exclude outliers from calculations plot_exclude <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "I-Chart with Excluded Outliers", exclude = c(3, 15) # Exclude data points 3 and 15 ) # Example 7: Use median instead of mean for aggregation plot_median <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "I-Chart Using Median", agg.fun = "median" ) # Example 8: Multiply y-values for unit conversion # Convert proportions (0-1) to percentages (0-100) data_prop <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), proportion = runif(24, 0.01, 0.05) # Proportions 0.01-0.05 ) plot_multiply <- bfh_qic( data = data_prop, x = month, y = proportion, chart_type = "i", y_axis_unit = "percent", chart_title = "Proportions Converted to Percentages", multiply = 100 # Convert 0.01 -> 1% ) # Example 9: Custom centerline (cl parameter) # Use a fixed benchmark or standard instead of calculating from data plot_cl <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections with Custom Centerline", cl = 10 # Set centerline to fixed benchmark of 10 ) # Example 10: Custom plot margins (numeric vector in mm) plot_tight <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Chart with Tight Margins", plot_margin = c(2, 2, 2, 2) # 2mm on all sides ) # Example 11: Custom margins with margin() object plot_custom_margin <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Chart with Custom Margins", plot_margin = ggplot2::margin(t = 5, r = 15, b = 5, l = 10, unit = "mm") ) # Example 12: Responsive margins using lines (scales with base_size) plot_responsive <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", base_size = 18, plot_margin = ggplot2::margin(t = 0.5, r = 1, b = 0.5, l = 1, unit = "lines") ) # Example 13: Custom axis labels, subtitle, and caption plot_labels <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Hospital-Acquired Infections", ylab = "Antal infektioner", xlab = "Maaned", subtitle = "Kirurgisk afdeling - 2024", caption = "Data: EPJ system | Analyse: Kvalitetsafdelingen" ) # Example 14: Add BFHtheme branding (hospital logo, custom styling) plot_branded <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Hospital-Acquired Infections - Official Report", base_size = 14 ) |> BFHtheme::add_bfh_logo() # Add hospital branding # Alternate BFHtheme styles available: # - BFHtheme::theme_bfh_dark() for dark theme # - BFHtheme::theme_bfh_print() for print-optimized theme # - BFHtheme::theme_bfh_presentation() for presentations # Example 15: Danish-friendly unit support (centimeters) plot_cm <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Plot in Centimeters (Danish Standard)", width = 25, # 25 cm (auto-detected as cm) height = 15 # 15 cm ) # Example 16: Explicit unit specification plot_explicit <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Explicit Centimeters", width = 25, height = 15, units = "cm" ) # Example 17: Pixel dimensions for web/Shiny plot_px <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Plot for Web Display", width = 800, # 800 px (auto-detected as px) height = 600, # 600 px dpi = 96 ) # Example 18: Backward compatibility (inches still work) plot_inches <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Legacy Inches Format", width = 10, # 10 inches (auto-detected as in) height = 6 # 6 inches ) # Example 19: Get raw qic data for further analysis qic_data <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", return.data = TRUE # Return data.frame instead of plot ) # Now you can access all qic calculations head(qic_data) # Available columns: cl, ucl, lcl, runs.signal, sigma.signal, etc. # Example 20: Get summary statistics with Danish column names result <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections - With Summary" ) # Access the plot result$plot # Access the summary statistics (Danish column names) via S3 object print(result$summary) # Columns: fase, antal_observationer, anvendelige_observationer, # centerlinje, nedre_kontrolgraense, oevre_kontrolgraense, # laengste_loeb, antal_kryds, anhoej_signal, # runs_signal, crossings_signal, sigma_signal # Example 21: Get both raw qic data and summary via S3 object result <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", part = c(12) # Split into phases ) # Access raw qic data (all qicharts2 calculations) result$qic_data # Access summary statistics (one row per phase) result$summary # fase 1: baseline period # fase 2: intervention period # Example 22: Use summary for reporting result <- bfh_qic( data = data, x = month, y = infections, n = surgeries, chart_type = "p", y_axis_unit = "percent", chart_title = "Infection Rate - Multi-phase Analysis", part = c(12) ) # Extract key metrics for reporting via result$summary summary_stats <- result$summary cat("Fase 1 centerlinje:", summary_stats$centerlinje[1], "%\n") cat("Fase 2 centerlinje:", summary_stats$centerlinje[2], "%\n") cat("Forbedring:", summary_stats$centerlinje[1] - summary_stats$centerlinje[2], "%-point\n") if (summary_stats$sigma_signal[2]) { cat("VIGTIG: Special cause variation detekteret i fase 2!\n") } # Example 23: P-prime chart for large denominators (Laney-adjusted) data_large_n <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 24), events = rpois(24, lambda = 50), population = rpois(24, lambda = 5000) # Very large denominators ) plot_pp <- bfh_qic( data = data_large_n, x = month, y = events, n = population, chart_type = "pp", # Laney-adjusted: prevents artificially tight limits y_axis_unit = "percent", chart_title = "Event Rate (Laney P-prime)" ) # Example 24: Moving Range chart paired with I-chart # Use MR-chart alongside I-chart for full process variation characterisation plot_i <- bfh_qic( data = data, x = month, y = infections, chart_type = "i", y_axis_unit = "count", chart_title = "Infections - Process Level (I-chart)" ) plot_mr <- bfh_qic( data = data, x = month, y = infections, chart_type = "mr", # Moving Range: short-term variation y_axis_unit = "count", chart_title = "Infections - Short-term Variation (MR-chart)" ) ## End(Not run)
S3 class for wrapping SPC chart outputs. Enables pipe-compatible export workflows while maintaining backwards-compatible console display.
Objects of class bfh_qic_result are returned by bfh_qic.
Access components with result$plot, result$summary,
result$qic_data, and result$config.
Resolverer i18n-noegler i analysis$conclusions, analysis$caveats
og analysis$suggested_actions til tekst via texts_loader, sammen-
saetter base + target + action + caveats inden for max_chars-budget.
bfh_render_analysis(analysis, max_chars = 375, texts_loader = NULL)bfh_render_analysis(analysis, max_chars = 375, texts_loader = NULL)
analysis |
A |
max_chars |
Maximum characters in output. Default 375. |
texts_loader |
Function that returns SPC analysis text templates.
Defaults to |
render_context-vaerdier (target_display, centerline_formatted,
operator_unicode, outliers_word_key) bruges verbatim som
placeholder-vaerdier. Renderer re-deriverer IKKE disse fra
features/aux for at undgaa silent display-drift.
Character of length 1, nchar(...) <= max_chars.
## Not run: result <- bfh_qic(data, x = month, y = value, chart_type = "i") analysis <- bfh_analyse(result, metadata = list(target = ">= 90%")) text <- bfh_render_analysis(analysis, max_chars = 375) ## End(Not run)## Not run: result <- bfh_qic(data, x = month, y = value, chart_type = "i") analysis <- bfh_analyse(result, metadata = list(target = ">= 90%")) text <- bfh_render_analysis(analysis, max_chars = 375) ## End(Not run)
Returns deterministic, evenly-spaced indices for selecting which categorical
x-axis labels to display on a chart. First index is always 1. Intermediate
indices follow integer step ceiling((n_labels - 1) / (max_visible - 1)).
The last index n_labels is included only when it lands naturally on the
step grid (i.e. when (n_labels - 1) is divisible by step); otherwise
the sequence ends at the highest step-aligned position <= n_labels.
Designed for tekst-x-data (month names, weekdays, observation IDs) where
showing all labels would overlap or require rotation.
bfh_subsample_label_indices(n_labels, max_visible = BFH_MAX_X_LABELS_TEXT)bfh_subsample_label_indices(n_labels, max_visible = BFH_MAX_X_LABELS_TEXT)
n_labels |
Integer. Total number of labels available. |
max_visible |
Integer. Maximum number of labels to display. Default BFH_MAX_X_LABELS_TEXT (currently 12). |
Algorithm scales smoothly: for n <= max_visible all indices returned;
otherwise step-based thinning preserves a constant label-density and
avoids the asymmetric-rounding gap-bug that
round(seq(..., length.out)) produces for moderate n (issue #396).
Note: prior versions (0.22.0) appended n_labels as a force-last anchor,
producing a shorter tail-gap mid-rhythm. This was removed because the
rhythm-break was visually jarring (e.g. n=24 jumped from a constant step=3
to a step=2 tail). Callers requiring the last label as anchor must append
it explicitly.
Integer vector of indices into the label sequence. First index is
always 1; last index is n_labels only when it falls on the step grid,
otherwise the highest step-aligned position <= n_labels.
Length <= max_visible.
# All 12 months visible when n <= max bfh_subsample_label_indices(12) # [1] 1 2 3 4 5 6 7 8 9 10 11 12 # 24 months: step=3, no force-last (sidste = 22, ej 24) bfh_subsample_label_indices(24) # [1] 1 4 7 10 13 16 19 22 # 100 obs: step=9, n=100 lands naturally on grid -> included bfh_subsample_label_indices(100) # [1] 1 10 19 28 37 46 55 64 73 82 91 100 # Custom max bfh_subsample_label_indices(24, max_visible = 6) # [1] 1 6 11 16 21# All 12 months visible when n <= max bfh_subsample_label_indices(12) # [1] 1 2 3 4 5 6 7 8 9 10 11 12 # 24 months: step=3, no force-last (sidste = 22, ej 24) bfh_subsample_label_indices(24) # [1] 1 4 7 10 13 16 19 22 # 100 obs: step=9, n=100 lands naturally on grid -> included bfh_subsample_label_indices(100) # [1] 1 10 19 28 37 46 55 64 73 82 91 100 # Custom max bfh_subsample_label_indices(24, max_visible = 6) # [1] 1 6 11 16 21
When TRUE, suppresses the warning emitted by .validate_inject_assets()
for functions defined in .GlobalEnv (development convenience).
BFHCHARTS_OPT_ALLOW_GLOBALENV_INJECTBFHCHARTS_OPT_ALLOW_GLOBALENV_INJECT
When set, .resolve_analysis_date() uses this Date as the analysis
anchor instead of Sys.Date(). Lower priority than per-call
metadata$analysis_date. Intended for test-suites (set in setup.R)
and audit-replay scenarios where determinism across calendar days
matters.
BFHCHARTS_OPT_ANALYSIS_DATEBFHCHARTS_OPT_ANALYSIS_DATE
When set, .emit_audit_event() appends JSON-line records to this path.
Otherwise events are emitted via message().
BFHCHARTS_OPT_AUDIT_LOGBFHCHARTS_OPT_AUDIT_LOG
When TRUE, place_two_labels_npc() emits diagnostic messages when the
niveau cascade falls back to legacy NPC-based gap calculation.
BFHCHARTS_OPT_DEBUG_LABEL_PLACEMENTBFHCHARTS_OPT_DEBUG_LABEL_PLACEMENT
When set, find_quarto() uses this path instead of running auto-detection.
The path is shell-metachar validated and existence-checked before use.
BFHCHARTS_OPT_QUARTO_PATHBFHCHARTS_OPT_QUARTO_PATH
When TRUE, bfh_export_pdf() and friends do not emit the informational
message "Auto-detected units: ..." when units are inferred from filename.
BFHCHARTS_OPT_SUPPRESS_UNIT_AUTO_DETECTBFHCHARTS_OPT_SUPPRESS_UNIT_AUTO_DETECT
Releases the session tmpdir and marks the session as closed. Subsequent
calls are no-ops. Called automatically by the session finalizer at
garbage collection or R session exit, but explicit close() is
recommended for prompt cleanup.
## S3 method for class 'bfh_export_session' close(con, ...)## S3 method for class 'bfh_export_session' close(con, ...)
con |
A |
... |
Additional arguments (ignored). |
Invisibly returns NULL.
data_consent when use_ai = TRUE
Caller must pass exactly this string to acknowledge AI-egress of clinical
data. Used by bfh_generate_analysis().
DATA_CONSENT_EXPLICITDATA_CONSENT_EXPLICIT
Returns a single-line character summary. Used by paste(), format-
contexts, and debugging-helpers.
## S3 method for class 'bfh_spc_analysis' format(x, ...)## S3 method for class 'bfh_spc_analysis' format(x, ...)
x |
A |
... |
Ignored. |
Character scalar.
Check if Object is bfh_qic_result
is_bfh_qic_result(x)is_bfh_qic_result(x)
x |
Object to test |
Logical indicating whether x is a bfh_qic_result object
## Not run: data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 12), infections = rpois(12, lambda = 15) ) result <- bfh_qic(data, x = month, y = infections, chart_type = "run") is_bfh_qic_result(result) # TRUE is_bfh_qic_result(result$plot) # FALSE ## End(Not run)## Not run: data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 12), infections = rpois(12, lambda = 15) ) result <- bfh_qic(data, x = month, y = infections, chart_type = "run") is_bfh_qic_result(result) # TRUE is_bfh_qic_result(result$plot) # FALSE ## End(Not run)
Check if Object is bfh_spc_analysis
is_bfh_spc_analysis(x)is_bfh_spc_analysis(x)
x |
Object to test. |
Logical indicating whether x inherits from
bfh_spc_analysis.
When abs(yA_npc - yB_npc) < label_height_npc * THIS_FACTOR, lines are
treated as effectively coincident; labels are placed one above and one
below the same line position.
LABEL_PLACEMENT_COINCIDENT_THRESHOLD_FACTORLABEL_PLACEMENT_COINCIDENT_THRESHOLD_FACTOR
When the initial label placement creates a line-gap collision, NIVEAU 1
incrementally shrinks the inter-label gap by these factors (50%, then 30%,
then 15% of the configured gap_labels). The first factor that resolves
the collision is used.
LABEL_PLACEMENT_GAP_REDUCTION_FACTORSLABEL_PLACEMENT_GAP_REDUCTION_FACTORS
During NIVEAU 3 (last-resort shelf placement), the non-priority label is pushed to the opposite shelf (top vs bottom of panel) based on whether the priority label center is below this NPC threshold.
LABEL_PLACEMENT_SHELF_CENTER_THRESHOLDLABEL_PLACEMENT_SHELF_CENTER_THRESHOLD
When abs(yA_npc - yB_npc) < min_center_gap * THIS_FACTOR, lines are
considered too close for both labels to share a side; pref_pos is rewritten
so one label sits above and the other below.
LABEL_PLACEMENT_TIGHT_LINES_THRESHOLD_FACTORLABEL_PLACEMENT_TIGHT_LINES_THRESHOLD_FACTOR
Set af gyldige low_confidence_reason-vaerdier ved
confidence_tier == "low". Bruges af feature-extraction
(.compute_low_confidence_reason), render-dispatch
(.render_stability -> texts$base$not_evaluable[[reason]]) og
schema-validator. NA_character_ er gyldig vaerdi naar tier != "low".
LOW_CONFIDENCE_REASONSLOW_CONFIDENCE_REASONS
Safety-cap for .compute_magnitude() baseline-delta-sigma-ratio.
Microscopic sigma (~1e-12) fra near-constant data combineret med
float-noise baseline-delta kan producere astronomical ratios and
falsk "large"-klassifikation. Ratio > cap returnerer NA i stedet
for magnitude-bucket. Default 100 svarer til 100 sigma-shifts –
empirisk usandsynligt klinisk-meaningful + klart-ufysisk for
kontrolgraense-baseret SPC.
MAGNITUDE_RATIO_CAPMAGNITUDE_RATIO_CAP
Cycle 04 H2 fix (2026-05-18). Foreloebigt safety-net; permanent loesning kraever sigma-floor med scale/unit-awareness.
The Anhoej rules and SPC literature (Anhoej & Olesen 2014) require approximately 8+ points for meaningful control limits and reliable signal detection. Below this threshold, the control limits are statistically unreliable.
MIN_BASELINE_NMIN_BASELINE_N
Below this threshold, confidence_tier collapses to "low" and the
renderer substitutes the not_evaluable-base. Default value follows
Anhoej & Olesen (2014) detection-power analysis: run-detection-power
drops sharply below n = 12.
N_MINN_MIN
Anhoej J, Olesen AV (2014). Run charts revisited: a simulation study of run chart rules for detection of non-random variation in health care processes. PLoS One. 9(11):e113825.
Constructor for the bfh_qic_result S3 class. This class wraps SPC chart outputs to enable pipe-compatible export functions while preserving backwards-compatible console behavior.
new_bfh_qic_result(plot, summary, qic_data, config)new_bfh_qic_result(plot, summary, qic_data, config)
plot |
ggplot2 object containing the SPC chart |
summary |
data.frame with SPC statistics (runs, crossings, control
limits). Plain |
qic_data |
data.frame with raw qicharts2 calculation results |
config |
list with original function parameters |
An object of class bfh_qic_result containing:
plot |
ggplot2 object with the SPC chart |
summary |
data.frame with summary statistics (Danish column names).
Returned as plain |
qic_data |
data.frame with qicharts2 calculations |
config |
list with original parameters |
new_bfh_qic_result is a stable, exported constructor. The structure of
bfh_qic_result objects has been stable since v0.10.0. Field names
($plot, $summary, $qic_data, $config) will not
be removed without a deprecation cycle.
The $qic_data field is a data.frame with the following
canonical columns (supplied by qicharts2 >= 0.7.0):
Original x-axis input values
Original y-axis input values (per-period)
Denominator counts (for proportion/rate charts)
Numeric center line
Numeric upper control limit
Numeric lower control limit
95-percent warning limits (Shewhart sigma = 2)
Logical: point outside 3-sigma control limits
Logical: Anhoej runs-rule signal
Observed and threshold run lengths
Observed and minimum crossing counts
Logical combined Anhoej-rule signal (runs OR
crossings). May be FALSE for series < 10 points where crossing
criterion is not applied.
Character labels for chart annotations
Phase/freeze/filter columns
Free-text annotation column
The qicharts2 column contract is stable across minor versions >= 0.7.0. Additional columns may be present but should not be relied upon.
## Not run: # bfh_qic() returns a bfh_qic_result via this constructor data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 12), infections = rpois(12, lambda = 15) ) result <- bfh_qic(data, x = month, y = infections, chart_type = "run") inherits(result, "bfh_qic_result") ## End(Not run)## Not run: # bfh_qic() returns a bfh_qic_result via this constructor data <- data.frame( month = seq(as.Date("2024-01-01"), by = "month", length.out = 12), infections = rpois(12, lambda = 15) ) result <- bfh_qic(data, x = month, y = infections, chart_type = "run") inherits(result, "bfh_qic_result") ## End(Not run)
Extracts and displays the ggplot object from a bfh_qic_result. Enables use of generic plot() function.
## S3 method for class 'bfh_qic_result' plot(x, ...)## S3 method for class 'bfh_qic_result' plot(x, ...)
x |
A |
... |
Additional arguments passed to print.ggplot |
The ggplot object invisibly
Displays a one-line summary of the session: open/closed status, tmpdir location, and font path when set.
## S3 method for class 'bfh_export_session' print(x, ...)## S3 method for class 'bfh_export_session' print(x, ...)
x |
A |
... |
Additional arguments (ignored). |
The session object invisibly, for pipe chaining.
Displays the plot in the console/viewer, maintaining backwards-compatible behavior with previous versions that returned ggplot objects directly.
## S3 method for class 'bfh_qic_result' print(x, ...)## S3 method for class 'bfh_qic_result' print(x, ...)
x |
A |
... |
Additional arguments (ignored) |
The object invisibly for pipe chaining
Displays a compact summary of features, conclusions, confidence, and
active caveats. Use format() for a single-line summary or
as.list() to inspect raw structure.
## S3 method for class 'bfh_spc_analysis' print(x, ...)## S3 method for class 'bfh_spc_analysis' print(x, ...)
x |
A |
... |
Ignored. |
x (invisibly).