diagcalc 3.2.4 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -8
- package/bin/diagcalc.js +142 -41
- package/docs/PROVENANCE.md +17 -0
- package/index.html +430 -118
- package/lib/README.md +98 -0
- package/lib/diagcalc-case.js +148 -0
- package/lib/diagcalc-core.js +554 -89
- package/lib/diagcalc-datasets.js +140 -1
- package/lib/diagcalc-geometry.js +27 -0
- package/lib/diagcalc-i18n.js +584 -0
- package/lib/diagcalc-meta.js +9 -0
- package/lib/diagcalc-presentation.js +278 -0
- package/lib/diagcalc-storage.js +45 -0
- package/lib/diagcalc-types.d.ts +59 -0
- package/package.json +18 -4
- package/script.js +1269 -277
- package/styles.css +2231 -325
- package/tui/index.js +75 -272
- package/tui/input.js +24 -0
- package/tui/presentation.js +168 -0
- package/tui/report.js +130 -0
- package/web/charts.js +339 -0
- package/web/results.js +235 -0
package/index.html
CHANGED
|
@@ -4,138 +4,135 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>DIAG-CALC</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700;800&display=swap" />
|
|
10
7
|
<link rel="stylesheet" href="styles.css" />
|
|
11
8
|
<script defer src="https://analytics.tiagojct.eu/script.js" data-website-id="2c7df365-61e6-42e6-8298-a20ada3e296a"></script>
|
|
12
9
|
</head>
|
|
13
10
|
<body>
|
|
14
|
-
<header class="
|
|
15
|
-
<div class="
|
|
16
|
-
<div class="
|
|
17
|
-
<
|
|
11
|
+
<header class="app-bar" role="banner">
|
|
12
|
+
<div class="app-bar-inner">
|
|
13
|
+
<div class="app-bar-brand">
|
|
14
|
+
<span class="brand-mark">DIAGCALC</span>
|
|
15
|
+
<span class="app-bar-version" aria-label="version" data-i18n-attr="aria-label=attr.version">v5.1.0</span>
|
|
16
|
+
<span class="app-bar-divider" aria-hidden="true">·</span>
|
|
17
|
+
<span class="app-bar-case" id="appCaseStrip" data-i18n="app.case.empty">No case yet</span>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="app-bar-controls">
|
|
20
|
+
<label class="lang-picker" for="languageSelect">
|
|
21
|
+
<span class="visually-hidden" data-i18n="lang.label">Language</span>
|
|
22
|
+
<select id="languageSelect" aria-label="Language" data-i18n-attr="aria-label=attr.language">
|
|
23
|
+
<option value="en">English</option>
|
|
24
|
+
<option value="pt-PT">Português</option>
|
|
25
|
+
</select>
|
|
26
|
+
</label>
|
|
27
|
+
<button id="themeToggle" class="theme-toggle" type="button" data-i18n-attr="aria-label=theme.toggle.label" >
|
|
18
28
|
<span class="theme-icon">◐</span>
|
|
19
29
|
</button>
|
|
20
30
|
</div>
|
|
21
|
-
<p class="tag">Educational tool</p>
|
|
22
|
-
<h1>DIAGCALC</h1>
|
|
23
|
-
<p class="lead">
|
|
24
|
-
Interactive calculator for exploring diagnostic test performance measures
|
|
25
|
-
and applying Bayesian reasoning in clinical practice.
|
|
26
|
-
</p>
|
|
27
|
-
<div class="intro-highlights" role="list">
|
|
28
|
-
<article class="highlight" role="listitem">
|
|
29
|
-
<h2>Key measures</h2>
|
|
30
|
-
<p>Sensitivity, specificity, PPV, NPV, and likelihood ratios with 95% CI.</p>
|
|
31
|
-
</article>
|
|
32
|
-
<article class="highlight" role="listitem">
|
|
33
|
-
<h2>Post-test probability</h2>
|
|
34
|
-
<p>Convert pre-test probability into positive and negative post-test probability.</p>
|
|
35
|
-
</article>
|
|
36
|
-
<article class="highlight" role="listitem">
|
|
37
|
-
<h2>Guided interpretation</h2>
|
|
38
|
-
<p>Results explained to support self-directed learning.</p>
|
|
39
|
-
</article>
|
|
40
|
-
</div>
|
|
41
31
|
</div>
|
|
42
32
|
</header>
|
|
43
33
|
<main class="container" id="main" role="main">
|
|
44
34
|
<div class="workspace">
|
|
45
|
-
<section aria-labelledby="
|
|
35
|
+
<section aria-labelledby="form-title" class="panel panel-form">
|
|
46
36
|
<div class="panel-heading">
|
|
47
|
-
<p class="panel-kicker">
|
|
48
|
-
<h2 id="
|
|
49
|
-
</div>
|
|
50
|
-
<div class="form-row">
|
|
51
|
-
<label class="full" for="datasetSelect">Scenario</label>
|
|
52
|
-
<select id="datasetSelect" class="full" aria-describedby="dataset-help">
|
|
53
|
-
<option value="">— Select —</option>
|
|
54
|
-
<optgroup label="Generic scenarios">
|
|
55
|
-
<option value="screening">Screening programme (low prevalence)</option>
|
|
56
|
-
<option value="caseControl">Case-control study</option>
|
|
57
|
-
<option value="clinic">Specialist clinic</option>
|
|
58
|
-
</optgroup>
|
|
59
|
-
<optgroup label="Medical literature cases">
|
|
60
|
-
<option value="ddimer">D-dimer for pulmonary embolism</option>
|
|
61
|
-
<option value="troponin">High-sensitivity troponin for AMI</option>
|
|
62
|
-
<option value="mammography">Screening mammography</option>
|
|
63
|
-
<option value="covid_antigen">Rapid COVID-19 antigen test</option>
|
|
64
|
-
<option value="hiv_elisa">HIV ELISA</option>
|
|
65
|
-
<option value="strep_throat">Rapid Streptococcus test</option>
|
|
66
|
-
<option value="xray_pneumonia">Chest X-ray for pneumonia</option>
|
|
67
|
-
</optgroup>
|
|
68
|
-
</select>
|
|
69
|
-
<p id="dataset-help" class="help-text">
|
|
70
|
-
Values are filled in automatically and can be adjusted afterwards.
|
|
71
|
-
</p>
|
|
72
|
-
<div id="dataset-reference" class="dataset-reference" style="display: none;"></div>
|
|
73
|
-
</div>
|
|
74
|
-
</section>
|
|
75
|
-
|
|
76
|
-
<section aria-labelledby="form-title" class="panel panel-inputs">
|
|
77
|
-
<div class="panel-heading">
|
|
78
|
-
<p class="panel-kicker">Editor</p>
|
|
79
|
-
<h2 id="form-title">Build the confusion matrix</h2>
|
|
80
|
-
<p>
|
|
81
|
-
Use non-negative integers and a pre-test probability under 100%.
|
|
82
|
-
</p>
|
|
37
|
+
<p class="panel-kicker" data-i18n="editor.kicker">Editor</p>
|
|
38
|
+
<h2 id="form-title" data-i18n="editor.h">Build the case</h2>
|
|
83
39
|
</div>
|
|
84
40
|
<form id="inputForm" novalidate>
|
|
41
|
+
<div class="form-row">
|
|
42
|
+
<label class="full" for="datasetSelect" data-i18n="form.scenario">Scenario</label>
|
|
43
|
+
<select id="datasetSelect" class="full" aria-describedby="dataset-help">
|
|
44
|
+
<option value="" data-i18n="form.scenario.empty">— Select —</option>
|
|
45
|
+
<optgroup label="Generic scenarios" data-i18n-attr="label=form.scenario.group.generic" >
|
|
46
|
+
<option value="screening" data-i18n="dataset.screening.name">Screening programme (low prevalence)</option>
|
|
47
|
+
<option value="caseControl" data-i18n="dataset.caseControl.name">Case-control study</option>
|
|
48
|
+
<option value="clinic" data-i18n="dataset.clinic.name">Specialist clinic</option>
|
|
49
|
+
</optgroup>
|
|
50
|
+
<optgroup label="Illustrative medical scenarios" data-i18n-attr="label=form.scenario.group.literature" >
|
|
51
|
+
<option value="ddimer" data-i18n="dataset.ddimer.name">D-dimer for pulmonary embolism</option>
|
|
52
|
+
<option value="troponin" data-i18n="dataset.troponin.name">High-sensitivity troponin for AMI</option>
|
|
53
|
+
<option value="mammography" data-i18n="dataset.mammography.name">Screening mammography</option>
|
|
54
|
+
<option value="covid_antigen" data-i18n="dataset.covid_antigen.name">Rapid COVID-19 antigen test</option>
|
|
55
|
+
<option value="hiv_elisa" data-i18n="dataset.hiv_elisa.name">HIV ELISA</option>
|
|
56
|
+
<option value="strep_throat" data-i18n="dataset.strep_throat.name">Rapid Streptococcus test</option>
|
|
57
|
+
<option value="xray_pneumonia" data-i18n="dataset.xray_pneumonia.name">Chest X-ray for pneumonia</option>
|
|
58
|
+
<option value="wells_dvt" data-i18n="dataset.wells_dvt.name">Wells score (≥2) for DVT</option>
|
|
59
|
+
<option value="heart_acs" data-i18n="dataset.heart_acs.name">HEART score (≥4) for MACE in chest pain</option>
|
|
60
|
+
<option value="curb65" data-i18n="dataset.curb65.name">CURB-65 (≥2) for severe community-acquired pneumonia</option>
|
|
61
|
+
<option value="qsofa" data-i18n="dataset.qsofa.name">qSOFA (≥2) for sepsis screening</option>
|
|
62
|
+
<option value="apgar_5min" data-i18n="dataset.apgar_5min.name">APGAR < 7 at 5 min for severe neonatal outcome</option>
|
|
63
|
+
<option value="fast_trauma" data-i18n="dataset.fast_trauma.name">FAST ultrasound for blunt abdominal trauma</option>
|
|
64
|
+
</optgroup>
|
|
65
|
+
</select>
|
|
66
|
+
<p id="dataset-help" class="help-text" data-i18n="library.help">
|
|
67
|
+
Pick a preset to load values, or fill in the matrix manually below.
|
|
68
|
+
</p>
|
|
69
|
+
<div id="dataset-reference" class="dataset-reference" style="display: none;"></div>
|
|
70
|
+
</div>
|
|
85
71
|
<fieldset class="confusion-matrix">
|
|
86
|
-
<legend>Confusion matrix</legend>
|
|
87
|
-
<div class="grid-container" role="group" aria-label="Diagnostic test results">
|
|
72
|
+
<legend data-i18n="form.matrix.legend">Confusion matrix</legend>
|
|
73
|
+
<div class="grid-container" role="group" aria-label="Diagnostic test results" data-i18n-attr="aria-label=attr.diagnostic.test.results">
|
|
88
74
|
<span class="empty" aria-hidden="true"></span>
|
|
89
|
-
<span class="grid-header grid-header-pos">Disease +</span>
|
|
90
|
-
<span class="grid-header grid-header-neg">Disease -</span>
|
|
91
|
-
<label class="grid-label grid-label-pos" for="tp">Test +</label>
|
|
75
|
+
<span class="grid-header grid-header-pos" data-i18n="form.matrix.dpos"><span data-i18n="matrix.disease">Disease +</span></span>
|
|
76
|
+
<span class="grid-header grid-header-neg" data-i18n="form.matrix.dneg">Disease -</span>
|
|
77
|
+
<label class="grid-label grid-label-pos" for="tp" data-i18n="form.matrix.tpos"><span data-i18n="matrix.test">Test +</span></label>
|
|
92
78
|
<div class="grid-item">
|
|
93
79
|
<input type="number" id="tp" inputmode="numeric" min="0" step="1"
|
|
94
|
-
placeholder="
|
|
80
|
+
placeholder="TP"
|
|
95
81
|
aria-label="True positives (TP): disease present, test positive"
|
|
96
|
-
required />
|
|
82
|
+
required data-i18n-attr="placeholder=attr.tp;aria-label=attr.true.positives.tp.disease.present.test.positive" />
|
|
97
83
|
</div>
|
|
98
84
|
<div class="grid-item">
|
|
99
85
|
<input type="number" id="fp" inputmode="numeric" min="0" step="1"
|
|
100
|
-
placeholder="
|
|
86
|
+
placeholder="FP"
|
|
101
87
|
aria-label="False positives (FP): disease absent, test positive"
|
|
102
|
-
required />
|
|
88
|
+
required data-i18n-attr="placeholder=attr.fp;aria-label=attr.false.positives.fp.disease.absent.test.positive" />
|
|
103
89
|
</div>
|
|
104
|
-
<label class="grid-label grid-label-neg" for="fn">Test -</label>
|
|
90
|
+
<label class="grid-label grid-label-neg" for="fn" data-i18n="form.matrix.tneg">Test -</label>
|
|
105
91
|
<div class="grid-item">
|
|
106
92
|
<input type="number" id="fn" inputmode="numeric" min="0" step="1"
|
|
107
|
-
placeholder="
|
|
93
|
+
placeholder="FN"
|
|
108
94
|
aria-label="False negatives (FN): disease present, test negative"
|
|
109
|
-
required />
|
|
95
|
+
required data-i18n-attr="placeholder=attr.fn;aria-label=attr.false.negatives.fn.disease.present.test.negative" />
|
|
110
96
|
</div>
|
|
111
97
|
<div class="grid-item">
|
|
112
98
|
<input type="number" id="tn" inputmode="numeric" min="0" step="1"
|
|
113
|
-
placeholder="
|
|
99
|
+
placeholder="TN"
|
|
114
100
|
aria-label="True negatives (TN): disease absent, test negative"
|
|
115
|
-
required />
|
|
101
|
+
required data-i18n-attr="placeholder=attr.tn;aria-label=attr.true.negatives.tn.disease.absent.test.negative" />
|
|
116
102
|
</div>
|
|
117
103
|
</div>
|
|
118
104
|
</fieldset>
|
|
119
105
|
<div class="form-row">
|
|
120
|
-
<label for="preTestProb">Pre-test probability (%)</label>
|
|
106
|
+
<label for="preTestProb" data-i18n="form.pre.label">Pre-test probability (%)</label>
|
|
121
107
|
<input
|
|
122
108
|
class="preTestProb"
|
|
123
|
-
type="
|
|
109
|
+
type="text"
|
|
124
110
|
id="preTestProb"
|
|
125
111
|
inputmode="decimal"
|
|
126
|
-
min="0"
|
|
127
|
-
max="100"
|
|
128
|
-
step="0.1"
|
|
129
112
|
placeholder="e.g. 12.5"
|
|
130
|
-
required
|
|
113
|
+
required data-i18n-attr="placeholder=attr.e.g.12.5"
|
|
131
114
|
/>
|
|
132
|
-
<p class="help-text">
|
|
115
|
+
<p class="help-text" data-i18n="form.pre.help">
|
|
133
116
|
Suggestion: estimate the disease prevalence in the study population or the clinical probability before the test.
|
|
134
117
|
</p>
|
|
135
118
|
</div>
|
|
119
|
+
<details class="advanced-settings">
|
|
120
|
+
<summary data-i18n="form.advanced.summary">Advanced settings</summary>
|
|
121
|
+
<div class="form-row">
|
|
122
|
+
<label for="continuityModeSelect" data-i18n="form.advanced.continuity">Continuity correction for LR / DOR CIs</label>
|
|
123
|
+
<select id="continuityModeSelect" class="full">
|
|
124
|
+
<option value="auto" selected data-i18n="form.advanced.continuity.auto">Auto — apply +0.5 only when a 2×2 cell is zero</option>
|
|
125
|
+
<option value="always" data-i18n="form.advanced.continuity.always">Always — apply +0.5 to every cell</option>
|
|
126
|
+
<option value="never" data-i18n="form.advanced.continuity.never">Never — leave undefined intervals unavailable</option>
|
|
127
|
+
</select>
|
|
128
|
+
<p class="help-text" data-i18n="form.advanced.continuity.help">
|
|
129
|
+
When any cell of the confusion matrix is zero the log-normal CI for LR+ / LR− / DOR is undefined.
|
|
130
|
+
</p>
|
|
131
|
+
</div>
|
|
132
|
+
</details>
|
|
136
133
|
<div class="form-actions" role="group">
|
|
137
|
-
<button type="submit">Calculate results</button>
|
|
138
|
-
<button type="reset" id="resetButton" class="secondary">Clear fields</button>
|
|
134
|
+
<button type="submit" data-i18n="form.calculate">Calculate results</button>
|
|
135
|
+
<button type="reset" id="resetButton" class="secondary" data-i18n="form.clear">Clear fields</button>
|
|
139
136
|
</div>
|
|
140
137
|
<p id="feedback" class="feedback" role="alert" aria-live="polite"></p>
|
|
141
138
|
</form>
|
|
@@ -144,59 +141,340 @@
|
|
|
144
141
|
<section aria-labelledby="results-title" class="panel panel-results results-section">
|
|
145
142
|
<div class="panel-heading panel-heading-inline">
|
|
146
143
|
<div>
|
|
147
|
-
<p class="panel-kicker">Output</p>
|
|
148
|
-
<h2 id="results-title">Results</h2>
|
|
149
|
-
<p class="help-text">
|
|
150
|
-
Values are shown with 95% confidence intervals
|
|
144
|
+
<p class="panel-kicker" data-i18n="results.kicker">Output</p>
|
|
145
|
+
<h2 id="results-title" data-i18n="results.h">Results</h2>
|
|
146
|
+
<p class="help-text" data-i18n="results.help">
|
|
147
|
+
Values are shown with 95% confidence intervals where applicable.
|
|
151
148
|
</p>
|
|
152
149
|
</div>
|
|
153
150
|
<div class="results-actions" role="group">
|
|
154
|
-
<button type="button" id="printButton" class="secondary">Print report</button>
|
|
151
|
+
<button type="button" id="printButton" class="secondary" data-i18n="results.print">Print report</button>
|
|
155
152
|
</div>
|
|
156
153
|
</div>
|
|
157
|
-
<div id="
|
|
154
|
+
<div id="biasWarnings" class="bias-warnings" role="status" aria-live="polite"></div>
|
|
155
|
+
<div id="resultsHeadline" class="results-headline" aria-live="polite" tabindex="-1"></div>
|
|
156
|
+
<div id="bayesianUpdate" class="bayesian-update" aria-live="polite"></div>
|
|
157
|
+
<div id="resultsSecondary" class="results-secondary" aria-live="polite"></div>
|
|
158
158
|
<div id="faganNomogram" class="fagan-nomogram" style="display: none;">
|
|
159
|
-
<h3>Fagan Nomogram</h3>
|
|
160
|
-
<p class="help-text">
|
|
161
|
-
<canvas id="faganCanvas" width="600" height="400" aria-label="Fagan nomogram"></canvas>
|
|
159
|
+
<h3 data-i18n="fagan.h">Fagan Nomogram</h3>
|
|
160
|
+
<p class="help-text" data-i18n="fagan.help">Calibrated logarithmic axes: pre-test odds × LR = post-test odds. Off-scale or undefined values are shown in the summary.</p>
|
|
161
|
+
<canvas id="faganCanvas" width="600" height="400" role="img" aria-label="Fagan nomogram" aria-describedby="faganSummary" data-i18n-attr="aria-label=attr.fagan.nomogram"></canvas>
|
|
162
|
+
<dl id="faganSummary" class="fagan-summary">
|
|
163
|
+
<div class="fagan-summary-item">
|
|
164
|
+
<dt data-i18n="fagan.pre">Pre-test</dt>
|
|
165
|
+
<dd id="faganPreValue">—</dd>
|
|
166
|
+
</div>
|
|
167
|
+
<div class="fagan-summary-item">
|
|
168
|
+
<dt data-i18n="fagan.lrpos">LR+</dt>
|
|
169
|
+
<dd id="faganLrPosValue">—</dd>
|
|
170
|
+
</div>
|
|
171
|
+
<div class="fagan-summary-item">
|
|
172
|
+
<dt data-i18n="fagan.lrneg">LR−</dt>
|
|
173
|
+
<dd id="faganLrNegValue">—</dd>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="fagan-summary-item">
|
|
176
|
+
<dt data-i18n="fagan.postpos">Post-test (+)</dt>
|
|
177
|
+
<dd id="faganPostPosValue">—</dd>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="fagan-summary-item">
|
|
180
|
+
<dt data-i18n="fagan.postneg">Post-test (−)</dt>
|
|
181
|
+
<dd id="faganPostNegValue">—</dd>
|
|
182
|
+
</div>
|
|
183
|
+
</dl>
|
|
162
184
|
</div>
|
|
163
|
-
<
|
|
185
|
+
<details id="sequentialTest" class="sequential-test" style="display: none;">
|
|
186
|
+
<summary><strong><span data-i18n="chain.title">Chain a second test</span></strong></summary>
|
|
187
|
+
<p class="help-text">
|
|
188
|
+
<span data-i18n="chain.help">Real diagnostic workflows often chain tests (e.g. D-dimer → CT-PA, rapid antigen → PCR, screen → confirm). Choose which result of the first test you are following up on, then enter the second test's confusion matrix. The second test's pre-test probability is set automatically.</span>
|
|
189
|
+
</p>
|
|
190
|
+
<div class="sequential-controls">
|
|
191
|
+
<label for="sequentialFrom">
|
|
192
|
+
<span><span data-i18n="chain.follow">Follow up on test 1 result</span></span>
|
|
193
|
+
<select id="sequentialFrom">
|
|
194
|
+
<option value="positive" data-i18n="chain.positive">Positive (use post-test (+) as pre-test for test 2)</option>
|
|
195
|
+
<option value="negative" data-i18n="chain.negative">Negative (use post-test (−) as pre-test for test 2)</option>
|
|
196
|
+
</select>
|
|
197
|
+
</label>
|
|
198
|
+
<p class="sequential-pretest" id="sequentialPretest"></p>
|
|
199
|
+
</div>
|
|
200
|
+
<fieldset class="confusion-matrix sequential-matrix">
|
|
201
|
+
<legend><span data-i18n="chain.matrix">Test 2 — confusion matrix</span></legend>
|
|
202
|
+
<div class="grid-container" role="group" aria-label="Diagnostic test 2 results" data-i18n-attr="aria-label=attr.diagnostic.test.2.results">
|
|
203
|
+
<span class="empty" aria-hidden="true"></span>
|
|
204
|
+
<span class="grid-header"><span data-i18n="matrix.disease">Disease +</span></span>
|
|
205
|
+
<span class="grid-header"><span data-i18n="matrix.disease.150">Disease −</span></span>
|
|
206
|
+
<label class="grid-label" for="tp2"><span data-i18n="matrix.test">Test +</span></label>
|
|
207
|
+
<div class="grid-item">
|
|
208
|
+
<input type="number" id="tp2" inputmode="numeric" min="0" step="1" placeholder="TP" aria-label="Test 2 true positives" data-i18n-attr="placeholder=attr.tp;aria-label=attr.test.2.true.positives" />
|
|
209
|
+
</div>
|
|
210
|
+
<div class="grid-item">
|
|
211
|
+
<input type="number" id="fp2" inputmode="numeric" min="0" step="1" placeholder="FP" aria-label="Test 2 false positives" data-i18n-attr="placeholder=attr.fp;aria-label=attr.test.2.false.positives" />
|
|
212
|
+
</div>
|
|
213
|
+
<label class="grid-label" for="fn2"><span data-i18n="matrix.test.152">Test −</span></label>
|
|
214
|
+
<div class="grid-item">
|
|
215
|
+
<input type="number" id="fn2" inputmode="numeric" min="0" step="1" placeholder="FN" aria-label="Test 2 false negatives" data-i18n-attr="placeholder=attr.fn;aria-label=attr.test.2.false.negatives" />
|
|
216
|
+
</div>
|
|
217
|
+
<div class="grid-item">
|
|
218
|
+
<input type="number" id="tn2" inputmode="numeric" min="0" step="1" placeholder="TN" aria-label="Test 2 true negatives" data-i18n-attr="placeholder=attr.tn;aria-label=attr.test.2.true.negatives" />
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
</fieldset>
|
|
222
|
+
<div class="form-actions">
|
|
223
|
+
<button type="button" id="sequentialCalc"><span data-i18n="chain.calculate">Calculate test 2 result</span></button>
|
|
224
|
+
<button type="button" id="sequentialClear" class="secondary"><span data-i18n="chain.clear">Clear test 2</span></button>
|
|
225
|
+
</div>
|
|
226
|
+
<p id="sequentialFeedback" class="feedback" role="alert" aria-live="polite"></p>
|
|
227
|
+
<div id="sequentialResults" class="results-grid sequential-results" aria-live="polite"></div>
|
|
228
|
+
</details>
|
|
229
|
+
<details id="thresholdPanel" class="threshold-panel" style="display: none;">
|
|
230
|
+
<summary><strong><span data-i18n="threshold.title">Decision thresholds</span></strong></summary>
|
|
231
|
+
<p class="help-text">
|
|
232
|
+
<span data-i18n="threshold.help1">Pauker–Kassirer (1980) framework. Set the treatment threshold (the post-test probability at which you would act). Below the</span> <em><span data-i18n="threshold.help2">testing</span></em> <span data-i18n="threshold.help3">threshold even a positive result keeps you below the action level — testing changes nothing. Above the</span> <em><span data-i18n="threshold.help4">test-treatment</span></em> <span data-i18n="threshold.help5">threshold even a negative result keeps you above the action level — just treat. The "test is useful" zone is the interval between them.</span>
|
|
233
|
+
</p>
|
|
234
|
+
<div class="threshold-input">
|
|
235
|
+
<label for="treatmentThresholdInput">
|
|
236
|
+
<span><span data-i18n="threshold.treatment">Treatment threshold</span></span>
|
|
237
|
+
<output id="treatmentThresholdValue" for="treatmentThresholdInput">30%</output>
|
|
238
|
+
</label>
|
|
239
|
+
<input type="range" id="treatmentThresholdInput" min="1" max="99" step="1" value="30" />
|
|
240
|
+
</div>
|
|
241
|
+
<div id="thresholdGauge" class="threshold-gauge" aria-hidden="true">
|
|
242
|
+
<div class="threshold-zone threshold-zone-no"></div>
|
|
243
|
+
<div class="threshold-zone threshold-zone-test"></div>
|
|
244
|
+
<div class="threshold-zone threshold-zone-treat"></div>
|
|
245
|
+
<div class="threshold-marker threshold-marker-pt" id="thresholdMarkerPt"></div>
|
|
246
|
+
<div class="threshold-marker threshold-marker-pre" id="thresholdMarkerPre"></div>
|
|
247
|
+
</div>
|
|
248
|
+
<div class="threshold-legend">
|
|
249
|
+
<span><span class="threshold-swatch threshold-zone-no"></span><span data-i18n="threshold.no">Don't test</span></span>
|
|
250
|
+
<span><span class="threshold-swatch threshold-zone-test"></span><span data-i18n="threshold.useful">Test useful</span></span>
|
|
251
|
+
<span><span class="threshold-swatch threshold-zone-treat"></span><span data-i18n="threshold.treat">Treat without testing</span></span>
|
|
252
|
+
<span><span class="threshold-swatch threshold-swatch-pt"></span><span data-i18n="threshold.pt">Pt (treatment threshold)</span></span>
|
|
253
|
+
<span><span class="threshold-swatch threshold-swatch-pre"></span><span data-i18n="threshold.current">Current pre-test</span></span>
|
|
254
|
+
</div>
|
|
255
|
+
<div id="thresholdReadout" class="threshold-readout"></div>
|
|
256
|
+
</details>
|
|
257
|
+
<details id="rocPanel" class="roc-panel" style="display: none;">
|
|
258
|
+
<summary><strong><span data-i18n="roc.title">Build an ROC curve</span></strong></summary>
|
|
259
|
+
<p class="help-text">
|
|
260
|
+
<span data-i18n="roc.help">For a continuous test (e.g. troponin, D-dimer, score), each candidate cutoff defines its own 2×2 table. Add one row per cutoff and the plot draws sensitivity vs (1 − specificity), the trapezoidal AUC, and highlights the cutoff with the largest Youden's J (sens + spec − 1).</span>
|
|
261
|
+
</p>
|
|
262
|
+
<div class="roc-table-wrapper">
|
|
263
|
+
<label for="rocDirection"><span data-i18n="roc.direction">Positive result direction</span></label>
|
|
264
|
+
<select id="rocDirection"><option value="higher" data-i18n="roc.higher">Higher scores are positive</option><option value="lower" data-i18n="roc.lower">Lower scores are positive</option></select>
|
|
265
|
+
<table class="roc-table">
|
|
266
|
+
<thead>
|
|
267
|
+
<tr>
|
|
268
|
+
<th scope="col"><span data-i18n="roc.cutoff">Cutoff</span></th>
|
|
269
|
+
<th scope="col"><span data-i18n="matrix.tp">TP</span></th>
|
|
270
|
+
<th scope="col">FP</th>
|
|
271
|
+
<th scope="col">FN</th>
|
|
272
|
+
<th scope="col"><span data-i18n="matrix.tn">TN</span></th>
|
|
273
|
+
<th scope="col" aria-label="Remove row" data-i18n-attr="aria-label=attr.remove.row"></th>
|
|
274
|
+
</tr>
|
|
275
|
+
</thead>
|
|
276
|
+
<tbody id="rocRows"></tbody>
|
|
277
|
+
</table>
|
|
278
|
+
</div>
|
|
279
|
+
<div class="form-actions">
|
|
280
|
+
<button type="button" id="rocAddRow"><span data-i18n="roc.add">+ Add cutoff row</span></button>
|
|
281
|
+
<button type="button" id="rocFillSynthetic" class="secondary" title="Fit an illustrative equal-variance binormal model and generate seven simulated cutoffs." data-i18n-attr="title=attr.fit.an.illustrative.equal.variance.binormal.model.and"><span data-i18n="roc.fill">+ Fill synthetic cutoffs</span></button>
|
|
282
|
+
<button type="button" id="rocReset" class="secondary"><span data-i18n="roc.reset">Reset rows</span></button>
|
|
283
|
+
</div>
|
|
284
|
+
<canvas id="rocCanvas" width="480" height="480" role="img" aria-label="Receiver operating characteristic curve" aria-describedby="rocReadout" data-i18n-attr="aria-label=attr.receiver.operating.characteristic.curve"></canvas>
|
|
285
|
+
<div id="rocReadout" class="roc-readout"></div>
|
|
286
|
+
</details>
|
|
287
|
+
<details id="historyPanel" class="history-panel" style="display: none;">
|
|
288
|
+
<summary><strong><span data-i18n="history.title">History</span></strong></summary>
|
|
289
|
+
<p class="help-text">
|
|
290
|
+
<span data-i18n="history.help1">Recent Calculate runs are saved in your browser. Click</span> <em><span data-i18n="history.reload">Reload</span></em> <span data-i18n="history.help2">to drop a case back into the form, tick two or more boxes to compare them side by side, or</span> <em><span data-i18n="history.clear">Clear all</span></em> <span data-i18n="history.help3">to wipe local storage. Nothing leaves this device.</span>
|
|
291
|
+
</p>
|
|
292
|
+
<div id="historyCompare" class="history-compare" aria-live="polite"></div>
|
|
293
|
+
<ul id="historyList" class="history-list" aria-label="Saved calculations" data-i18n-attr="aria-label=attr.saved.calculations"></ul>
|
|
294
|
+
<div class="form-actions">
|
|
295
|
+
<button type="button" id="historyClearAll" class="secondary"><span data-i18n="history.clearall">Clear all history</span></button>
|
|
296
|
+
</div>
|
|
297
|
+
</details>
|
|
298
|
+
<details id="prevalenceExplorer" class="prevalence-explorer" style="display: none;">
|
|
299
|
+
<summary><strong><span data-i18n="prevalence.title">Explore prevalence sensitivity</span></strong></summary>
|
|
300
|
+
<p class="help-text">
|
|
301
|
+
<span data-i18n="prevalence.help">Sensitivity and specificity are intrinsic to the test and stay fixed. Drag the slider to see how the predictive values and post-test probabilities respond if the disease were more or less common in the population you are testing.</span>
|
|
302
|
+
</p>
|
|
303
|
+
<div class="prevalence-controls">
|
|
304
|
+
<label for="prevalenceSlider">
|
|
305
|
+
<span><span data-i18n="prevalence.label">Prevalence</span></span>
|
|
306
|
+
<output id="prevalenceValue" for="prevalenceSlider">10.0%</output>
|
|
307
|
+
</label>
|
|
308
|
+
<input type="range" id="prevalenceSlider" min="0.1" max="100" step="0.1" value="10" aria-describedby="prevalenceValue" />
|
|
309
|
+
</div>
|
|
310
|
+
<div id="prevalenceReadout" class="prevalence-readout"></div>
|
|
311
|
+
<canvas id="prevalenceCanvas" width="600" height="320" aria-label="PPV and NPV as a function of prevalence" data-i18n-attr="aria-label=attr.ppv.and.npv.as.a.function.of.prevalence"></canvas>
|
|
312
|
+
</details>
|
|
313
|
+
<details id="thresholdDemo" class="threshold-demo" style="display: none;">
|
|
314
|
+
<summary><strong data-i18n="thresholdDemo.summary">Threshold explorer</strong></summary>
|
|
315
|
+
<p class="help-text" data-i18n="thresholdDemo.help">
|
|
316
|
+
A continuous test produces scores for 200 individuals: 80 diseased (higher scores) and 120 healthy (lower scores), with overlap. Move the threshold slider to see how the confusion matrix and all performance metrics change in real time. There is no perfect threshold — only trade-offs.
|
|
317
|
+
</p>
|
|
318
|
+
<canvas id="thresholdDemoCanvas" width="600" height="200" role="img" aria-label="Score distribution with threshold line" data-i18n-attr="aria-label=attr.score.distribution.with.threshold.line"></canvas>
|
|
319
|
+
<div class="threshold-demo-controls">
|
|
320
|
+
<label for="thresholdDemoSlider">
|
|
321
|
+
<span data-i18n="thresholdDemo.threshold">Threshold</span>
|
|
322
|
+
<output id="thresholdDemoValue" for="thresholdDemoSlider">0.50</output>
|
|
323
|
+
</label>
|
|
324
|
+
<input type="range" id="thresholdDemoSlider" min="0" max="100" step="1" value="50" aria-describedby="thresholdDemoValue" />
|
|
325
|
+
</div>
|
|
326
|
+
<div id="thresholdDemoStats" class="threshold-demo-stats" aria-live="polite">
|
|
327
|
+
<div class="threshold-demo-stat">
|
|
328
|
+
<div class="threshold-demo-stat-label" data-i18n="thresholdDemo.sens">Sensitivity</div>
|
|
329
|
+
<div class="threshold-demo-stat-value" id="tdSens">—</div>
|
|
330
|
+
<div class="threshold-demo-stat-sub"><span data-i18n="matrix.tp.tp.fn">TP / (TP + FN)</span></div>
|
|
331
|
+
</div>
|
|
332
|
+
<div class="threshold-demo-stat">
|
|
333
|
+
<div class="threshold-demo-stat-label" data-i18n="thresholdDemo.spec">Specificity</div>
|
|
334
|
+
<div class="threshold-demo-stat-value" id="tdSpec">—</div>
|
|
335
|
+
<div class="threshold-demo-stat-sub"><span data-i18n="matrix.tn.tn.fp">TN / (TN + FP)</span></div>
|
|
336
|
+
</div>
|
|
337
|
+
<div class="threshold-demo-stat">
|
|
338
|
+
<div class="threshold-demo-stat-label" data-i18n="thresholdDemo.ppv">PPV</div>
|
|
339
|
+
<div class="threshold-demo-stat-value" id="tdPpv">—</div>
|
|
340
|
+
<div class="threshold-demo-stat-sub"><span data-i18n="matrix.tp.tp.fp">TP / (TP + FP)</span></div>
|
|
341
|
+
</div>
|
|
342
|
+
<div class="threshold-demo-stat">
|
|
343
|
+
<div class="threshold-demo-stat-label" data-i18n="thresholdDemo.npv">NPV</div>
|
|
344
|
+
<div class="threshold-demo-stat-value" id="tdNpv">—</div>
|
|
345
|
+
<div class="threshold-demo-stat-sub"><span data-i18n="matrix.tn.tn.fn">TN / (TN + FN)</span></div>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
<div class="threshold-demo-matrix" aria-label="Confusion matrix at current threshold" data-i18n-attr="aria-label=attr.confusion.matrix.at.current.threshold">
|
|
349
|
+
<div class="threshold-demo-cell head"></div>
|
|
350
|
+
<div class="threshold-demo-cell head" data-i18n="thresholdDemo.disease"><span data-i18n="matrix.disease">Disease +</span></div>
|
|
351
|
+
<div class="threshold-demo-cell head" data-i18n="thresholdDemo.healthy"><span data-i18n="matrix.disease.150">Disease −</span></div>
|
|
352
|
+
<div class="threshold-demo-cell head" data-i18n="thresholdDemo.testpos"><span data-i18n="matrix.test">Test +</span></div>
|
|
353
|
+
<div class="threshold-demo-cell tp">
|
|
354
|
+
<div class="threshold-demo-cell-n" id="tdTp">0</div>
|
|
355
|
+
<div class="threshold-demo-cell-meta" data-i18n="thresholdDemo.tp">True positives</div>
|
|
356
|
+
</div>
|
|
357
|
+
<div class="threshold-demo-cell fp">
|
|
358
|
+
<div class="threshold-demo-cell-n" id="tdFp">0</div>
|
|
359
|
+
<div class="threshold-demo-cell-meta" data-i18n="thresholdDemo.fp">False positives</div>
|
|
360
|
+
</div>
|
|
361
|
+
<div class="threshold-demo-cell head" data-i18n="thresholdDemo.testneg"><span data-i18n="matrix.test.152">Test −</span></div>
|
|
362
|
+
<div class="threshold-demo-cell fn">
|
|
363
|
+
<div class="threshold-demo-cell-n" id="tdFn">0</div>
|
|
364
|
+
<div class="threshold-demo-cell-meta" data-i18n="thresholdDemo.fn">False negatives</div>
|
|
365
|
+
</div>
|
|
366
|
+
<div class="threshold-demo-cell tn">
|
|
367
|
+
<div class="threshold-demo-cell-n" id="tdTn">0</div>
|
|
368
|
+
<div class="threshold-demo-cell-meta" data-i18n="thresholdDemo.tn">True negatives</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
<p id="thresholdDemoNote" class="threshold-demo-note" aria-live="polite"></p>
|
|
372
|
+
</details>
|
|
164
373
|
</section>
|
|
165
374
|
</div>
|
|
166
375
|
|
|
167
376
|
<div class="resource-grid">
|
|
377
|
+
<section aria-labelledby="kappa-title" class="panel panel-docs kappa-panel">
|
|
378
|
+
<div class="panel-heading">
|
|
379
|
+
<p class="panel-kicker"><span data-i18n="kappa.kicker">Companion tool</span></p>
|
|
380
|
+
<h2 id="kappa-title"><span data-i18n="kappa.title">Inter-rater agreement (Cohen's κ)</span></h2>
|
|
381
|
+
<p class="help-text">
|
|
382
|
+
<span data-i18n="kappa.help">Two raters classify the same items as positive / negative. Enter the four counts to get Cohen's κ with 95% asymptotic CI adjusted for estimated rater marginals and a Landis–Koch qualitative label. Useful for imaging or pathology cases where the "test" is a human judgement.</span>
|
|
383
|
+
</p>
|
|
384
|
+
</div>
|
|
385
|
+
<fieldset class="confusion-matrix kappa-matrix">
|
|
386
|
+
<legend><span data-i18n="kappa.matrix">Rater 1 × Rater 2</span></legend>
|
|
387
|
+
<div class="grid-container" role="group" aria-label="Inter-rater agreement matrix" data-i18n-attr="aria-label=attr.inter.rater.agreement.matrix">
|
|
388
|
+
<span class="empty" aria-hidden="true"></span>
|
|
389
|
+
<span class="grid-header"><span data-i18n="kappa.r2pos">Rater 2 +</span></span>
|
|
390
|
+
<span class="grid-header"><span data-i18n="kappa.r2neg">Rater 2 −</span></span>
|
|
391
|
+
<label class="grid-label" for="kappaBothPos"><span data-i18n="kappa.r1pos">Rater 1 +</span></label>
|
|
392
|
+
<div class="grid-item">
|
|
393
|
+
<input type="number" id="kappaBothPos" inputmode="numeric" min="0" step="1" placeholder="++" aria-label="Both raters positive" data-i18n-attr="placeholder=attr.;aria-label=attr.both.raters.positive" />
|
|
394
|
+
</div>
|
|
395
|
+
<div class="grid-item">
|
|
396
|
+
<input type="number" id="kappaOnly1Pos" inputmode="numeric" min="0" step="1" placeholder="+−" aria-label="Only rater 1 positive" data-i18n-attr="placeholder=attr..18;aria-label=attr.only.rater.1.positive" />
|
|
397
|
+
</div>
|
|
398
|
+
<label class="grid-label" for="kappaOnly2Pos"><span data-i18n="kappa.r1neg">Rater 1 −</span></label>
|
|
399
|
+
<div class="grid-item">
|
|
400
|
+
<input type="number" id="kappaOnly2Pos" inputmode="numeric" min="0" step="1" placeholder="−+" aria-label="Only rater 2 positive" data-i18n-attr="placeholder=attr..19;aria-label=attr.only.rater.2.positive" />
|
|
401
|
+
</div>
|
|
402
|
+
<div class="grid-item">
|
|
403
|
+
<input type="number" id="kappaBothNeg" inputmode="numeric" min="0" step="1" placeholder="−−" aria-label="Both raters negative" data-i18n-attr="placeholder=attr..17;aria-label=attr.both.raters.negative" />
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</fieldset>
|
|
407
|
+
<div class="form-actions">
|
|
408
|
+
<button type="button" id="kappaCalc"><span data-i18n="kappa.calculate">Compute κ</span></button>
|
|
409
|
+
<button type="button" id="kappaClear" class="secondary"><span data-i18n="clear">Clear</span></button>
|
|
410
|
+
</div>
|
|
411
|
+
<div id="kappaResult" class="kappa-result" aria-live="polite"></div>
|
|
412
|
+
</section>
|
|
168
413
|
<section aria-labelledby="interpretation-title" class="interpretation panel panel-docs">
|
|
169
|
-
<h2 id="interpretation-title">Interpretation guide</h2>
|
|
414
|
+
<h2 id="interpretation-title"><span data-i18n="guide.title">Interpretation guide</span></h2>
|
|
170
415
|
<details>
|
|
171
|
-
<summary>Sensitivity and specificity</summary>
|
|
416
|
+
<summary><span data-i18n="guide.sens">Sensitivity and specificity</span></summary>
|
|
172
417
|
<p>
|
|
173
|
-
Quantify the test's ability to correctly identify people with and without the condition. Useful for understanding the intrinsic performance of the test. Learn more at <a href="https://en.wikipedia.org/wiki/Sensitivity_and_specificity" target="_blank" rel="noopener noreferrer">Wikipedia</a>.
|
|
418
|
+
<span data-i18n="guide.sens.help">Quantify the test's ability to correctly identify people with and without the condition. Useful for understanding the intrinsic performance of the test. Learn more at</span> <a href="https://en.wikipedia.org/wiki/Sensitivity_and_specificity" target="_blank" rel="noopener noreferrer">Wikipedia</a>.
|
|
174
419
|
</p>
|
|
175
420
|
</details>
|
|
176
421
|
<details>
|
|
177
|
-
<summary>Predictive values</summary>
|
|
422
|
+
<summary><span data-i18n="guide.predictive">Predictive values</span></summary>
|
|
178
423
|
<p>
|
|
179
|
-
Depend on prevalence (pre-test probability). Use them to estimate the probability that a person has or does not have the disease after a positive or negative result. See <a href="https://en.wikipedia.org/wiki/Positive_and_negative_predictive_values" target="_blank" rel="noopener noreferrer">Wikipedia</a> for further reading
|
|
424
|
+
<span data-i18n="guide.predictive.help">Depend on prevalence (pre-test probability). Use them to estimate the probability that a person has or does not have the disease after a positive or negative result. See</span> <a href="https://en.wikipedia.org/wiki/Positive_and_negative_predictive_values" target="_blank" rel="noopener noreferrer">Wikipedia</a> <span data-i18n="guide.readmore">for further reading.</span>
|
|
180
425
|
</p>
|
|
181
426
|
</details>
|
|
182
427
|
<details>
|
|
183
|
-
<summary>Likelihood ratios</summary>
|
|
428
|
+
<summary><span data-i18n="guide.lr">Likelihood ratios</span></summary>
|
|
184
429
|
<p>
|
|
185
|
-
Relate the probability of a result in diseased vs. non-diseased individuals and can be applied directly in Bayes' formula to update the probability of disease. Further reference at <a href="https://en.wikipedia.org/wiki/Likelihood_ratio" target="_blank" rel="noopener noreferrer">Wikipedia</a>.
|
|
430
|
+
<span data-i18n="guide.lr.help">Relate the probability of a result in diseased vs. non-diseased individuals and can be applied directly in Bayes' formula to update the probability of disease. Further reference at</span> <a href="https://en.wikipedia.org/wiki/Likelihood_ratio" target="_blank" rel="noopener noreferrer">Wikipedia</a>.
|
|
186
431
|
</p>
|
|
187
432
|
</details>
|
|
188
433
|
<details>
|
|
189
|
-
<summary>Post-test probabilities</summary>
|
|
434
|
+
<summary><span data-i18n="guide.post">Post-test probabilities</span></summary>
|
|
190
435
|
<p>
|
|
191
|
-
Result from applying the LR to pre-test odds. They are crucial for clinical decisions such as initiating treatment or ordering additional tests. See also <a href="https://en.wikipedia.org/wiki/Posterior_probability" target="_blank" rel="noopener noreferrer">Wikipedia</a>.
|
|
436
|
+
<span data-i18n="guide.post.help">Result from applying the LR to pre-test odds. They are crucial for clinical decisions such as initiating treatment or ordering additional tests. See also</span> <a href="https://en.wikipedia.org/wiki/Posterior_probability" target="_blank" rel="noopener noreferrer">Wikipedia</a>.
|
|
192
437
|
</p>
|
|
193
438
|
</details>
|
|
194
439
|
</section>
|
|
195
440
|
|
|
441
|
+
<section aria-labelledby="stard-title" class="panel panel-docs stard-panel">
|
|
442
|
+
<div class="panel-heading">
|
|
443
|
+
<p class="panel-kicker"><span data-i18n="stard.kicker">Critical appraisal</span></p>
|
|
444
|
+
<h2 id="stard-title"><span data-i18n="stard.title">STARD essentials</span></h2>
|
|
445
|
+
<p class="help-text">
|
|
446
|
+
<span data-i18n="stard.intro1">Numbers from a 2×2 only mean what the underlying study allows them to mean. The</span> <a href="https://www.equator-network.org/reporting-guidelines/stard/" target="_blank" rel="noopener noreferrer"><span data-i18n="stard.link">STARD 2015 checklist</span></a> <span data-i18n="stard.intro2">has 30 items; six of them dominate the interpretation of sens / spec / PPV / NPV.</span>
|
|
447
|
+
</p>
|
|
448
|
+
</div>
|
|
449
|
+
<details>
|
|
450
|
+
<summary><span data-i18n="stard.1">1. Reference standard — what counted as "truth"?</span></summary>
|
|
451
|
+
<p><span data-i18n="stard.1a">If the gold standard is imperfect (e.g. expert consensus instead of biopsy), sensitivity and specificity are biased. Worse: if the reference standard incorporates the index test result, agreement is inflated (</span><em><span data-i18n="stard.1b">incorporation bias</span></em><span data-i18n="stard.1c">). Ask which standard was used and whether the index test fed into it.</span></p>
|
|
452
|
+
</details>
|
|
453
|
+
<details>
|
|
454
|
+
<summary><span data-i18n="stard.2">2. Spectrum of patients — who got tested?</span></summary>
|
|
455
|
+
<p><span data-i18n="stard.2a">Performance estimated in obvious cases ("sick" vs healthy volunteers) almost always overestimates real-world accuracy. Check whether enrolment was</span> <em><span data-i18n="stard.2b">consecutive</span></em> <span data-i18n="stard.or">or</span> <em><span data-i18n="stard.2c">random</span></em><span data-i18n="stard.2d">, and whether it spanned the clinical spectrum you'll apply the test to (mild, atypical, comorbid).</span></p>
|
|
456
|
+
</details>
|
|
457
|
+
<details>
|
|
458
|
+
<summary><span data-i18n="stard.3">3. Verification — was the reference standard applied to everyone?</span></summary>
|
|
459
|
+
<p><em><span data-i18n="stard.3a">Partial verification bias</span></em> <span data-i18n="stard.3b">arises when positive-index-test patients are more likely to receive the gold standard than negative ones — inflates sensitivity, deflates specificity. Look for differential or post-hoc verification protocols.</span></p>
|
|
460
|
+
</details>
|
|
461
|
+
<details>
|
|
462
|
+
<summary><span data-i18n="stard.4">4. Blinding — were readers independent?</span></summary>
|
|
463
|
+
<p><span data-i18n="stard.4a">If the person interpreting the index test also knew the reference result (or vice versa), expect</span> <em><span data-i18n="stard.4b">review bias</span></em><span data-i18n="stard.4c">: estimates drift toward perfect agreement. Imaging and pathology studies should explicitly report blinding between readers and from clinical context.</span></p>
|
|
464
|
+
</details>
|
|
465
|
+
<details>
|
|
466
|
+
<summary><span data-i18n="stard.5">5. Indeterminate results — where did the "?"s go?</span></summary>
|
|
467
|
+
<p><span data-i18n="stard.5a">If indeterminate, missing, or excluded results were silently dropped, the 2×2 misrepresents real practice. Ask how many results fell outside the binary scheme and how they were handled (re-tested, excluded, counted as negative…).</span></p>
|
|
468
|
+
</details>
|
|
469
|
+
<details>
|
|
470
|
+
<summary><span data-i18n="stard.6">6. Subgroups — does one number describe everyone?</span></summary>
|
|
471
|
+
<p><span data-i18n="stard.6a">Sensitivity / specificity often vary by age, severity, prior testing, or setting. A single 2×2 hides those differences. Check whether the authors pre-specified clinically meaningful subgroups and reported them separately.</span></p>
|
|
472
|
+
</details>
|
|
473
|
+
</section>
|
|
196
474
|
<section aria-labelledby="references-title" class="references panel panel-docs">
|
|
197
|
-
<h2 id="references-title">Resources and references</h2>
|
|
475
|
+
<h2 id="references-title"><span data-i18n="references.title">Resources and references</span></h2>
|
|
198
476
|
<details>
|
|
199
|
-
<summary>Supporting literature</summary>
|
|
477
|
+
<summary><span data-i18n="references.literature">Supporting literature</span></summary>
|
|
200
478
|
<ul class="reference-list">
|
|
201
479
|
<li>
|
|
202
480
|
<strong>Akobeng AK.</strong> Understanding diagnostic tests 1-3: sensitivity, specificity, and predictive values.
|
|
@@ -216,52 +494,86 @@
|
|
|
216
494
|
</ul>
|
|
217
495
|
</details>
|
|
218
496
|
<details>
|
|
219
|
-
<summary>Online resources</summary>
|
|
497
|
+
<summary><span data-i18n="references.online">Online resources</span></summary>
|
|
220
498
|
<ul class="reference-list">
|
|
221
499
|
<li>
|
|
222
500
|
<strong>The Centre for Evidence-Based Medicine (CEBM):</strong>
|
|
223
|
-
<a href="https://www.cebm.ox.ac.uk/resources/ebm-tools" target="_blank" rel="noopener noreferrer">EBM Tools and Resources</a>
|
|
501
|
+
<a href="https://www.cebm.ox.ac.uk/resources/ebm-tools" target="_blank" rel="noopener noreferrer"><span data-i18n="references.ebm">EBM Tools and Resources</span></a>
|
|
224
502
|
</li>
|
|
225
503
|
<li>
|
|
226
504
|
<strong>StatPearls:</strong>
|
|
227
|
-
<a href="https://www.ncbi.nlm.nih.gov/books/NBK557491/" target="_blank" rel="noopener noreferrer">Sensitivity and Specificity</a>
|
|
505
|
+
<a href="https://www.ncbi.nlm.nih.gov/books/NBK557491/" target="_blank" rel="noopener noreferrer"><span data-i18n="references.sens">Sensitivity and Specificity</span></a>
|
|
228
506
|
</li>
|
|
229
507
|
<li>
|
|
230
508
|
<strong>Deeks JJ, Altman DG.</strong> Diagnostic tests 4: likelihood ratios.
|
|
231
509
|
<em>BMJ.</em> 2004;329(7458):168-169.
|
|
232
|
-
<a href="https://www.bmj.com/content/329/7458/168" target="_blank" rel="noopener noreferrer">Open access</a>
|
|
510
|
+
<a href="https://www.bmj.com/content/329/7458/168" target="_blank" rel="noopener noreferrer"><span data-i18n="references.open">Open access</span></a>
|
|
233
511
|
</li>
|
|
234
512
|
</ul>
|
|
235
513
|
</details>
|
|
236
514
|
<details>
|
|
237
|
-
<summary>Complementary calculators</summary>
|
|
515
|
+
<summary><span data-i18n="references.calculators">Complementary calculators</span></summary>
|
|
238
516
|
<ul class="reference-list">
|
|
239
517
|
<li>
|
|
240
518
|
<strong>MDCalc:</strong>
|
|
241
|
-
<a href="https://www.mdcalc.com/" target="_blank" rel="noopener noreferrer">Medical calculators</a>
|
|
519
|
+
<a href="https://www.mdcalc.com/" target="_blank" rel="noopener noreferrer"><span data-i18n="references.medical">Medical calculators</span></a>
|
|
242
520
|
</li>
|
|
243
521
|
<li>
|
|
244
522
|
<strong>ClinCalc:</strong>
|
|
245
|
-
<a href="https://clincalc.com/Stats/SampleSize.aspx" target="_blank" rel="noopener noreferrer">Diagnostic Test Calculator</a>
|
|
523
|
+
<a href="https://clincalc.com/Stats/SampleSize.aspx" target="_blank" rel="noopener noreferrer"><span data-i18n="references.diagnostic">Diagnostic Test Calculator</span></a>
|
|
246
524
|
</li>
|
|
247
525
|
</ul>
|
|
248
526
|
</details>
|
|
249
527
|
</section>
|
|
528
|
+
<section aria-labelledby="about-title" class="panel panel-docs about-panel">
|
|
529
|
+
<div class="panel-heading">
|
|
530
|
+
<p class="panel-kicker" data-i18n="intro.tag">Educational tool</p>
|
|
531
|
+
<h2 id="about-title" data-i18n="about.h">About DIAGCALC</h2>
|
|
532
|
+
<p data-i18n="intro.lead">
|
|
533
|
+
Interactive calculator for exploring diagnostic test performance measures and applying Bayesian reasoning in clinical practice.
|
|
534
|
+
</p>
|
|
535
|
+
</div>
|
|
536
|
+
<div class="about-grid">
|
|
537
|
+
<article class="about-card">
|
|
538
|
+
<h3 data-i18n="intro.highlight1.h">Key measures</h3>
|
|
539
|
+
<p data-i18n="intro.highlight1.p">Sensitivity, specificity, PPV, NPV, and likelihood ratios with 95% CI.</p>
|
|
540
|
+
</article>
|
|
541
|
+
<article class="about-card">
|
|
542
|
+
<h3 data-i18n="intro.highlight2.h">Post-test probability</h3>
|
|
543
|
+
<p data-i18n="intro.highlight2.p">Convert pre-test probability into positive and negative post-test probability.</p>
|
|
544
|
+
</article>
|
|
545
|
+
<article class="about-card">
|
|
546
|
+
<h3 data-i18n="intro.highlight3.h">Guided interpretation</h3>
|
|
547
|
+
<p data-i18n="intro.highlight3.p">Results explained to support self-directed learning.</p>
|
|
548
|
+
</article>
|
|
549
|
+
</div>
|
|
550
|
+
</section>
|
|
250
551
|
</div>
|
|
251
552
|
</main>
|
|
252
553
|
<footer>
|
|
253
554
|
<p>
|
|
254
|
-
DIAGCALC is a diagnostic test performance calculator created for educational purposes and training of healthcare students
|
|
555
|
+
<span data-i18n="footer.education">DIAGCALC is a diagnostic test performance calculator created for educational purposes and training of healthcare students.</span>
|
|
255
556
|
</p>
|
|
256
557
|
<p>
|
|
257
|
-
Any analyses, results and conclusions may not be used, interpreted or generalised beyond this specific context
|
|
558
|
+
<span data-i18n="footer.scope">Any analyses, results and conclusions may not be used, interpreted or generalised beyond this specific context.</span>
|
|
559
|
+
</p>
|
|
560
|
+
<p class="privacy-note">
|
|
561
|
+
<span data-i18n="footer.privacy">🔒 All calculations run locally in your browser. No inputs, datasets, or results leave this device.</span>
|
|
258
562
|
</p>
|
|
259
563
|
<p>
|
|
260
|
-
|
|
564
|
+
<span data-i18n="footer.credit">© 2025 — MIT Licence — Developed by</span> <a href="https://tiagojct.eu">Tiago Jacinto</a>. <a href="mailto:tiagojacinto@med.up.pt"><span data-i18n="footer.contact">Contact</span></a>
|
|
261
565
|
</p>
|
|
262
566
|
</footer>
|
|
567
|
+
<script src="lib/diagcalc-i18n.js"></script>
|
|
263
568
|
<script src="lib/diagcalc-datasets.js"></script>
|
|
569
|
+
<script src="lib/diagcalc-presentation.js"></script>
|
|
264
570
|
<script src="lib/diagcalc-core.js"></script>
|
|
571
|
+
<script src="lib/diagcalc-meta.js"></script>
|
|
572
|
+
<script src="lib/diagcalc-case.js"></script>
|
|
573
|
+
<script src="lib/diagcalc-storage.js"></script>
|
|
574
|
+
<script src="lib/diagcalc-geometry.js"></script>
|
|
575
|
+
<script src="web/results.js"></script>
|
|
576
|
+
<script src="web/charts.js"></script>
|
|
265
577
|
<script src="script.js"></script>
|
|
266
578
|
</body>
|
|
267
579
|
</html>
|