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 (39/s) | 561ms
#> ℹ The lower bound for efficiency is 99.9986187416549%
eff_before <- design_efficiency(init_des, result_opt)
#> ℹ The efficiency of the design is 38.5312233953934%
eff_after <- design_efficiency(augmented, result_opt)
#> ℹ The efficiency of the design is 34.2933573587506%
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 16 done (21/s) | 768ms
#> ℹ The lower bound for efficiency is 99.999008611314%
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
)#> ℹ 1908 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)
)#> ℹ 1911 candidate points with efficiency >= 0.85 (from LHS sample of 2000)
#> Sample of candidate points:
#> x1 x2 efficiency
#> 1 6.604019 3.5770853 0.9305153
#> 2 2.986849 9.2308325 0.9469363
#> 3 6.837091 7.8039254 1.1095147
#> 4 9.265387 1.5304097 0.9055531
#> 5 7.388549 9.0941243 1.1605922
#> 6 7.519186 7.9031675 1.1344705
#> 7 4.603956 8.6034051 1.0316979
#> 8 1.063764 1.9389029 0.9424937
#> 9 4.414748 5.4695013 0.9377933
#> 10 9.797774 0.5699912 0.9456065
#> 11 9.166432 6.0109445 1.1113294
#> 12 6.859997 9.2476372 1.1460846
#> 13 8.610351 8.7533619 1.1875898
#> 14 7.862832 0.5033345 0.9384653
#> 15 6.289833 0.5094069 0.9370205
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.04 %
print(aug_2d)
#> x1 x2 Weight
#> 1 0.8000 10.000000 0.25
#> 2 10.0000 0.800000 0.25
#> 3 5.0000 5.000000 0.25
#> 4 9.9191 9.849981 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
)#> ℹ 960 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: 960
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
)#> ℹ 3514 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
)
#> ℹ 3497 candidate points with efficiency >= 0.85 (from LHS sample of 5000)
#> Sample of candidate points:
#> x1 x2 efficiency
#> 1 4.126848 8.800625 1.3971038
#> 2 4.726649 8.904344 1.6048992
#> 3 9.196152 7.968630 2.5440271
#> 4 8.946269 9.056518 2.7221034
#> 5 4.855862 9.936881 1.7393406
#> 6 1.036351 2.666206 0.9543814
#> 7 5.443666 6.840287 1.5610176
#> 8 5.739300 3.920053 1.0635992
#> 9 8.657005 3.424947 1.1570966
#> 10 8.014783 2.502456 0.8742776
#> 11 3.278351 1.178112 0.8787589
#> 12 2.298828 1.393648 0.9229633
#> 13 2.570354 7.349777 0.8683183
#> 14 3.311086 6.399931 0.9857228
#> 15 4.869097 9.877299 1.7390267
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.916312 9.955887 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.