qRisk3-2017
This stroke/heart attack risk calculator was developed in the UK by University of Nottingham and EMIS (a major GP EMR supplier). An online calculator is visible here. The algorithm is published as a PHP program. See also this article.
What we can show here is how to represent the algorithm in a way that clinicians can directly understand, and indeed, could develop and maintain, given appropriate tools.
Decision Logic Module
This DLM is a fairly literal rendering of the algorithm at the above link, with a cleaner representation of the scale factors, and a bit of vector math.
dlm qRisk3_2017.v0.5.0
definitions -- Descriptive
language = {
original_language: [ISO_639-1::en]
}
;
description = {
lifecycle_state: "unmanaged",
original_author: {
name: "Thomas Beale",
email: "thomas.beale@openEHR.org",
organisation: "openEHR International <http://www.openEHR.org>",
date: "2021-01-10"
},
details: {
"en" : {
language: [ISO_639-1::en],
purpose: "To record an individual's QRISK3 score."
}
},
copyright: "© 2021 openEHR Foundation",
licence: "Creative Commons CC-BY <https://creativecommons.org/licenses/by/3.0/>",
ip_acknowledgements: {
"ClinRisk" : "This content developed from original publication of
© 2017 ClinRisk Ltd., see https://qrisk.org",
"QRISK" : "QRISK® is a registered trademark of the University of Nottingham and EMIS"
}
}
;
use
BASIC: Basic_patient_data.v0.5.0
BMI: Body_mass_index.v0.5.0
definitions -- Reference
Ethnicity_risk_factors = {
#female: {
#white_or_not_stated: 0.280403143329954250,
#indian: 0.562989941420753980,
#pakistani: 0.295900008511165160,
#bangladeshi: 0.072785379877982545,
#other_asian: -0.170721355088573170,
#black_caribbean: -0.393710433148749710,
#black_african: -0.326324952835302720,
#other_ethnic_group: -0.171270568832417840
},
#male: {
#white_or_not_stated: 0.277192487603082790,
#indian: 0.474463607149312680,
#pakistani: 0.529617299196893710,
#bangladeshi: 0.035100159186299017,
#other_asian: -0.358078996693279190,
#black_caribbean: -0.400564852321651400,
#black_african: -0.415227928898301730,
#other_ethnic_group: -0.263213481347499670
},
}
;
Smoking_risk_factors = {
#female: {
#non_smoker: 0,
#ex_smoker: 0.133868337865462620,
#light_smoker: 0.562008580124385370,
#moderate_smoker: 0.667495933775025470,
#heavy_smoker: 0.849481776448308470
},
#male: {
#non_smoker: 0,
#ex_smoker: 0.191282228633889830,
#light_smoker: 0.552415881926455520,
#moderate_smoker: 0.638350530275060720,
#heavy_smoker: 0.789838198818580190
}
}
;
Age_1_stats = {
#female: {
#centre: 0.053274843841791,
#scale: -8.138810924772618800
},
#male: {
#centre: 0.053274843841791,
#scale: -17.839781666005575000
}
}
;
Age_2_stats = {
#female: {
#centre: 4.332503318786621,
#scale: 0.797333766896990980
},
#male: {
#centre: 77.284080505371094,
#scale: 0.0022964880605765492
}
}
;
BMI_1_stats = {
#female: {
#centre: 0.154946178197861,
#scale: 0.292360922754600520
},
#male: {
#centre: 0.149176135659218,
#scale: 2.456277666053635800
}
}
;
BMI_2_stats = {
#female: {
#centre: 0.144462317228317,
#scale: -4.151330021383766500
},
#male: {
#centre: 0.141913309693336,
#scale: -8.301112231471135400
}
}
;
Rheumatoid_arthritis_stats = {
#female: {
#centre: 3.476326465606690,
#scale: 0.153380358208025540
},
#male: {
#centre: 4.300998687744141,
#scale: 0.173401968563271110
}
}
;
Systolic_BP_stats = {
#female: {
#centre: 123.130012512207030,
#scale: 0.0131314884071034240
},
#male: {
#centre: 128.571578979492190,
#scale: 0.0129101265425533050
}
}
;
Systolic_BP_std_dev_stats = {
#female: {
#centre: 9.002537727355957,
#scale: 0.0078894541014586095
},
#male: {
#centre: 8.756621360778809,
#scale: 0.0129101265425533050
}
}
;
Townsend_stats = {
#female: {
#centre: 0.392308831214905,
#scale: 0.0772237905885901080
},
#male: {
#centre: 0.526304900646210,
#scale: 0.0332682012772872950
}
}
;
Risk_factor_scales = {
#female: {
#has_atrial_fibrillation: 1.59233549692696630,
#atypical_antipsychotic_medication: 0.252376420701155570,
#on_corticosteroids: 0.595207253046018510,
#has_impotence: 0,
#has_migraines: 0.3012672608703450,
#has_rheumatoid_arthritis: 0.213648034351819420,
#has_chronic_kidney_disease: 0.651945694938458330,
#has_severe_mental_illness: 0.125553080588201780,
#has_systemic_lupus: 0.758809386542676930,
#on_hypertension_treatment: 0.509315936834230040,
#has_family_history_CV_disease: 0.454453190208962130
},
#male: {
#has_atrial_fibrillation: 0.882092369280546570,
#atypical_antipsychotic_medication: 0.130468798551735130,
#on_corticosteroids: 0.454853997504455430,
#has_impotence: 0.222518590867053830,
#has_migraines: 0.255841780741599130,
#has_rheumatoid_arthritis: 0.209706580139565670,
#has_chronic_kidney_disease: 0.718532612882743840,
#has_severe_mental_illness: 0.121330398820471640,
#has_systemic_lupus: 0.440157217445752200,
#on_hypertension_treatment: 0.516598710826954740,
#has_family_history_CV_disease: 0.540554690093901560
}
}
;
Diabetes_scales:
#female: {
#no_diabetes: 0,
#type1_diabetes: 1.72679775105373470,
#type2_diabetes: 1.06887732446154680
}
#male: {
#no_diabetes: 0,
#type1_diabetes: 1.234342552167517500
#type2_diabetes: 0.859420714309322210
}
;
Interaction_scales = {
#female: {
#age_1: {
#has_atrial_fibrillation: 19.9380348895465610,
#on_corticosteroids: -0.9840804523593628100000000,
#has_impotence: 0,
#has_migraines: 1.7634979587872999000000000,
#has_chronic_kidney_disease: -3.5874047731694114000000000,
#has_systemic_lupus: 19.6903037386382920000000000,
#on_hypertension_treatment: 11.8728097339218120000000000,
#bmi_1: 23.8026234121417420000000000,
#bmi_2: -71.1849476920870070000000000,
#family_history_CV_disease: 0.9946780794043512700000000,
#systolic_BP: 0.0341318423386154850000000,
#townsend: -1.0301180802035639000000000
},
#age_2: {
#has_atrial_fibrillation: -0.0761826510111625050000000,
#on_corticosteroids: -0.1200536494674247200000000,
#has_impotence: 0,
#has_migraines: -0.0655869178986998590000000,
#has_chronic_kidney_disease: -0.2268887308644250700000000,
#has_systemic_lupus: 0.0773479496790162730000000,
#on_hypertension_treatment: 0.0009685782358817443600000,
#bmi_1: 0.5236995893366442900000000,
#bmi_2: 0.0457441901223237590000000,
#family_history_CV_disease: -0.0768850516984230380000000,
#systolic_BP: -0.0015082501423272358000000,
#townsend: -0.0315934146749623290000000
}
},
#male: {
#age_1: {
#has_atrial_fibrillation: 3.4896675530623207000000000,
#on_corticosteroids: 1.1708133653489108000000000,
#has_impotence: -1.5064009857454310000000000,
#has_migraines: 2.3491159871402441000000000,
#has_chronic_kidney_disease: -0.5065671632722369400000000,
#on_hypertension_treatment: 6.5114581098532671000000000,
#bmi_1: 31.0049529560338860000000000,
#bmi_2: -111.2915718439164300000000000,
#family_history_CV_disease: 2.7808628508531887000000000,
#systolic_BP: 0.0188585244698658530000000,
#townsend: -0.1007554870063731000000000
},
#age_2: {
#has_atrial_fibrillation: -0.0003499560834063604900000,
#on_corticosteroids: -0.0002496045095297166000000,
#has_impotence: -0.0011058218441227373000000,
#has_migraines: 0.0001989644604147863100000,
#has_chronic_kidney_disease: -0.0018325930166498813000000,
#on_hypertension_treatment: 0.0006383805310416501300000,
#bmi_1: 0.0050380102356322029000000,
#bmi_2: -0.0130744830025243190000000,
#family_history_CV_disease: -0.0002479180990739603700000,
#systolic_BP: -0.0000127187419158845700000,
#townsend: -0.0000932996423232728880000
}
}
}
;
Snoking_interaction_scales = {
#female: {
#age_1: {
#non_smoker: 0,
#ex_smoker: -4.70571617858518910,
#light_smoker: -2.74303834035733370,
#moderate_smoker: -0.866080888293921820,
#heavy_smoker: 0.902415623697106480
},
#age_2: {
#non_smoker: 0,
#ex_smoker: -0.0755892446431930260000000,
#light_smoker: -0.1195119287486707400000000,
#moderate_smoker: -0.1036630639757192300000000,
#heavy_smoker: -0.1399185359171838900000000
}
},
#male: {
#age_1: {
#non_smoker: 0,
#ex_smoker: -0.2101113393351634600000000,
#light_smoker: 0.7526867644750319100000000,
#moderate_smoker: 0.9931588755640579100000000,
#heavy_smoker: 2.1331163414389076000000000
},
#age_2: {
#non_smoker: 0,
#ex_smoker: -0.0004985487027532612100000,
#light_smoker: -0.0007987563331738541400000,
#moderate_smoker: -0.0008370618426625129600000,
#heavy_smoker: -0.0007840031915563728900000
}
}
}
;
Diabetes_interaction_scales = {
#female: {
#age_1: {
#no_diabetes: 0,
#type1_diabetes: -1.2444332714320747000000000,
#type2_diabetes: 6.8652342000009599000000000
},
#age_2: {
#no_diabetes: 0,
#type1_diabetes: -0.2872406462448894900000000,
#type2_diabetes: -0.0971122525906954890000000
}
},
#male: {
#age_1: {
#no_diabetes: 0,
#type1_diabetes: 5.3379864878006531000000000,
#type2_diabetes: 3.6461817406221311000000000
},
#age_2: {
#no_diabetes: 0,
#type1_diabetes: 0.0006409780808752897000000,
#type2_diabetes: -0.0002469569558886831500000
}
}
}
;
input -- Administrative
|
| Ethnicity for qRisk3:
| #white_or_not_stated
| #indian
| #pakistani
| #bangladeshi
| #other_asian
| #black_caribbean
| #black_african
| #other_ethnic_group
|
qRisk3_ethnicity: Terminology_code «qrisk_ethnicities»,
;
townsend: Real
;
input -- Historical state
|
| Smoking status:
| #non_smoker
| #ex_smoker
| #light_smoker
| #moderate_smoker
| #heavy_smoker
|
smoking_status: Terminology_code «smoking_status»,
;
|
| Diabetes:
| #no_diabetes
| #type1_diabetes
| #type2_diabetes
|
diabetes_status: Terminology_code «diabetes_status»,
;
|
| Angina or heart attack in a 1st degree relative < 60
|
family_history_CV_disease: Boolean
;
|
| Chronic kidney disease (stage 3, 4 or 5)
|
has_chronic_kidney_disease: Boolean
;
has_atrial_fibrillation: Boolean
;
on_hypertension_treatment: Boolean
;
has_migraines: Boolean
;
has_rheumatoid_arthritis: Boolean
;
|
| Has or being treated for erectile dysfunction
| (female -> False)
|
has_impotence: Boolean
|
| Has Systemic lupus erythematosus (SLE)
|
has_systemic_lupus: Boolean
;
|
| Severe mental illness (this includes schizophrenia,
| bipolar disorder and moderate/severe depression)
|
has_severe_mental_illness: Boolean
;
on_atypical_antipsychotic_medication: Boolean
;
on_corticosteroids: Boolean
;
input -- Tracking state
total_cholesterol_HDL_ratio: Real
;
|
| Systolic BP in mmHg, at least 2, max 10 samples
|
systolic_BP_history: Array<Real>[2..10]
;
rules -- Main
systolic_BP_std_deviation: Real
Result := {Statistical_evaluator}.std_dev (systolic_BP_history)
;
|
| Applying the fractional polynomial transforms
| (which includes scaling)
|
age_1_centred: Real
Result := (BASIC.age_in_years/10) ^ 0.5 - Age_1_stats[BASIC.sex]#centre
;
age_1_score: Real
Result := age_1_centred * Age_1_stats[BASIC.sex]#scale
;
age_2_centred: Real
Result := BASIC.age_in_years/10 - Age_2_stats[BASIC.sex]#centre
;
age_2_score: Real
Result := age_2_centred * Age_2_stats[BASIC.sex]#scale
;
BMI_scaled: Real
Result := BMI.BMI/10
;
BMI_1_centred: Real
Result := BMI_scaled ^ 0.5 - BMI_1_stats[BASIC.sex]#centre
;
BMI_1_score: Real
Result := BMI_1_centred * BMI_1_stats[BASIC.sex]#scale
;
BMI_2_centred: Real
Result := BMI_scaled ^ 0.5 * {math}.ln (BMI_scaled) - BMI_2_stats[BASIC.sex]#centre
;
BMI_2_score: Real
Result := BMI_2_centred * BMI_2_stats[BASIC.sex]#scale
;
rheumatoid_arthritis_score: Real
Result := (has_rheumatoid_arthritis.as_integer - Rheumatoid_arthritis_stats[BASIC.sex]#centre)
* Rheumatoid_arthritis_stats[BASIC.sex]#scale
;
systolic_BP: Real
Result := systolic_BP_history.last
;
systolic_BP_score: Real
Result := (systolic_BP - Systolic_BP_stats[BASIC.sex]#centre)
* Systolic_BP_stats[BASIC.sex]#scale
;
systolic_BP_std_dev_score: Real
Result := (systolic_BP_std_deviation - Systolic_BP_std_dev_stats[BASIC.sex]#centre)
* Systolic_BP_std_dev_stats[BASIC.sex]#scale
;
townsend_score: Real
Result := (townsend_score - Townsend_stats[BASIC.sex]#centre)
* Townsend_stats[BASIC.sex]#scale
;
|
| TODO: Unclear what this is from published algorithm
|
survivor_factor: Real
;
|
| Compute quantitative & classified part of score
|
raw_score_1: Real
Result := add (
Ethnicity_risk_factors[BASIC.sex, qRisk3_ethnicity],
Smoking_risk_factors[BASIC.sex, smoking_status],
age_1_score,
age_2_score,
BMI_1_score,
BMI_2_score,
rheumatoid_arthritis_score,
systolic_BP_score,
systolic_BP_std_dev_score,
townsend_score_score,
Diabetes_scales[BASIC.sex, diabetes_status]
)
;
|
| Compute boolean part of score; use vector
| in order to copmpute dot product with scales
|
boolean_risks: Vector<Real>,
Result := [
has_atrial_fibrillation.as_integer,
on_atypical_antipsychotic_medication.as_integer,
on_corticosteroids.as_integer,
has_impotence.as_integer,
has_migraines.as_integer,
has_rheumatoid_arthritis.as_integer,
has_chronic_kidney_disease.as_integer,
has_severe_mental_illness.as_integer,
has_systemic_lupus.as_integer,
on_hypertension_treatment.as_integer,
family_history_CV_disease.as_integer
]
;
raw_score_2: Real
Result := boolean_risks . Risk_factor_scales[BASIC.sex]
;
|
| Compute interaction part of score; use vector
| in order to copmpute dot product with scales
|
interaction_risks: Vector<Real>,
Result := [
has_atrial_fibrillation.as_integer,
on_corticosteroids.as_integer,
has_impotence.as_integer,
has_migraines.as_integer,
has_chronic_kidney_disease.as_integer,
has_systemic_lupus.as_integer,
on_hypertension_treatment.as_integer,
BMI_1_centred,
BMI_2_centred,
family_history_CV_disease.as_integer,
systolic_BP,
townsend
]
;
raw_score_3: Real
Result := add (
age_1_centred * Smoking_interaction_scales[BASIC.sex, #age_1, smoking_status],
age_1_centred * Diabetes_interaction_scales[BASIC.sex, #age_1, diabetes_status],
age_1_centred * interaction_risks . Interaction_scales[BASIC.sex, #age_1],
age_2_centred * Smoking_interaction_scales[BASIC.sex, #age_2, smoking_status],
age_2_centred * Diabetes_interaction_scales[BASIC.sex, #age_2, diabetes_status],
age_2_centred * interaction_risks . Interaction_scales[BASIC.sex, #age_2]
)
;
raw_score: Real
Result = raw_score_1 + raw_score_2 + raw_score_3
;
rules -- Output
qRisk3_score: Real
Result := 100.0 * (1 - survivor_factor ^ exp (raw_score))
;
definitions -- Terminology
terminology = {
term_definitions: {
"en" : {
"qRisk_score" : {
text: "QRISK2 score"
},
"non_smoker" : {
text: "Non-smoker"
},
"no_diabetes" : {
text: "Non-diabetic"
},
"total_cholesterol_HDL_ratio" : {
text: "Total cholesterol : HDL ratio"
},
"TODO: rest of terminology" : {
text: "TODO: rest of terminology"
}
}
}
value_sets: {
"diabetes_status" : {
id: "diabetes_status",
members: ["no_diabetes", "type1_diabetes", "type2_diabetes"]
},
"smoking status": {
id: "status",
members: ["non_smoker", "ex_smoker", "light_smoker",
"moderate_smoker", "heavy_smoker"]
}
}
}
;