flowchart LR
A["Check day, site, run,<br/>and replicate design"] --> B["precision()<br/>Create VC object"]
B --> C["outlier() / normal()<br/>Diagnostics"]
C --> D["variance() / vc()<br/>VC, SD, CV"]
D --> E["ci()<br/>Component and total intervals"]
E --> F["profile()<br/>Concentration-related precision"]
F --> G["report()<br/>EP05/EP15 terminology"]
G --> H["Compare with prespecified limits"]
13 Precision Analysis
Precision evaluates agreement among results obtained from the same measurement system under repeatable conditions, usually expressed as SD or CV%. CLSI EP05 separates within-run, between-run, between-day, and related variance components and reports their estimates and confidence intervals for comparison with acceptance limits.
This chapter demonstrates how to use precision() to build a variance-component analysis, perform outlier and normality checks, estimate variance components and intervals, fit a Sadler precision profile, and rearrange results into EP05-style tables.
Code
library(ivdtools)
library(readr)13.1 Main Functions
| Function | Main purpose | Key input or output |
|---|---|---|
precision() |
Create a precision variance-component object | Nested formula, by, balance checks |
outlier() |
Detect outliers by sample | Grubbs or IQR |
normal() |
Test normality by sample | Shapiro–Wilk and related methods |
variance() / vc() |
Estimate variance components | VC, % total, SD, CV%, NegVC |
ci() |
Confidence intervals for components and total SD/CV | Satterthwaite approximation |
profile() |
Fit a Sadler precision profile | Ten candidate models, AIC selection |
report() |
Generate EP05-structured tables | Explicit day, run, and site mapping |
list_sadler() |
View the ten Sadler models | Model formulas and types |
summary() / plot() |
Summaries and figures | plot(type=...) |
After creating an object, analysis results accumulate through outlier(), normal(), variance(), ci(), and profile(). Reassign the object after every call. Only completed analyses appear in summary(), and some plots require a preceding analysis.
Code
precision(data, form, by = NULL)
variance(x, digits = 4, method = c("auto", "anova", "reml"), NegVC = FALSE)
ci(x, digits = 4)
profile(object, model.no = 1:10)
report(x, day, run = NULL, site = NULL,
table = c("simple", "detailed", "CI"), digits = 4)precision() ultimately calls VCA::anovaVCA() and requires an ordinary data.frame; convert readr::read_csv() tibbles before use and convert design factors such as day, lot, and replicate to factors.
For a two-level nested design,
\[ Y_{drk}=\mu+D_d+R_{r(d)}+\varepsilon_{drk}, \]
with independent random terms of mean zero,
\[ \sigma_T^2=\sigma_D^2+\sigma_{R(D)}^2+\sigma_e^2,\qquad SD_T=\sqrt{\sigma_T^2},\qquad CV_T=100\,SD_T/\bar Y. \]
The right side of form must represent the actual random hierarchy; the replicate layer is usually the residual error. by fits different samples or concentrations separately and does not combine them into one common variance. With method="auto", VCA::isBalanced() selects anovaVCA() for balanced data and remlVCA() otherwise; estimates and degrees of freedom can differ, so preserve the actual Method in reports.
variance() stores numeric values in results[[sample]]$vc, sd_comp, and cv_comp; formatted display tables are separate. Use the numeric components for later calculations. Run ci() after variance(); it uses VCA::VCAinference() for two-sided SD/CV intervals. profile() fits \(SD=f(\bar Y;\theta)\) across at least three valid levels and selects a Sadler/VFP candidate by AIC; extrapolation outside observed concentrations requires caution. report() maps components to EP05 terminology and can calculate within-laboratory and reproducibility intervals from component covariances and Satterthwaite degrees of freedom.
13.2 Analysis Examples
13.2.1 EP05 20×2×2 Single-Sample Precision
Twenty days, two runs per day, and two replicates per run produce 80 results:
Code
ep05 <- read_csv("./data/013-精密度分析/precision-ep05.csv", show_col_types = FALSE)
ep05 <- as.data.frame(ep05)
ep05$day <- factor(ep05$day)
ep05$run <- factor(ep05$run)
ep05$rep <- factor(ep05$rep)
head(ep05, 6) day run rep value
1 1 1 1 96.94
2 1 1 2 93.48
3 1 2 1 99.28
4 1 2 2 101.54
5 2 1 1 102.96
6 2 1 2 102.85
Code
p <- precision(ep05, value ~ day/run)
p <- outlier(p, method = "grubbs")Outlier detection -- Grubbs (alpha = 0.05)
No outliers detected.
Code
p <- normal(p, method = "shapiro")Normality test -- Shapiro-Wilk
Sample n Method Statistic p-value
---------------------------------------------------------------------------
all 80 Shapiro-Wilk W=0.9868 0.5858
Code
p <- variance(p)Variance components
sample component VC %Total SD CV[%] Method
-----------------------------------------------------
all day 18.8358 42.6 4.3400 4.3788 anova
all day:run 17.4343 39.4 4.1754 4.2128 anova
all error 7.9482 18.0 2.8193 2.8445 anova
Code
p <- ci(p)Confidence intervals (SD)
sample component estimate lower upper
----------------------------------------
all total 6.6497 5.4288 8.5842
all day 4.3400 0.0000 6.2255
all day:run 4.1754 2.0129 5.5513
all error 2.8193 2.3147 3.6073
Confidence intervals (%CV)
sample component estimate lower upper
----------------------------------------
all total 6.7091 5.4773 8.6609
all day 4.3788 0.0000 6.2812
all day:run 4.2128 2.0309 5.6009
all error 2.8445 2.3353 3.6395
Code
summary(p)
Precision analysis -- summary
----------------------------------------
Precision analysis object
Data class : data.frame
Total rows : 80
Formula : value ~ day/run
Samples : 1
Factor levels
day : 1 (4), 2 (4), 3 (4), 4 (4), 5 (4), 6 (4), 7 (4), 8 (4), 9 (4), 10 (4), 11 (4), 12 (4), 13 (4), 14 (4), 15 (4), 16 (4), 17 (4), 18 (4), 19 (4), 20 (4)
run : 1 (40), 2 (40)
rep : 1 (40), 2 (40)
Analysis status
[x] outlier
[x] normal
[x] variance
[x] ci
[ ] profile
Outlier detection -- Grubbs (alpha = 0.05)
No outliers detected.
Normality test -- Shapiro-Wilk
Sample n Method Statistic p-value
---------------------------------------------------------------------------
all 80 Shapiro-Wilk W=0.9868 0.5858
Variance components
sample component VC %Total SD CV[%] Method
-----------------------------------------------------
all day 18.8358 42.6 4.3400 4.3788 anova
all day:run 17.4343 39.4 4.1754 4.2128 anova
all error 7.9482 18.0 2.8193 2.8445 anova
Confidence intervals (SD)
sample component estimate lower upper
----------------------------------------
all total 6.6497 5.4288 8.5842
all day 4.3400 0.0000 6.2255
all day:run 4.1754 2.0129 5.5513
all error 2.8193 2.3147 3.6073
Confidence intervals (%CV)
sample component estimate lower upper
----------------------------------------
all total 6.7091 5.4773 8.6609
all day 4.3788 0.0000 6.2812
all day:run 4.2128 2.0309 5.6009
all error 2.8445 2.3353 3.6395
Code
plot(p, type = "dot")
Code
plot(p, type = "qq")
value ~ day/run means day, nested run within day, and replicate residual error. outlier() accepts "grubbs" or "iqr"; normal(method="auto") selects by sample size. NegVC is supplied to variance(), not to precision():
Code
p <- variance(p, NegVC = TRUE)13.2.2 Multiple Samples and a Sadler Profile
Code
profile_data <- as.data.frame(read_csv(
"./data/013-精密度分析/precision-profile.csv", show_col_types = FALSE
))
profile_data$sample <- factor(profile_data$sample)
profile_data$day <- factor(profile_data$day)
profile_data$rep <- factor(profile_data$rep)
p2 <- precision(profile_data, value ~ day, by = "sample")
p2 <- variance(p2)Variance components
sample component VC %Total SD CV[%] Method
-------------------------------------------------------
L1 day 0.6376 63.0 0.7985 3.9446 anova
L1 error 0.3750 37.0 0.6124 3.0250 anova
L2 day 5.7139 75.1 2.3904 4.7584 anova
L2 error 1.8937 24.9 1.3761 2.7393 anova
L3 day 3.3913 15.7 1.8415 1.8088 anova
L3 error 18.1466 84.3 4.2599 4.1840 anova
L4 day 30.5549 54.6 5.5276 2.7529 anova
L4 error 25.4525 45.4 5.0451 2.5126 anova
L5 day 0.0000 0.0 0.0000 0.0000 anova
L5 error 171.7986 100.0 13.1072 3.3003 anova
Code
p2 <- ci(p2)Confidence intervals (SD)
sample component estimate lower upper
-----------------------------------------
L1 total 1.0063 0.6007 2.9368
L1 day 0.7985 0.0000 1.4267
L1 error 0.6124 0.4391 1.0108
L2 total 2.7582 1.5714 9.9889
L2 day 2.3904 0.0000 4.2023
L2 error 1.3761 0.9868 2.2716
L3 total 4.6409 3.2872 7.8819
L3 day 1.8415 0.0000 4.1779
L3 error 4.2599 3.0547 7.0319
L4 total 7.4838 4.6166 19.1652
L4 day 5.5276 0.0000 10.0268
L4 error 5.0451 3.6177 8.3280
L5 total 13.1072 9.5629 20.8255
L5 day 0.0000 NA NA
L5 error 13.1072 9.3990 21.6365
Confidence intervals (%CV)
sample component estimate lower upper
-----------------------------------------
L1 total 4.9710 2.9673 14.5079
L1 day 3.9446 0.0000 7.0480
L1 error 3.0250 2.1692 4.9936
L2 total 5.4905 3.1281 19.8843
L2 day 4.7584 0.0000 8.3652
L2 error 2.7393 1.9643 4.5219
L3 total 4.5583 3.2287 7.7416
L3 day 1.8088 0.0000 4.1035
L3 error 4.1840 3.0003 6.9067
L4 total 3.7271 2.2992 9.5447
L4 day 2.7529 0.0000 4.9936
L4 error 2.5126 1.8017 4.1476
L5 total 3.3003 2.4078 5.2436
L5 day 0.0000 NA NA
L5 error 3.3003 2.3666 5.4478
Code
plot(p2, type = "var")
Code
list_sadler()Sadler Precision Profile Models
1 Constant variance sigma^2 = beta1
2 Constant CV sigma^2 = beta1 * u^2
3 Mixed (const + prop var) sigma^2 = beta1 + beta2 * u^2
4 Constrained power (const exponent) sigma^2 = (beta1 + beta2 * u)^2
5 Alternative constrained power sigma^2 = beta1 + beta2 * u^K
6 Unconstrained power (with min) sigma^2 = beta1 + beta2 * u + beta3 * u^J
7 Alternative unconstrained power sigma^2 = beta1 + beta2 * u^J
8 Unconstrained power (Sadler default) sigma^2 = (beta1 + beta2 * u)^J
9 CLSI EP17-like sigma^2 = beta1 * u^J
10 CLSI EP17 exact (log-log) CV = beta1 * u^J
Code
p2 <- profile(p2, model.no = 1:10)Precision profiles -- Sadler
total: sigma^2 = 0.0000 + 0.0086 * u^J (AIC = 56.45, R^2 = 0.9847)
day: sigma^2 = 0.0000 + 0.0095 * u^J (AIC = 43.46, R^2 = 0.8919)
error: sigma^2 = 0.0010 * u^2 (AIC = 55.96, R^2 = 0.9806)
Code
plot(p2, type = "profile")
Each variance component needs at least three finite-SD concentrations for profile fitting. The profile can predict SD/CV at arbitrary concentrations for applications such as functional sensitivity, but predictions outside the observed range are extrapolations.
13.2.3 Multicenter Precision
Code
sites_data <- as.data.frame(read_csv(
"./data/013-精密度分析/precision-sites.csv", show_col_types = FALSE
))
p3 <- precision(sites_data, value ~ site/day/run, by = "sample")
p3 <- variance(p3)Variance components
sample component VC %Total SD CV[%] Method
--------------------------------------------------------
S1 site 0.2746 10.4 0.5241 1.0550 anova
S1 site:day 0.0000 0.0 0.0000 0.0000 anova
S1 site:day:run 0.3553 13.4 0.5960 1.1998 anova
S1 error 2.0231 76.3 1.4223 2.8632 anova
S2 site 0.7117 8.6 0.8436 0.8359 anova
S2 site:day 0.1018 1.2 0.3191 0.3162 anova
S2 site:day:run 0.9106 11.0 0.9543 0.9456 anova
S2 error 6.5670 79.2 2.5626 2.5392 anova
S3 site 22.0901 42.6 4.7000 2.3517 anova
S3 site:day 2.6252 5.1 1.6202 0.8107 anova
S3 site:day:run 0.0000 0.0 0.0000 0.0000 anova
S3 error 27.1915 52.4 5.2145 2.6091 anova
Code
knitr::kable(report(p3, run = "run", day = "day", site = "site"))| Sample Description | Mean Value | Repeatability SD | %CV Repeatability | Reproducibility SD | %CV Reproducibility |
|---|---|---|---|---|---|
| S1 | 49.6766 | 1.4223 | 2.8632 | 1.6288 | 3.2788 |
| S2 | 100.9203 | 2.5626 | 2.5392 | 2.8794 | 2.8532 |
| S3 | 199.8592 | 5.2145 | 2.6091 | 7.2046 | 3.6049 |
To analyze each site separately, use multiple grouping columns while retaining all runs within each subset:
Code
p3s <- precision(sites_data, value ~ day/run, by = c("site", "sample"))
p3s <- variance(p3s)Variance components
sample component VC %Total SD CV[%] Method
-----------------------------------------------------
A.S1 day 0.0000 0.0 0.0000 0.0000 anova
A.S1 day:run 0.1646 6.8 0.4057 0.8231 anova
A.S1 error 2.2677 93.2 1.5059 3.0553 anova
B.S1 day 0.0000 0.0 0.0000 0.0000 anova
B.S1 day:run 0.5459 23.5 0.7389 1.4758 anova
B.S1 error 1.7784 76.5 1.3335 2.6637 anova
A.S2 day 0.3241 4.3 0.5693 0.5602 anova
A.S2 day:run 0.1523 2.0 0.3903 0.3840 anova
A.S2 error 7.0483 93.7 2.6549 2.6124 anova
B.S2 day 0.0000 0.0 0.0000 0.0000 anova
B.S2 day:run 1.6690 21.5 1.2919 1.2891 anova
B.S2 error 6.0856 78.5 2.4669 2.4616 anova
A.S3 day 4.4871 15.6 2.1183 1.0420 anova
A.S3 day:run 1.7006 5.9 1.3041 0.6415 anova
A.S3 error 22.5797 78.5 4.7518 2.3375 anova
B.S3 day 0.7633 2.3 0.8737 0.4448 anova
B.S3 day:run 0.0000 0.0 0.0000 0.0000 anova
B.S3 error 31.8033 97.7 5.6394 2.8709 anova
Code
knitr::kable(report(p3s, run = "run", day = "day"))| Sample Description | Mean Value | Repeatability SD | %CV Repeatability | Within-Laboratory Precision SD | %CV Within-Laboratory Precision |
|---|---|---|---|---|---|
| A.S1 | 49.2884 | 1.5059 | 3.0553 | 1.5596 | 3.1642 |
| B.S1 | 50.0647 | 1.3335 | 2.6637 | 1.5246 | 3.0452 |
| A.S2 | 101.6249 | 2.6549 | 2.6124 | 2.7431 | 2.6993 |
| B.S2 | 100.2158 | 2.4669 | 2.4616 | 2.7847 | 2.7787 |
| A.S3 | 203.2864 | 4.7518 | 2.3375 | 5.3635 | 2.6384 |
| B.S3 | 196.4320 | 5.6394 | 2.8709 | 5.7067 | 2.9052 |
13.2.4 More Complex Nested Models
precision() can represent other VCA hierarchies, for example operator, day, and run:
Code
custom_data <- as.data.frame(read_csv(
"./data/013-精密度分析/precision-custom.csv", show_col_types = FALSE
))
p4 <- precision(custom_data, value ~ operator/day/run)
p4 <- variance(p4)Variance components
sample component VC %Total SD CV[%] Method
-----------------------------------------------------------
all operator 2.2914 11.8 1.5137 1.4898 anova
all operator:day 6.3159 32.4 2.5132 2.4734 anova
all operator:day:run 3.2463 16.7 1.8017 1.7733 anova
all error 7.6322 39.2 2.7627 2.7190 anova
13.2.5 Standard-Data Cross-Checks
The supplied EP05-A3 Appendix A and B files can be analyzed using the same pattern. Appendix A is a balanced day/run design; Appendix B is a multicenter site/day design with samples grouped by by = "sample":
Code
ep05_a <- as.data.frame(read_csv(
"./data/013-精密度分析/std-ep05-appendix-a-glucose.csv", show_col_types = FALSE
))
ep05_a$day <- factor(ep05_a$day)
ep05_a$run <- factor(ep05_a$run)
ep05_a_precision <- precision(ep05_a, result ~ day/run)
ep05_a_precision <- variance(ep05_a_precision, NegVC = FALSE)Variance components
sample component VC %Total SD CV[%] Method
----------------------------------------------------
all day 1.9586 15.1 1.3995 0.5731 anova
all day:run 3.0750 23.8 1.7536 0.7181 anova
all error 7.9000 61.1 2.8107 1.1510 anova
Code
ep05_a_precision <- ci(ep05_a_precision)Confidence intervals (SD)
sample component estimate lower upper
----------------------------------------
all total 3.5963 3.0696 4.3430
all day 1.3995 0.0000 2.4622
all day:run 1.7536 0.0000 2.7858
all error 2.8107 2.3076 3.5963
Confidence intervals (%CV)
sample component estimate lower upper
----------------------------------------
all total 1.4727 1.2570 1.7785
all day 0.5731 0.0000 1.0083
all day:run 0.7181 0.0000 1.1408
all error 1.1510 0.9450 1.4727
Code
knitr::kable(report(ep05_a_precision, day = "day", run = "run", digits = 6))| Sample Description | Mean Value | Repeatability SD | %CV Repeatability | Within-Laboratory Precision SD | %CV Within-Laboratory Precision |
|---|---|---|---|---|---|
| all | 244.2 | 2.810694 | 1.15098 | 3.596325 | 1.472697 |
Code
ep05_b <- as.data.frame(read_csv(
"./data/013-精密度分析/std-ep05-appendix-b-ca19-9.csv", show_col_types = FALSE
))
ep05_b_precision <- precision(ep05_b, result ~ site/day, by = "sample")
ep05_b_precision <- variance(ep05_b_precision)Variance components
sample component VC %Total SD CV[%] Method
-------------------------------------------------------
P1 site 0.3843 35.4 0.6199 5.1312 anova
P1 site:day 0.1778 16.4 0.4216 3.4899 anova
P1 error 0.5248 48.3 0.7244 5.9963 anova
P2 site 1.6189 47.9 1.2724 3.0597 anova
P2 site:day 0.1232 3.6 0.3509 0.8439 anova
P2 error 1.6348 48.4 1.2786 3.0747 anova
P5 site 24.9068 29.3 4.9907 1.3165 anova
P5 site:day 3.1861 3.7 1.7850 0.4709 anova
P5 error 56.9669 67.0 7.5476 1.9910 anova
Q3 site 3.1742 60.4 1.7816 3.1959 anova
Q3 site:day 0.5232 10.0 0.7233 1.2975 anova
Q3 error 1.5599 29.7 1.2490 2.2404 anova
Q4 site 30.0735 75.7 5.4839 3.3104 anova
Q4 site:day 1.8663 4.7 1.3661 0.8247 anova
Q4 error 7.8128 19.7 2.7951 1.6873 anova
Q6 site 164.1097 68.1 12.8105 3.0922 anova
Q6 site:day 3.0208 1.3 1.7380 0.4195 anova
Q6 error 73.9590 30.7 8.5999 2.0758 anova
Code
ep05_b_precision <- ci(ep05_b_precision)Confidence intervals (SD)
sample component estimate lower upper
-----------------------------------------
P1 total 1.0425 0.7415 1.7535
P1 site 0.6199 0.0000 1.1178
P1 site:day 0.4216 0.0000 0.6380
P1 error 0.7244 0.6148 0.8819
P2 total 1.8376 1.2313 3.5995
P2 site 1.2724 0.0000 2.2291
P2 site:day 0.3509 0.0000 0.7084
P2 error 1.2786 1.0852 1.5566
P5 total 9.2228 6.9060 13.8849
P5 site 4.9907 0.0000 8.9156
P5 site:day 1.7850 0.0000 3.9426
P5 error 7.5476 6.4058 9.1888
Q3 total 2.2929 1.4259 5.7003
Q3 site 1.7816 0.0000 3.1184
Q3 site:day 0.7233 0.0000 1.0958
Q3 error 1.2490 1.0600 1.5205
Q4 total 6.3050 3.6468 21.1983
Q4 site 5.4839 0.0000 9.5060
Q4 site:day 1.3661 0.0000 2.1602
Q4 error 2.7951 2.3723 3.4029
Q6 total 15.5271 9.3516 43.6651
Q6 site 12.8105 0.0000 22.1981
Q6 site:day 1.7380 0.0000 4.2690
Q6 error 8.5999 7.2989 10.4699
Confidence intervals (%CV)
sample component estimate lower upper
-----------------------------------------
P1 total 8.6292 6.1376 14.5139
P1 site 5.1312 0.0000 9.2524
P1 site:day 3.4899 0.0000 5.2812
P1 error 5.9963 5.0891 7.3001
P2 total 4.4191 2.9611 8.6559
P2 site 3.0597 0.0000 5.3606
P2 site:day 0.8439 0.0000 1.7036
P2 error 3.0747 2.6095 3.7433
P5 total 2.4329 1.8217 3.6627
P5 site 1.3165 0.0000 2.3518
P5 site:day 0.4709 0.0000 1.0400
P5 error 1.9910 1.6898 2.4239
Q3 total 4.1130 2.5578 10.2254
Q3 site 3.1959 0.0000 5.5938
Q3 site:day 1.2975 0.0000 1.9656
Q3 error 2.2404 1.9015 2.7276
Q4 total 3.8061 2.2014 12.7966
Q4 site 3.3104 0.0000 5.7384
Q4 site:day 0.8247 0.0000 1.3040
Q4 error 1.6873 1.4320 2.0542
Q6 total 3.7479 2.2573 10.5398
Q6 site 3.0922 0.0000 5.3581
Q6 site:day 0.4195 0.0000 1.0305
Q6 error 2.0758 1.7618 2.5272
Code
knitr::kable(report(ep05_b_precision, day = "day", site = "site", table = "detailed"))| Sample Description | Mean Value | N | Repeatability SD | %CV Repeatability | Between-Run SD | %CV Between-Run | Between-Day SD | %CV Between-Day | Between-Site SD | %CV Between-Site | Within-Laboratory Precision SD | %CV Within-Laboratory Precision | Reproducibility SD | %CV Reproducibility |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| P1 | 12.0813 | 75 | 0.7244 | 5.9963 | NA | NA | 0.4216 | 3.4899 | 0.6199 | 5.1312 | 0.8382 | 6.9379 | 1.0425 | 8.6292 |
| P2 | 41.5840 | 75 | 1.2786 | 3.0747 | NA | NA | 0.3509 | 0.8439 | 1.2724 | 3.0597 | 1.3259 | 3.1884 | 1.8376 | 4.4191 |
| P5 | 379.0907 | 75 | 7.5476 | 1.9910 | NA | NA | 1.7850 | 0.4709 | 4.9907 | 1.3165 | 7.7558 | 2.0459 | 9.2228 | 2.4329 |
| Q3 | 55.7467 | 75 | 1.2490 | 2.2404 | NA | NA | 0.7233 | 1.2975 | 1.7816 | 3.1959 | 1.4433 | 2.5890 | 2.2929 | 4.1130 |
| Q4 | 165.6560 | 75 | 2.7951 | 1.6873 | NA | NA | 1.3661 | 0.8247 | 5.4839 | 3.3104 | 3.1111 | 1.8781 | 6.3050 | 3.8061 |
| Q6 | 414.2867 | 75 | 8.5999 | 2.0758 | NA | NA | 1.7380 | 0.4195 | 12.8105 | 3.0922 | 8.7738 | 2.1178 | 15.5271 | 3.7479 |
Standard examples can still differ because of exclusion rules, displayed intermediate values, ANOVA versus REML, and the exact degrees-of-freedom convention. Disclose the path used rather than treating similar rounded values as identical algorithms.
13.3 Complete Standard Cases
13.3.1 CLSI EP05-A3 Appendix A: Glucose 20×2×2
Appendix A uses one patient-plasma pool measured in two runs per day on 20 days, with two replicates per run, for 80 glucose results. The data are complete and balanced; the nested structure is day/run, with replicate measurements forming the residual layer.
Code
ep05_a <- as.data.frame(read_csv(
"./data/013-精密度分析/std-ep05-appendix-a-glucose.csv",
show_col_types = FALSE
))
ep05_a$day <- factor(ep05_a$day)
ep05_a$run <- factor(ep05_a$run)
ep05_a$replicate <- factor(ep05_a$replicate)Code
ep05_a_precision <- precision(
ep05_a, form = result ~ day/run
)
ep05_a_precision <- variance(ep05_a_precision, NegVC = FALSE)Variance components
sample component VC %Total SD CV[%] Method
----------------------------------------------------
all day 1.9586 15.1 1.3995 0.5731 anova
all day:run 3.0750 23.8 1.7536 0.7181 anova
all error 7.9000 61.1 2.8107 1.1510 anova
Code
ep05_a_precision <- ci(ep05_a_precision)Confidence intervals (SD)
sample component estimate lower upper
----------------------------------------
all total 3.5963 3.0696 4.3430
all day 1.3995 0.0000 2.4622
all day:run 1.7536 0.0000 2.7858
all error 2.8107 2.3076 3.5963
Confidence intervals (%CV)
sample component estimate lower upper
----------------------------------------
all total 1.4727 1.2570 1.7785
all day 0.5731 0.0000 1.0083
all day:run 0.7181 0.0000 1.1408
all error 1.1510 0.9450 1.4727
Code
knitr::kable(report(
ep05_a_precision,
day = "day", run = "run", digits = 6
))| Sample Description | Mean Value | Repeatability SD | %CV Repeatability | Within-Laboratory Precision SD | %CV Within-Laboratory Precision |
|---|---|---|---|---|---|
| all | 244.2 | 2.810694 | 1.15098 | 3.596325 | 1.472697 |
| Standard location | Statistic | Standard | ivdtools |
|---|---|---|---|
| EP05-A3 Appendix A | mean | 244.20 | 244.2000 |
| EP05-A3 Appendix A | SS_day | 415.80 | 415.8000 |
| EP05-A3 Appendix A | SS_run_day | 281.00 | 281.0000 |
| EP05-A3 Appendix A | SS_error | 316.00 | 316.0000 |
| EP05-A3 Appendix A | MS_day | 21.88 | 21.8842 |
| EP05-A3 Appendix A | MS_run_day | 14.05 | 14.0500 |
| EP05-A3 Appendix A | MS_error | 7.90 | 7.9000 |
| EP05-A3 Appendix A | V_day | 1.96 | 1.9586 |
| EP05-A3 Appendix A | V_run | 3.08 | 3.0750 |
| EP05-A3 Appendix A | V_error | 7.90 | 7.9000 |
| EP05-A3 Appendix A | SD_repeatability | 2.81 | 2.8107 |
| EP05-A3 Appendix A | SD_within_lab | 3.60 | 3.5963 |
| EP05-A3 Appendix A | CV_repeatability | 1.20 | 1.1510 |
| EP05-A3 Appendix A | CV_within_lab | 1.50 | 1.4727 |
13.3.2 CLSI EP05-A3 Appendix B: CA19-9 Multicenter Study
Appendix B contains three sites, six samples, five days per site, and five replicates per day, for 450 results. The sites use different instruments and operators but the same reagent and calibrator lots. The nested model is site/day, with replicate measurements as the residual layer.
Code
ep05_b <- as.data.frame(read_csv(
"./data/013-精密度分析/std-ep05-appendix-b-ca19-9.csv",
show_col_types = FALSE
))
ep05_b$sample <- factor(ep05_b$sample)
ep05_b$site <- factor(ep05_b$site)
ep05_b$day <- factor(ep05_b$day)
ep05_b$replicate <- factor(ep05_b$replicate)Code
ep05_b_precision <- precision(
ep05_b, form = result ~ site/day, by = "sample"
)
ep05_b_precision <- variance(ep05_b_precision)Variance components
sample component VC %Total SD CV[%] Method
-------------------------------------------------------
P1 site 0.3843 35.4 0.6199 5.1312 anova
P1 site:day 0.1778 16.4 0.4216 3.4899 anova
P1 error 0.5248 48.3 0.7244 5.9963 anova
P2 site 1.6189 47.9 1.2724 3.0597 anova
P2 site:day 0.1232 3.6 0.3509 0.8439 anova
P2 error 1.6348 48.4 1.2786 3.0747 anova
P5 site 24.9068 29.3 4.9907 1.3165 anova
P5 site:day 3.1861 3.7 1.7850 0.4709 anova
P5 error 56.9669 67.0 7.5476 1.9910 anova
Q3 site 3.1742 60.4 1.7816 3.1959 anova
Q3 site:day 0.5232 10.0 0.7233 1.2975 anova
Q3 error 1.5599 29.7 1.2490 2.2404 anova
Q4 site 30.0735 75.7 5.4839 3.3104 anova
Q4 site:day 1.8663 4.7 1.3661 0.8247 anova
Q4 error 7.8128 19.7 2.7951 1.6873 anova
Q6 site 164.1097 68.1 12.8105 3.0922 anova
Q6 site:day 3.0208 1.3 1.7380 0.4195 anova
Q6 error 73.9590 30.7 8.5999 2.0758 anova
Code
ep05_b_precision <- ci(ep05_b_precision)Confidence intervals (SD)
sample component estimate lower upper
-----------------------------------------
P1 total 1.0425 0.7415 1.7535
P1 site 0.6199 0.0000 1.1178
P1 site:day 0.4216 0.0000 0.6380
P1 error 0.7244 0.6148 0.8819
P2 total 1.8376 1.2313 3.5995
P2 site 1.2724 0.0000 2.2291
P2 site:day 0.3509 0.0000 0.7084
P2 error 1.2786 1.0852 1.5566
P5 total 9.2228 6.9060 13.8849
P5 site 4.9907 0.0000 8.9156
P5 site:day 1.7850 0.0000 3.9426
P5 error 7.5476 6.4058 9.1888
Q3 total 2.2929 1.4259 5.7003
Q3 site 1.7816 0.0000 3.1184
Q3 site:day 0.7233 0.0000 1.0958
Q3 error 1.2490 1.0600 1.5205
Q4 total 6.3050 3.6468 21.1983
Q4 site 5.4839 0.0000 9.5060
Q4 site:day 1.3661 0.0000 2.1602
Q4 error 2.7951 2.3723 3.4029
Q6 total 15.5271 9.3516 43.6651
Q6 site 12.8105 0.0000 22.1981
Q6 site:day 1.7380 0.0000 4.2690
Q6 error 8.5999 7.2989 10.4699
Confidence intervals (%CV)
sample component estimate lower upper
-----------------------------------------
P1 total 8.6292 6.1376 14.5139
P1 site 5.1312 0.0000 9.2524
P1 site:day 3.4899 0.0000 5.2812
P1 error 5.9963 5.0891 7.3001
P2 total 4.4191 2.9611 8.6559
P2 site 3.0597 0.0000 5.3606
P2 site:day 0.8439 0.0000 1.7036
P2 error 3.0747 2.6095 3.7433
P5 total 2.4329 1.8217 3.6627
P5 site 1.3165 0.0000 2.3518
P5 site:day 0.4709 0.0000 1.0400
P5 error 1.9910 1.6898 2.4239
Q3 total 4.1130 2.5578 10.2254
Q3 site 3.1959 0.0000 5.5938
Q3 site:day 1.2975 0.0000 1.9656
Q3 error 2.2404 1.9015 2.7276
Q4 total 3.8061 2.2014 12.7966
Q4 site 3.3104 0.0000 5.7384
Q4 site:day 0.8247 0.0000 1.3040
Q4 error 1.6873 1.4320 2.0542
Q6 total 3.7479 2.2573 10.5398
Q6 site 3.0922 0.0000 5.3581
Q6 site:day 0.4195 0.0000 1.0305
Q6 error 2.0758 1.7618 2.5272
Code
ep05_b_report <- report(
ep05_b_precision,
day = "day", site = "site", table = "detailed", digits = 6
)
ep05_b_report_ci <- report(
ep05_b_precision,
day = "day", site = "site", table = "CI", digits = 6
)| Standard location | Statistic | Standard | ivdtools |
|---|---|---|---|
| EP05-A3 Appendix B | P1_mean | 12.100 | 12.0813 |
| EP05-A3 Appendix B | P1_SD_R | 0.724 | 0.7244 |
| EP05-A3 Appendix B | P1_SD_WL | 0.838 | 0.8382 |
| EP05-A3 Appendix B | P1_SD_REP | 1.040 | 1.0425 |
| EP05-A3 Appendix B | P1_CV_R | 6.000 | 5.9963 |
| EP05-A3 Appendix B | P1_CV_WL | 6.900 | 6.9379 |
| EP05-A3 Appendix B | P1_CV_REP | 8.600 | 8.6292 |
| EP05-A3 Appendix B | P2_mean | 41.600 | 41.5840 |
| EP05-A3 Appendix B | P2_SD_R | 1.280 | 1.2786 |
| EP05-A3 Appendix B | P2_SD_WL | 1.330 | 1.3259 |
| EP05-A3 Appendix B | P2_SD_REP | 1.840 | 1.8376 |
| EP05-A3 Appendix B | P2_CV_R | 3.100 | 3.0747 |
| EP05-A3 Appendix B | P2_CV_WL | 3.200 | 3.1884 |
| EP05-A3 Appendix B | P2_CV_REP | 4.400 | 4.4191 |
| EP05-A3 Appendix B | P5_mean | 379.000 | 379.0907 |
| EP05-A3 Appendix B | P5_SD_R | 7.550 | 7.5476 |
| EP05-A3 Appendix B | P5_SD_WL | 7.760 | 7.7558 |
| EP05-A3 Appendix B | P5_SD_REP | 9.220 | 9.2228 |
| EP05-A3 Appendix B | P5_CV_R | 2.000 | 1.9910 |
| EP05-A3 Appendix B | P5_CV_WL | 2.000 | 2.0459 |
| EP05-A3 Appendix B | P5_CV_REP | 2.400 | 2.4329 |
| EP05-A3 Appendix B | Q3_mean | 55.700 | 55.7467 |
| EP05-A3 Appendix B | Q3_SD_R | 1.250 | 1.2490 |
| EP05-A3 Appendix B | Q3_SD_WL | 1.440 | 1.4433 |
| EP05-A3 Appendix B | Q3_SD_REP | 2.290 | 2.2929 |
| EP05-A3 Appendix B | Q3_CV_R | 2.200 | 2.2404 |
| EP05-A3 Appendix B | Q3_CV_WL | 2.600 | 2.5890 |
| EP05-A3 Appendix B | Q3_CV_REP | 4.100 | 4.1130 |
| EP05-A3 Appendix B | Q4_mean | 166.000 | 165.6560 |
| EP05-A3 Appendix B | Q4_SD_R | 2.800 | 2.7951 |
| EP05-A3 Appendix B | Q4_SD_WL | 3.110 | 3.1111 |
| EP05-A3 Appendix B | Q4_SD_REP | 6.300 | 6.3050 |
| EP05-A3 Appendix B | Q4_CV_R | 1.700 | 1.6873 |
| EP05-A3 Appendix B | Q4_CV_WL | 1.900 | 1.8781 |
| EP05-A3 Appendix B | Q4_CV_REP | 3.800 | 3.8061 |
| EP05-A3 Appendix B | Q6_mean | 414.000 | 414.2867 |
| EP05-A3 Appendix B | Q6_SD_R | 8.600 | 8.5999 |
| EP05-A3 Appendix B | Q6_SD_WL | 8.770 | 8.7738 |
| EP05-A3 Appendix B | Q6_SD_REP | 15.500 | 15.5271 |
| EP05-A3 Appendix B | Q6_CV_R | 2.100 | 2.0758 |
| EP05-A3 Appendix B | Q6_CV_WL | 2.100 | 2.1178 |
| EP05-A3 Appendix B | Q6_CV_REP | 3.700 | 3.7479 |
13.3.3 CLSI EP15-A3: Ferritin 5×5 Verification
The complete EP15 case contains three serum ferritin levels, measured for five days with five replicates per day. Keep the raw data intact, then reproduce the standard case’s prespecified Grubbs exclusion of 30.2 µg/L from Level 1, Day 1, Replicate 3. This exclusion reproduces the standard case and is not selected retrospectively by the code.
Code
ep15_ferritin_raw <- as.data.frame(read_csv(
"./data/013-精密度分析/std-ep15-ferritin.csv", show_col_types = FALSE
))
ep15_ferritin_raw$sample <- factor(ep15_ferritin_raw$sample)
ep15_ferritin_raw$run <- factor(ep15_ferritin_raw$run)
ep15_ferritin_raw$replicate <- factor(ep15_ferritin_raw$replicate)
ep15_exclude <- with(
ep15_ferritin_raw,
sample == 1L & run == 1L & replicate == 3L
)
ep15_ferritin_raw[ep15_exclude, ] run replicate sample result
7 1 3 1 30.2
Code
# Exclude the prespecified result.
ep15_ferritin <- ep15_ferritin_raw[!ep15_exclude, ]After exclusion, Level 1 has 24 results, with four replicates on Day 1; Levels 2 and 3 each retain 25 results. Split by sample and fit one result ~ run model for each level. run represents the between-run component, while daily replicates form the residual error component.
Code
# Separate levels to compare ANOVA and REML explicitly.
ep15_ferritin_list <- split(ep15_ferritin, ep15_ferritin$sample)
ep15_level_1 <- droplevels(ep15_ferritin_list[["1"]])
ep15_level_2 <- droplevels(ep15_ferritin_list[["2"]])
ep15_level_3 <- droplevels(ep15_ferritin_list[["3"]])
# Level 1
ep15_level_1_fit <- precision(ep15_level_1, result ~ run)
# The unbalanced design automatically switches to REML.
ep15_level_1_fit_r <- variance(
ep15_level_1_fit, NegVC = FALSE
)Variance components
sample component VC %Total SD CV[%] Method
----------------------------------------------------
all run 0.2820 27.5 0.5311 2.0816 reml
all error 0.7423 72.5 0.8616 3.3770 reml
Code
# Use ANOVA explicitly to match the standard case.
ep15_level_1_fit <- variance(
ep15_level_1_fit, method = "anova", NegVC = FALSE
)Variance components
sample component VC %Total SD CV[%] Method
----------------------------------------------------
all run 0.2804 27.4 0.5295 2.0756 anova
all error 0.7414 72.6 0.8610 3.3749 anova
Code
ep15_level_1_fit <- ci(ep15_level_1_fit, digits = 6)Confidence intervals (SD)
sample component estimate lower upper
--------------------------------------------
all total 1.010837 0.752556 1.539604
all run 0.529550 0.000000 0.944544
all error 0.861028 0.654803 1.257592
Confidence intervals (%CV)
sample component estimate lower upper
--------------------------------------------
all total 3.962125 2.949754 6.034706
all run 2.075649 0.000000 3.702279
all error 3.374924 2.566597 4.929319
Code
# Level 2
ep15_level_2_fit <- precision(ep15_level_2, result ~ run)
ep15_level_2_fit <- variance(
ep15_level_2_fit, method = "anova", NegVC = FALSE
)Variance components
sample component VC %Total SD CV[%] Method
----------------------------------------------------
all run 2.5400 44.6 1.5937 1.1374 anova
all error 3.1600 55.4 1.7776 1.2687 anova
Code
ep15_level_2_fit <- ci(ep15_level_2_fit, digits = 6)Confidence intervals (SD)
sample component estimate lower upper
--------------------------------------------
all total 2.387467 1.701094 3.999257
all run 1.593738 0.000000 2.636950
all error 1.777639 1.359999 2.567034
Confidence intervals (%CV)
sample component estimate lower upper
--------------------------------------------
all total 1.703873 1.214026 2.854166
all run 1.137409 0.000000 1.881922
all error 1.268655 0.970596 1.832025
Code
# Level 3
ep15_level_3_fit <- precision(ep15_level_3, result ~ run)
ep15_level_3_fit <- variance(
ep15_level_3_fit, method = "anova", NegVC = FALSE
)Variance components
sample component VC %Total SD CV[%] Method
-------------------------------------------------------
all run 102.6080 47.5 10.1296 1.6262 anova
all error 113.5200 52.5 10.6546 1.7105 anova
Code
ep15_level_3_fit <- ci(ep15_level_3_fit, digits = 6)Confidence intervals (SD)
sample component estimate lower upper
-----------------------------------------------
all total 14.701292 10.382650 25.141336
all run 10.129561 0.000000 16.638736
all error 10.654576 8.151381 15.385949
Confidence intervals (%CV)
sample component estimate lower upper
--------------------------------------------
all total 2.360213 1.666878 4.036305
all run 1.626246 0.000000 2.671259
all error 1.710534 1.308660 2.470130
Extract the total mean, run variance component, repeatability SD, and total within-laboratory SD from each object and compare them with the displayed standard values:
| Sample | Statistic | Standard | ivdtools |
|---|---|---|---|
| Level 1 | overall mean | 25.5000 | 25.5125 |
| Level 1 | between-run variance | 0.2805 | 0.2804 |
| Level 1 | repeatability SD | 0.8600 | 0.8610 |
| Level 1 | within-laboratory SD | 1.0100 | 1.0108 |
| Level 2 | overall mean | 140.1000 | 140.1200 |
| Level 2 | between-run variance | 2.5400 | 2.5400 |
| Level 2 | repeatability SD | 1.7800 | 1.7776 |
| Level 2 | within-laboratory SD | 2.4000 | 2.3875 |
| Level 3 | overall mean | 623.0000 | 622.8800 |
| Level 3 | between-run variance | 102.6100 | 102.6080 |
| Level 3 | repeatability SD | 10.7000 | 10.6546 |
| Level 3 | within-laboratory SD | 14.7000 | 14.7013 |
The recalculated results for all three levels agree with or are close to the displayed standard values. Level 1 is unbalanced after exclusion, so small differences in variance components arise from unbalanced-data calculation details and intermediate rounding. For Level 2, the exact within-laboratory SD is 2.3875 µg/L, while the standard displays 2.40 µg/L after rounding its variance components. The standard case passes all three levels under its stated PI and UVL criteria; that conclusion applies only to the stated claims and limits.
13.3.4 WS/T 408–2024: Precision Verification
WS/T 408 Appendix A.2 uses two HDL-C levels, five batches per level, and three replicates per batch. After import, verify that each sample and batch contains three results.
Code
wst408_precision <- as.data.frame(read_csv(
"./data/013-精密度分析/std-wst408-precision.csv",
show_col_types = FALSE
))
wst408_precision$sample <- factor(wst408_precision$sample)
wst408_precision$batch <- factor(wst408_precision$batch)
wst408_precision$replicate <- factor(wst408_precision$replicate)Code
wst408_precision_fit <- precision(
wst408_precision,
result ~ batch,
by = "sample"
)
wst408_precision_fit <- variance(
wst408_precision_fit,
method = "anova",
NegVC = FALSE
)Variance components
sample component VC %Total SD CV[%] Method
----------------------------------------------------
样品1 batch 0.0001 10.9 0.0080 0.7637 anova
样品1 error 0.0005 89.1 0.0228 2.1884 anova
样品2 batch 0.0001 12.1 0.0091 0.5892 anova
样品2 error 0.0006 87.9 0.0246 1.5898 anova
Code
wst408_precision_fit <- ci(wst408_precision_fit, digits = 6)Confidence intervals (SD)
sample component estimate lower upper
--------------------------------------------
样品1 total 0.024152 0.017523 0.038843
样品1 batch 0.007958 0.000000 0.020611
样品1 error 0.022804 0.015933 0.040019
样品2 total 0.026268 0.019035 0.042360
样品2 batch 0.009129 0.000000 0.022737
样品2 error 0.024631 0.017210 0.043225
Confidence intervals (%CV)
sample component estimate lower upper
--------------------------------------------
样品1 total 2.317879 1.681694 3.727688
样品1 batch 0.763745 0.000000 1.978014
样品1 error 2.188437 1.529098 3.840561
样品2 total 1.695429 1.228567 2.734049
样品2 batch 0.589202 0.000000 1.467529
样品2 error 1.589755 1.110789 2.789914
| Sample | Statistic | Standard | ivdtools |
|---|---|---|---|
| Level 1 | overall mean | 1.0420 | 1.0420 |
| Level 1 | s_WR | 0.0228 | 0.0228 |
| Level 1 | s_WL | 0.0242 | 0.0242 |
| Level 1 | CV_WL (%) | NA | 2.3179 |
| Level 2 | overall mean | 1.5493 | 1.5493 |
| Level 2 | s_WR | 0.0246 | 0.0246 |
| Level 2 | s_WL | 0.0263 | 0.0263 |
| Level 2 | CV_WL (%) | NA | 1.6954 |
13.3.5 YY/T 1789.1–2021 Appendix A: 25-Hydroxyvitamin D
Appendix A provides complete 20-day × 2-run × 2-replicate data, for 80 results.
Code
yyt1789_1 <- read.csv(
"./data/013-精密度分析/std-yyt1789-1-appendix-a.csv"
)
yyt1789_1$day <- factor(yyt1789_1$day)
yyt1789_1$run <- factor(yyt1789_1$run)
yyt1789_1_fit <- precision(yyt1789_1, result ~ day/run)
yyt1789_1_fit <- variance(
yyt1789_1_fit, method = "anova"
)Variance components
sample component VC %Total SD CV[%] Method
----------------------------------------------------
all day 0.1769 36.2 0.4206 2.4324 anova
all day:run 0.0648 13.2 0.2545 1.4721 anova
all error 0.2475 50.6 0.4975 2.8773 anova
Code
yyt1789_1_fit <- ci(yyt1789_1_fit)Confidence intervals (SD)
sample component estimate lower upper
----------------------------------------
all total 0.6994 0.5861 0.8674
all day 0.4206 0.0000 0.5991
all day:run 0.2545 0.0000 0.4400
all error 0.4975 0.4084 0.6365
Confidence intervals (%CV)
sample component estimate lower upper
----------------------------------------
all total 4.0450 3.3897 5.0170
all day 2.4324 0.0000 3.4650
all day:run 1.4721 0.0000 2.5448
all error 2.8773 2.3623 3.6815
| Standard location | Statistic | Standard | ivdtools |
|---|---|---|---|
| YY/T 1789.1 Appendix A | mean | 17.290 | 17.2898 |
| YY/T 1789.1 Appendix A | repeatability SD | 0.497 | 0.4975 |
| YY/T 1789.1 Appendix A | within-laboratory SD | 0.699 | 0.6994 |
| YY/T 1789.1 Appendix A | repeatability CV (%) | 2.900 | 2.8773 |
| YY/T 1789.1 Appendix A | within-laboratory CV (%) | 4.000 | 4.0450 |
| YY/T 1789.1 Appendix A | repeatability CI lower | 0.408 | 0.4084 |
| YY/T 1789.1 Appendix A | repeatability CI upper | 0.637 | 0.6365 |
| YY/T 1789.1 Appendix A | within-laboratory CI lower | 0.586 | 0.5861 |
| YY/T 1789.1 Appendix A | within-laboratory CI upper | 0.867 | 0.8674 |