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 (33/s) | 660ms
#> ℹ The lower bound for efficiency is 99.9986187505804%
eff_before <- design_efficiency(init_des, result_opt)
#> ℹ The efficiency of the design is 38.5312233926718%
eff_after <- design_efficiency(augmented, result_opt)
#> ℹ The efficiency of the design is 34.2933573563283%
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 19 done (19/s) | 983ms
#> ℹ The lower bound for efficiency is 99.9990058762859%
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
)#> ℹ 1911 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)
)#> ℹ 1905 candidate points with efficiency >= 0.85 (from LHS sample of 2000)
#> Sample of candidate points:
#> x1 x2 efficiency
#> 1 0.7611782 1.8747102 0.9477295
#> 2 1.5770442 4.1902073 0.8810413
#> 3 5.0766615 4.8306139 0.9412320
#> 4 1.3090452 6.2035373 0.9028696
#> 5 7.0637485 7.2161732 1.0996926
#> 6 3.9557854 0.2860621 0.8832119
#> 7 9.3979549 4.0914459 1.0178637
#> 8 7.1685236 2.4781115 0.8925406
#> 9 0.8498649 6.5480135 0.9370562
#> 10 9.1585168 3.3176372 0.9660453
#> 11 9.8404978 2.5341046 0.9290985
#> 12 4.7510415 4.9451374 0.9332216
#> 13 6.7557968 5.2888438 1.0179250
#> 14 5.1546876 6.6373871 1.0083672
#> 15 7.1764757 6.3282685 1.0729088
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: 84.5 %
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.763098 9.637105 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
)#> ℹ 989 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: 989
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
)#> ℹ 3528 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
)
#> ℹ 3522 candidate points with efficiency >= 0.85 (from LHS sample of 5000)
#> Sample of candidate points:
#> x1 x2 efficiency
#> 1 6.7835260 0.402285 0.9071073
#> 2 6.7474203 3.590032 1.0783303
#> 3 7.4717268 9.361849 2.4541269
#> 4 0.5344098 9.185204 0.8655550
#> 5 3.2656891 5.063149 0.8815170
#> 6 5.9696669 4.167820 1.1425919
#> 7 0.2454912 3.297296 0.9301367
#> 8 0.1982677 5.470957 0.8785634
#> 9 7.4017492 9.145874 2.4039867
#> 10 9.7450771 5.072476 1.7978839
#> 11 4.6115665 5.714111 1.2064730
#> 12 0.5320061 8.010434 0.8772578
#> 13 6.9280476 9.716881 2.3647339
#> 14 8.2884997 9.759583 2.7066870
#> 15 7.4194035 9.800505 2.5053478
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.879799 9.766438 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.