| Title: | Risk Quantification Engine for Clinical Submission Readiness |
|---|---|
| Description: | Quantifies submission risk using a Failure Modes and Effects Analysis (FMEA)-inspired framework (probability, impact, detectability). Builds risk registers from evidence, computes Risk Priority Numbers (RPN), classifies risk levels, and emits standardized R4SUB (R for Regulatory Submission) evidence table rows via 'r4subcore'. Supports risk mitigation tracking and trend analysis across submission milestones. |
| Authors: | Pawan Rama Mali [aut, cre, cph] (ORCID: <https://orcid.org/0000-0001-7864-5819>) |
| Maintainer: | Pawan Rama Mali <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.1 |
| Built: | 2026-05-20 09:12:05 UTC |
| Source: | https://github.com/r4sub/r4subrisk |
Applies mitigation updates to a risk register. Allows updating probability, impact, detectability, status, and mitigation notes for specific risks.
apply_mitigations(risk_register, updates, config = risk_config_default())apply_mitigations(risk_register, updates, config = risk_config_default())
risk_register |
A |
updates |
A data.frame with at minimum column |
config |
A |
An updated risk_register with recomputed RPN and risk levels.
risks <- data.frame( risk_id = c("R001", "R002"), description = c("Missing vars", "Bad derivation"), probability = c(4, 3), impact = c(5, 4), detectability = c(2, 3) ) rr <- create_risk_register(risks) updates <- data.frame( risk_id = "R001", probability = 2, mitigation = "Added validation check", status = "mitigated" ) rr2 <- apply_mitigations(rr, updates) rr2risks <- data.frame( risk_id = c("R001", "R002"), description = c("Missing vars", "Bad derivation"), probability = c(4, 3), impact = c(5, 4), detectability = c(2, 3) ) rr <- create_risk_register(risks) updates <- data.frame( risk_id = "R001", probability = 2, mitigation = "Added validation check", status = "mitigated" ) rr2 <- apply_mitigations(rr, updates) rr2
Classify RPN Value into Risk Level
classify_rpn(rpn, bands = risk_config_default()$rpn_bands)classify_rpn(rpn, bands = risk_config_default()$rpn_bands)
rpn |
Numeric RPN score (1–125). |
bands |
Named list of band boundaries from |
Character risk level name.
classify_rpn(90) classify_rpn(25) classify_rpn(5)classify_rpn(90) classify_rpn(25) classify_rpn(5)
Compares two risk register snapshots and reports changes in RPN, new risks, resolved risks, and risk level transitions.
compare_risk_registers(before, after)compare_risk_registers(before, after)
before |
A |
after |
A |
A list with:
rpn_changes: tibble of risks with changed RPN
new_risks: risk_ids present in after but not before
resolved_risks: risk_ids present in before but not after
level_transitions: tibble of risk level changes
delta_mean_rpn: change in mean RPN
r1 <- data.frame( risk_id = c("R001", "R002"), description = c("Missing vars", "Bad derivation"), probability = c(4, 3), impact = c(5, 4), detectability = c(2, 3) ) r2 <- data.frame( risk_id = c("R001", "R003"), description = c("Missing vars", "New issue"), probability = c(2, 3), impact = c(5, 3), detectability = c(2, 2) ) rr1 <- create_risk_register(r1) rr2 <- create_risk_register(r2) compare_risk_registers(rr1, rr2)r1 <- data.frame( risk_id = c("R001", "R002"), description = c("Missing vars", "Bad derivation"), probability = c(4, 3), impact = c(5, 4), detectability = c(2, 3) ) r2 <- data.frame( risk_id = c("R001", "R003"), description = c("Missing vars", "New issue"), probability = c(2, 3), impact = c(5, 3), detectability = c(2, 2) ) rr1 <- create_risk_register(r1) rr2 <- create_risk_register(r2) compare_risk_registers(rr1, rr2)
Computes aggregate risk metrics from a risk register, including mean RPN, risk distribution, and overall risk score normalized to 0–1.
compute_risk_scores(risk_register, config = risk_config_default())compute_risk_scores(risk_register, config = risk_config_default())
risk_register |
A |
config |
A |
A list of class "risk_scores" with:
overall_risk_score: 0–1 (0 = no risk, 1 = maximum risk)
mean_rpn: average RPN across all risks
max_rpn: highest individual RPN
n_risks: total risk count
risk_distribution: tibble of counts by risk_level
category_summary: tibble of mean RPN by category
risks <- data.frame( risk_id = c("R001", "R002"), description = c("Missing vars", "Bad derivation"), probability = c(4, 2), impact = c(5, 3), detectability = c(2, 3) ) rr <- create_risk_register(risks) compute_risk_scores(rr)risks <- data.frame( risk_id = c("R001", "R002"), description = c("Missing vars", "Bad derivation"), probability = c(4, 2), impact = c(5, 3), detectability = c(2, 3) ) rr <- create_risk_register(risks) compute_risk_scores(rr)
Builds a risk register from a user-supplied data.frame of identified risks. Validates required columns and fills defaults.
create_risk_register(risks, config = risk_config_default())create_risk_register(risks, config = risk_config_default())
risks |
A data.frame with at minimum columns |
config |
A |
A tibble of class "risk_register" with standardized columns and
computed RPN values.
risks <- data.frame( risk_id = c("R001", "R002", "R003"), description = c("Missing SDTM variables", "Unmapped ADaM derivations", "Inconsistent define.xml"), category = c("data_quality", "traceability", "documentation"), probability = c(4, 3, 2), impact = c(5, 4, 3), detectability = c(2, 3, 4) ) rr <- create_risk_register(risks) rrrisks <- data.frame( risk_id = c("R001", "R002", "R003"), description = c("Missing SDTM variables", "Unmapped ADaM derivations", "Inconsistent define.xml"), category = c("data_quality", "traceability", "documentation"), probability = c(4, 3, 2), impact = c(5, 4, 3), detectability = c(2, 3, 4) ) rr <- create_risk_register(risks) rr
Automatically generates risk items from an R4SUB evidence table. Each failing or warning indicator becomes a potential risk, with probability and impact inferred from evidence severity.
evidence_to_risks( evidence, config = risk_config_default(), include_pass = FALSE )evidence_to_risks( evidence, config = risk_config_default(), include_pass = FALSE )
evidence |
A validated evidence data.frame (from |
config |
A |
include_pass |
Logical; if |
The mapping from evidence to risk uses:
risk_id: derived from indicator_id + asset_id via
r4subcore::hash_id()
category: mapped from indicator_domain
probability: mapped from evidence severity via config
impact: mapped from evidence severity via config
detectability: uses config$default_detectability
Multiple evidence rows for the same indicator + asset are aggregated: probability and impact use the maximum across rows.
A tibble suitable for create_risk_register().
## Not run: risk_items <- evidence_to_risks(evidence) rr <- create_risk_register(risk_items) ## End(Not run)## Not run: risk_items <- evidence_to_risks(evidence) rr <- create_risk_register(risk_items) ## End(Not run)
Print Risk Register
## S3 method for class 'risk_register' print(x, ...)## S3 method for class 'risk_register' print(x, ...)
x |
A |
... |
Ignored. |
Print Risk Scores
## S3 method for class 'risk_scores' print(x, ...)## S3 method for class 'risk_scores' print(x, ...)
x |
A |
... |
Ignored. |
Returns configuration for risk assessment including FMEA scale definitions, RPN thresholds, and risk level classification bands.
risk_config_default( rpn_bands = list(critical = c(80, 125), high = c(40, 79), medium = c(15, 39), low = c(1, 14)), evidence_severity_to_probability = c(info = 1, low = 2, medium = 3, high = 4, critical = 5), evidence_severity_to_impact = c(info = 1, low = 2, medium = 3, high = 4, critical = 5), default_detectability = 3 )risk_config_default( rpn_bands = list(critical = c(80, 125), high = c(40, 79), medium = c(15, 39), low = c(1, 14)), evidence_severity_to_probability = c(info = 1, low = 2, medium = 3, high = 4, critical = 5), evidence_severity_to_impact = c(info = 1, low = 2, medium = 3, high = 4, critical = 5), default_detectability = 3 )
rpn_bands |
Named list of RPN band boundaries |
evidence_severity_to_probability |
Named numeric vector mapping evidence severity to probability scores (1–5 scale). |
evidence_severity_to_impact |
Named numeric vector mapping evidence severity to impact scores (1–5 scale). |
default_detectability |
Default detectability score (1–5) when not explicitly provided. Lower = more detectable. |
The FMEA-inspired risk model uses three dimensions:
Probability (1–5): likelihood of the issue occurring/persisting
Impact (1–5): severity of consequence if unresolved
Detectability (1–5): difficulty of detecting the issue (1 = easy, 5 = hard)
RPN = Probability x Impact x Detectability (range 1–125)
A list of class "risk_config" with elements:
rpn_bands, evidence_severity_to_probability,
evidence_severity_to_impact, default_detectability.
cfg <- risk_config_default() cfg$rpn_bandscfg <- risk_config_default() cfg$rpn_bands
Computes summary risk indicators from a risk register, similar to
r4subtrace::trace_indicator_scores().
risk_indicator_summary(risk_register)risk_indicator_summary(risk_register)
risk_register |
A |
A tibble with columns: indicator, value, description.
risks <- data.frame( risk_id = c("R001", "R002", "R003"), description = c("Missing vars", "Bad derivation", "Label mismatch"), probability = c(4, 2, 1), impact = c(5, 3, 2), detectability = c(2, 3, 1) ) rr <- create_risk_register(risks) risk_indicator_summary(rr)risks <- data.frame( risk_id = c("R001", "R002", "R003"), description = c("Missing vars", "Bad derivation", "Label mismatch"), probability = c(4, 2, 1), impact = c(5, 3, 2), detectability = c(2, 3, 1) ) rr <- create_risk_register(risks) risk_indicator_summary(rr)
Emits evidence rows compatible with r4subcore::validate_evidence() for
each risk item in the register, plus aggregate risk metric rows.
risk_register_to_evidence( risk_register, ctx, source_name = "r4subrisk", source_version = NULL )risk_register_to_evidence( risk_register, ctx, source_name = "r4subrisk", source_version = NULL )
risk_register |
A |
ctx |
An |
source_name |
Character; the name of the evidence source. |
source_version |
Character or |
A data.frame of evidence rows passing r4subcore::validate_evidence().
## Not run: library(r4subcore) ctx <- r4sub_run_context(study_id = "TEST001", environment = "DEV") risks <- data.frame( risk_id = "R001", description = "Missing vars", probability = 4, impact = 5, detectability = 2 ) rr <- create_risk_register(risks) ev <- risk_register_to_evidence(rr, ctx = ctx) ## End(Not run)## Not run: library(r4subcore) ctx <- r4sub_run_context(study_id = "TEST001", environment = "DEV") risks <- data.frame( risk_id = "R001", description = "Missing vars", probability = 4, impact = 5, detectability = 2 ) rr <- create_risk_register(risks) ev <- risk_register_to_evidence(rr, ctx = ctx) ## End(Not run)