In practice an experiment is rarely designed from scratch. A researcher may already have data collected at certain conditions and want to add new observations to improve estimation — without discarding what has already been measured. The key question is: where can new points be placed so that the efficiency of the augmented design stays above an acceptable threshold?
optedr answers this question with two functions used in sequence:
get_augment_region() — computes the candidate region:
the set of design points whose addition keeps the D-efficiency of the
augmented design above a user-specified threshold
delta_val.augment_design() — adds a chosen point to the initial
design and rescales the weights.Both functions support the same optimality criteria as
opt_des() and work for any number of factors.
| Parameter | Role |
|---|---|
init_design |
Current design (data frame with
Point/Weight in 1D, or factor columns +
Weight in multi-D) |
alpha |
Fraction of total weight assigned to the new point after augmentation |
delta_val |
Minimum acceptable D-efficiency of the augmented design |
calc_optimal_design |
If TRUE, also computes the optimal design and uses it
as the reference for efficiency |
new_points |
Data frame of points to add (non-interactive mode); omit for interactive mode |
par_int |
Indices of parameters of interest (Ds-Optimality only) |
n_lhs |
Number of Latin-Hypercube candidates for the region search (multi-D) |
We start with a uniform three-point design for Antoine’s equation and look for points that keep the D-efficiency of the augmented design above 85 %.
init_des <- data.frame(
Point = c(30, 60, 90),
Weight = c(1/3, 1/3, 1/3)
)
region <- get_augment_region(
criterion = "D-Optimality",
init_design = init_des,
alpha = 0.25,
model = y ~ 10^(a - b / (c + x)),
parameters = c("a", "b", "c"),
par_values = c(8.07131, 1730.63, 233.426),
design_space = c(1, 100),
calc_optimal_design = FALSE,
delta_val = 0.85
)region$region is a data frame of candidate intervals.
Each row gives a lower and upper bound on the design space where the new
point can be placed.
new_pt <- mean(region$region[1:2])
augmented <- augment_design(
criterion = "D-Optimality",
init_design = init_des,
alpha = 0.25,
model = y ~ 10^(a - b / (c + x)),
parameters = c("a", "b", "c"),
par_values = c(8.07131, 1730.63, 233.426),
design_space = c(1, 100),
calc_optimal_design = FALSE,
delta_val = 0.85,
new_points = data.frame(Point = new_pt, Weight = 1)
)result_opt <- opt_des(
"D-Optimality",
y ~ 10^(a - b / (c + x)), c("a", "b", "c"),
c(8.07131, 1730.63, 233.426), c(1, 100)
)
#>
#> ℹ Stop condition not reached, max iterations performed
#> ⠙ Calculating optimal design 22 done (16/s) | 1.4sℹ The lower bound for efficiency is 99.9986187558838%
#>
eff_before <- design_efficiency(init_des, result_opt)
#> ℹ The efficiency of the design is 38.5312233962882%
eff_after <- design_efficiency(augmented, result_opt)
#> ℹ The efficiency of the design is 34.2933573610529%
cat("Efficiency before augmenting:", round(eff_before * 100, 2), "%\n")
#> Efficiency before augmenting: 38.53 %
cat("Efficiency after augmenting: ", round(eff_after * 100, 2), "%\n")
#> Efficiency after augmenting: 34.29 %
cat("Gain: ", round((eff_after - eff_before) * 100, 2),
"percentage points\n")
#> Gain: -4.24 percentage pointscalc_optimal_design = TRUE)When calc_optimal_design = TRUE, the function internally
computes the optimal design and uses it to define the efficiency
threshold. This is the recommended mode when no optimal design has been
computed yet:
In multi-dimensional spaces get_augment_region() samples
candidate points with a Latin Hypercube (controlled by
n_lhs) and returns a data frame of candidates together with
their estimated efficiency gain. A heatmap of the efficiency function is
displayed automatically.
init_2d <- data.frame(
x1 = c(0.8, 10, 5),
x2 = c(10, 0.8, 5),
Weight = c(1/3, 1/3, 1/3)
)
result_2D <- opt_des(
criterion = "D-Optimality",
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10))
)
#>
#> ℹ Stop condition reached: difference between sensitivity and criterion < 1e-05
#> ⠙ Calculating optimal design 20 done (11/s) | 1.8sℹ The lower bound for efficiency is 99.9990066738592%
#>
region_2d <- get_augment_region(
criterion = "D-Optimality",
init_design = init_2d,
alpha = 0.25,
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10)),
calc_optimal_design = FALSE,
delta_val = 0.85
)#> ℹ 1920 candidate points with efficiency >= 0.85 (from LHS sample of 2000)
region_2d$region is a data frame of sampled candidates,
each with an efficiency column. Pick the candidate that
maximises efficiency:
best_2d <- region_2d$region[which.max(region_2d$region$efficiency), ]
eff_antes <- suppressMessages(design_efficiency(init_2d, result_2D))
aug_2d <- augment_design(
criterion = "D-Optimality",
init_design = init_2d,
alpha = 0.25,
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10)),
calc_optimal_design = FALSE,
delta_val = 0.85,
new_points = data.frame(x1 = best_2d$x1, x2 = best_2d$x2, Weight = 1)
)#> ℹ 1910 candidate points with efficiency >= 0.85 (from LHS sample of 2000)
#> Sample of candidate points:
#> x1 x2 efficiency
#> 1 8.794390 0.212954 0.8582158
#> 2 6.838483 9.551944 1.1518121
#> 3 9.459755 1.825559 0.9033251
#> 4 9.972765 6.345771 1.1414484
#> 5 5.583301 8.467391 1.0770282
#> 6 0.523416 6.496930 0.9385391
#> 7 3.039475 0.515030 0.9331933
#> 8 4.600099 8.381351 1.0267086
#> 9 9.709010 5.238821 1.0868141
#> 10 7.185740 7.984717 1.1262232
#> 11 1.269713 5.595604 0.9042071
#> 12 9.849443 2.206128 0.9145444
#> 13 2.698294 7.970581 0.9129588
#> 14 1.789031 3.713830 0.8694875
#> 15 7.478578 8.792885 1.1563916
eff_despues <- suppressMessages(design_efficiency(aug_2d, result_2D))
cat("Efficiency before:", round(eff_antes * 100, 2), "%\n")
#> Efficiency before: 68.4 %
cat("Efficiency after: ", round(eff_despues * 100, 2), "%\n")
#> Efficiency after: 85.12 %
print(aug_2d)
#> x1 x2 Weight
#> 1 0.800000 10.000000 0.25
#> 2 10.000000 0.800000 0.25
#> 3 5.000000 5.000000 0.25
#> 4 9.834484 9.991616 0.25
For three or more factors the candidate region is displayed as a scatter-matrix coloured by candidate/non-candidate status, with the current design shown as triangles.
init_3d <- data.frame(
x1 = c(0.8, 10, 10, 0.8, 10),
x2 = c(10, 0.8, 10, 10, 0.8),
x3 = c(10, 10, 0.8, 0.8, 10),
Weight = rep(0.2, 5)
)
region_3d <- get_augment_region(
criterion = "D-Optimality",
init_design = init_3d,
alpha = 0.45,
model = y ~ Vmax * x1 * x2 * x3 / ((K1+x1) * (K2+x2) * (K3+x3)),
parameters = c("Vmax", "K1", "K2", "K3"),
par_values = c(1, 1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10), x3 = c(0.1, 10)),
calc_optimal_design = FALSE,
delta_val = 0.93
)#> ℹ 981 candidate points with efficiency >= 0.93 (from LHS sample of 2000)
cat("Number of candidate points:", nrow(region_3d$region), "\n")
#> Number of candidate points: 981
plot(region_3d$plot)
When the goal is to augment while preserving estimation quality for a
subset of parameters, use
criterion = "Ds-Optimality" and pass
par_int:
region_ds <- get_augment_region(
criterion = "Ds-Optimality",
init_design = init_2d,
alpha = 0.25,
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10)),
calc_optimal_design = FALSE,
par_int = c(1),
delta_val = 0.85,
n_lhs = 5000
)#> ℹ 3453 candidate points with efficiency >= 0.85 (from LHS sample of 5000)
best_ds <- region_ds$region[which.max(region_ds$region$efficiency), ]
aug_ds <- augment_design(
criterion = "Ds-Optimality",
init_design = init_2d,
alpha = 0.25,
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10)),
calc_optimal_design = FALSE,
par_int = c(1),
delta_val = 0.85,
new_points = data.frame(x1 = best_ds$x1, x2 = best_ds$x2, Weight = 1),
n_lhs = 5000
)
#> ℹ 3469 candidate points with efficiency >= 0.85 (from LHS sample of 5000)
#> Sample of candidate points:
#> x1 x2 efficiency
#> 1 7.6032146 9.3698539 2.4871401
#> 2 6.3288098 2.7861100 0.8715261
#> 3 0.2309604 3.7634435 0.9144785
#> 4 9.0195639 9.0019514 2.7257777
#> 5 5.7033820 7.2771142 1.6913988
#> 6 6.6428368 4.2905667 1.2472598
#> 7 3.0184121 6.7119601 0.9384701
#> 8 5.1552525 8.1395227 1.6583790
#> 9 6.4106194 4.5610415 1.2870381
#> 10 5.5796609 0.7116589 0.8822207
#> 11 4.2507370 6.9246339 1.2670307
#> 12 4.6568340 5.0945095 1.1257668
#> 13 5.2756721 4.4240340 1.1090500
#> 14 6.9540511 6.5696378 1.8360266
#> 15 5.2299034 3.9739387 1.0192873
print(aug_ds)
#> x1 x2 Weight
#> 1 0.800000 10.000000 0.25
#> 2 10.000000 0.800000 0.25
#> 3 5.000000 5.000000 0.25
#> 4 9.889364 9.929961 0.25
Omitting new_points (and delta_val) from
both functions triggers an interactive session where the package plots
the candidate region and asks the user to type a point. This mode is
documented in ?augment_design.