Skip to content

Commit 0de886a

Browse files
skpr v1.9.1: Improve G-Efficiency calculation by explicitly sampling design space edges
1 parent 7681f8b commit 0de886a

File tree

6 files changed

+74
-53
lines changed

6 files changed

+74
-53
lines changed

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: skpr
22
Title: Design of Experiments Suite: Generate and Evaluate Optimal Designs
3-
Date: 2025-09-15
4-
Version: 1.9.0
3+
Date: 2025-09-19
4+
Version: 1.9.1
55
Authors@R: c(person("Tyler", "Morgan-Wall", email = "tylermw@gmail.com", role = c("aut", "cre")),
66
person("George", "Khoury", email = "george.m.khoury@gmail.com", role = c("aut")))
77
Description: Generates and evaluates D, I, A, Alias, E, T, and G optimal designs. Supports generation and evaluation of blocked and split/split-split/.../N-split plot designs. Includes parametric and Monte Carlo power evaluation functions, and supports calculating power for censored responses. Provides a framework to evaluate power using functions provided in other packages or written by the user. Includes a Shiny graphical user interface that displays the underlying code used to create and evaluate the design to improve ease-of-use and make analyses more reproducible. For details, see Morgan-Wall et al. (2021) <doi:10.18637/jss.v099.i01>.

R/calculate_gefficiency.R

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ calculate_gefficiency = function(
1919
if (is.null(randsearches)) {
2020
randsearches = 10000
2121
}
22-
variables = all.vars(get_attribute(design, "model"))
23-
designmm = get_attribute(design, "model_matrix")
22+
model = attr(design, "generating_model")
23+
variables = all.vars(model)
24+
designmm = attr(design, "model_matrix")
2425
modelentries = names(calculate_level_vector(
2526
run_matrix,
26-
get_attribute(design, "model"),
27+
model,
2728
FALSE
2829
))
2930
model_entries_mul = gsub(":", "*", modelentries)
3031
model_entries_mul[1] = 1
31-
fulllist = list()
3232
factorvars = attr(design, "contrastslist")
3333
variables = variables[!variables %in% names(factorvars)]
3434
rand_vector = function(factorlist, model_entries_mul) {
@@ -38,8 +38,16 @@ calculate_gefficiency = function(
3838
))
3939
matrix(fulloutput, 1, length(fulloutput))
4040
}
41-
41+
invXtX = solve(t(designmm) %*% designmm)
4242
calculate_optimality_for_point = function(x, infval = FALSE) {
43+
fulllist = list()
44+
if (any(x > 1) || any(x < -1)) {
45+
if (infval) {
46+
return(Inf)
47+
} else {
48+
return(10000)
49+
}
50+
}
4351
for (i in seq_len(length(x))) {
4452
fulllist[[i]] = x[i]
4553
}
@@ -52,17 +60,9 @@ calculate_gefficiency = function(
5260
}
5361

5462
mc_mm = rand_vector(fulllist, model_entries_mul)
55-
if (any(x > 1) || any(x < -1)) {
56-
if (infval) {
57-
return(Inf)
58-
} else {
59-
return(10000)
60-
}
61-
}
62-
100 *
63-
(ncol(designmm)) /
64-
(nrow(designmm) *
65-
max(diag(mc_mm %*% solve(t(designmm) %*% designmm) %*% t(mc_mm))))
63+
return(
64+
as.numeric(mc_mm %*% invXtX %*% t(mc_mm))
65+
)
6666
}
6767
modelentries = names(calculate_level_vector(
6868
run_matrix,
@@ -72,34 +72,43 @@ calculate_gefficiency = function(
7272
factorvars = attr(design, "contrastslist")
7373
variables = all.vars(get_attribute(design, "model"))
7474
variables = variables[!variables %in% names(factorvars)]
75+
76+
#First run edges
77+
candset = attr(design, "candidate_set")
78+
for (i in seq_len(ncol(candset))) {
79+
if (is.numeric(candset[[i]])) {
80+
num_col = candset[[i]]
81+
min_max = range(candset)
82+
is_exterior = num_col %in% min_max
83+
candset = candset[is_exterior, ]
84+
}
85+
}
86+
contrastslist = attr(design, "contrastslist")
87+
candset_mm = model.matrix(model, data = candset, contrasts = contrastslist)
88+
max_spv_verts = max(candset_mm %*% invXtX %*% t(candset_mm))
89+
7590
if (calculation_type == "random") {
7691
vals = list()
77-
lowest = 100
7892
for (i in seq_len(randsearches)) {
7993
vals[[i]] = calculate_optimality_for_point(
8094
2 * runif(length(variables)) - 1
8195
)
82-
if (vals[[i]] < lowest) {
83-
lowest = vals[[i]]
84-
}
8596
}
86-
return(min(unlist(vals)))
97+
max_interior_val = max(unlist(vals))
98+
max_val_overall = max(max_spv_verts, max_interior_val)
99+
return(100 * ncol(designmm) / (nrow(designmm) * max_val_overall))
87100
}
88101

89102
if (calculation_type == "optim") {
90103
vals = list()
91-
lowest = 100
92104
for (i in seq_len(randsearches)) {
93105
vals[[i]] = optim(
94106
2 * runif(length(variables)) - 1,
95107
calculate_optimality_for_point,
96108
method = "SANN"
97109
)$value
98-
if (vals[[i]] < lowest) {
99-
lowest = vals[[i]]
100-
}
101110
}
102-
return(min(unlist(vals)))
111+
return(100 * ncol(designmm) / (nrow(designmm) * max(unlist(vals))))
103112
}
104113
if (calculation_type == "custom" && !is.null(design_space_mm)) {
105114
return(GEfficiency(designmm, design_space_mm))

R/gen_design.R

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,8 @@ gen_design = function(
11401140
if (!is.null(custom_v)) {
11411141
V = custom_v
11421142
}
1143+
} else {
1144+
V = diag(trials) * varianceratio
11431145
}
11441146

11451147
initialreplace = FALSE
@@ -1975,6 +1977,8 @@ gen_design = function(
19751977
attr(design, "generating.criterion") = optimality
19761978
attr(design, "generating.contrast") = contrasts
19771979
attr(design, "contrastslist") = contrastslist
1980+
attr(design, "variance.matrix") = V
1981+
attr(design, "candidate_set") = og_candidate_set
19781982

19791983
if (!splitplot) {
19801984
if (blocking) {
@@ -2097,7 +2101,6 @@ gen_design = function(
20972101
error = function(e) {
20982102
}
20992103
)
2100-
21012104
if (!splitplot && !blocking) {
21022105
tryCatch(
21032106
{
@@ -2197,6 +2200,9 @@ gen_design = function(
21972200
}
21982201
)
21992202
}
2203+
if (is.null(attr(design, "I"))) {
2204+
attr(design, "I") = NA_real_
2205+
}
22002206

22012207
#Re-order factors so levels with the lowest number of factors come first
22022208
for (i in seq_len(ncol(design))) {
@@ -2327,7 +2333,6 @@ gen_design = function(
23272333
} else {
23282334
attr(design, "augmented") = FALSE
23292335
}
2330-
attr(design, "candidate_set") = og_candidate_set
23312336
modelmatrix_cor = model.matrix(
23322337
model,
23332338
design,

R/normalize_design.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ normalize_design = function(design, augmented = NULL) {
3535
"Design to be augmented and new design must have identical column classes"
3636
)
3737
}
38-
for (column in 1:ncol(design)) {
38+
for (column in seq_len(ncol(design))) {
3939
if (is.numeric(design[, column])) {
4040
midvalue = mean(c(
4141
max(c(design[, column], augmented[, column])),
@@ -46,7 +46,7 @@ normalize_design = function(design, augmented = NULL) {
4646
}
4747
}
4848
} else {
49-
for (column in 1:ncol(design)) {
49+
for (column in seq_len(ncol(design))) {
5050
if (is.numeric(design[, column])) {
5151
midvalue = mean(c(max(design[, column]), min(design[, column])))
5252
design[, column] = (design[, column] - midvalue) /

R/plot_fds.R

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ plot_fds = function(
136136
model = ~.
137137
model_matrix = model.matrix(
138138
model,
139-
design,
139+
data = design,
140140
contrasts.arg = temp_contrasts_list
141141
)
142142
factors = colnames(model_matrix)
@@ -145,13 +145,12 @@ plot_fds = function(
145145
new_model = TRUE
146146
model_matrix = model.matrix(
147147
model,
148-
design,
148+
data = design,
149149
contrasts.arg = temp_contrasts_list
150150
)
151151
factors = colnames(model_matrix)
152152
}
153153
#Need these inputs
154-
model_matrix = attr(skpr_output, "model_matrix")
155154
if (!is.null(attr(skpr_output, "runmatrix"))) {
156155
design = attr(skpr_output, "runmatrix")
157156
} else {
@@ -187,6 +186,25 @@ plot_fds = function(
187186
}
188187
normalized_candidate_set = normalize_design(candidate_set)
189188

189+
candset_verts = candidate_set
190+
for (i in seq_len(ncol(candset_verts))) {
191+
if (is.numeric(candset_verts[[i]])) {
192+
num_col = candset_verts[[i]]
193+
min_max = range(candset_verts[[i]])
194+
is_exterior = num_col %in% min_max
195+
candset_verts = candset_verts[is_exterior, , drop = FALSE]
196+
}
197+
}
198+
contrastslist = attr(design, "contrastslist")
199+
candset_mm = model.matrix(
200+
model,
201+
data = candset_verts,
202+
contrasts = contrastslist
203+
)
204+
invXtX = solve(t(model_matrix) %*% model_matrix)
205+
206+
spv_verts = diag(candset_mm %*% invXtX %*% t(candset_mm))
207+
190208
#Need to generate this here
191209
Iopt = attr(skpr_output, "I")
192210
if (is.na(Iopt)) {
@@ -204,7 +222,7 @@ plot_fds = function(
204222
} else {
205223
model_matrix_cor = model.matrix(
206224
model,
207-
design,
225+
data = design,
208226
contrasts.arg = temp_contrasts_list
209227
)
210228
}
@@ -214,7 +232,7 @@ plot_fds = function(
214232
diag(nrow(model_matrix_cor))
215233
)
216234
}
217-
if (is.null(Iopt)) {
235+
if (is.na(Iopt) || is.null(Iopt)) {
218236
stop(
219237
"No I-optimality value found in design--was your design generated outside of skpr? If so, pass in a high resolution candidate set to `high_resolution_candidate_set` to ensure I-optimality is computed."
220238
)
@@ -238,7 +256,7 @@ plot_fds = function(
238256
}
239257
sample_list = list()
240258

241-
for (col in 1:ncol(skpr_output)) {
259+
for (col in seq_len(ncol(skpr_output))) {
242260
if (inherits(skpr_output[, col], c("factor", "character"))) {
243261
vals = unique(skpr_output[, col])
244262
}
@@ -254,26 +272,15 @@ plot_fds = function(
254272
samples = as.data.frame(sample_list)
255273

256274
#------Normalize/Center numeric columns ------#
257-
for (column in 1:ncol(skpr_output)) {
258-
if (is.numeric(skpr_output[, column])) {
259-
midvalue = mean(c(max(skpr_output[, column]), min(skpr_output[, column])))
260-
skpr_output[, column] = (skpr_output[, column] - midvalue) /
261-
(max(skpr_output[, column]) - midvalue)
262-
}
263-
}
264-
mm = model.matrix(model, skpr_output, contrasts.arg = contrastlist)
275+
skpr_output_norm = normalize_design(skpr_output)
276+
mm = model.matrix(model, skpr_output_norm, contrasts.arg = contrastlist)
265277
samplemm = model.matrix(model, samples, contrasts.arg = contrastlist)
266278

267279
testcor = solve(t(mm) %*% solve(V) %*% mm)
268280

269-
v = list()
270-
271-
for (i in 1:nrow(samplemm)) {
272-
xi = samplemm[i, ]
273-
v[[i]] = t(xi) %*% testcor %*% xi
274-
}
281+
vals_interior = diag(samplemm %*% testcor %*% t(samplemm))
275282

276-
vars = do.call(rbind, v)
283+
vars = c(spv_verts, vals_interior)
277284
varsordered = vars[order(vars)]
278285
meanindex = which(
279286
abs(mean(varsordered) - varsordered) ==

tests/testthat/Rplots.pdf

9.04 KB
Binary file not shown.

0 commit comments

Comments
 (0)