Skip to content

Commit d8a33c6

Browse files
authored
Merge pull request #22 from jhelvy/design-fixes
Design fixes
2 parents f2814ba + 7b455dd commit d8a33c6

11 files changed

+908
-511
lines changed

.Rbuildignore

-3
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@
44
^docs$
55
^pkgdown$
66
^\.github$
7-
vignettes
87
^README\.Rmd$
98
^LICENSE\.md$
10-
^cjTools\.Rproj$
119
^doc$
1210
^Meta$
13-
^next_release\.md$
1411
^build\.R$
1512
^foo\.R$
1613
^cran-comments\.md$

CRAN-SUBMISSION

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Version: 0.5.0
2+
Date: 2023-07-11 10:27:50 UTC
3+
SHA: a1ceb125c1d1f805dfa343c723227d8efe2565f5

DESCRIPTION

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
Package: cbcTools
2-
Title: A Simulation-Based Workflow to Design and Evaluate Choice-Based Conjoint Survey Experiments
3-
Version: 0.4.0
2+
Title: Choice-Based Conjoint Experiment Design Generation and Power Evaluation in R
3+
Version: 0.5.0
44
Maintainer: John Helveston <[email protected]>
55
Authors@R: c(
66
person(given = "John",
77
family = "Helveston",
88
role = c("cre", "aut", "cph"),
99
email = "[email protected]",
1010
comment = c(ORCID = "0000-0002-2657-9191")))
11-
Description: A simulation-based workflow to design and evaluate choice-based conjoint survey experiments. Generate a variety of survey designs, including full factorial designs, orthogonal designs, and Bayesian D-efficient designs as well as designs with "no choice" options and "labeled" (also known as "alternative specific") designs. Full factorial and orthogonal designs are obtained using the 'DoE.base' package (Grömping, 2018) <doi:10.18637/jss.v085.i05>. Bayesian D-efficient designs are obtained using the 'idefix' package (Traets et al, 2020) <doi:10.18637/jss.v096.i03>. Conveniently inspect the design balance and overlap, and simulate choice data for a survey design either randomly or according to a multinomial or mixed logit utility model defined by user-provided prior parameters. Conduct a power analysis for a given survey design by estimating the same model on different subsets of the data to simulate different sample sizes. Choice simulation and model estimation in power analyses are handled using the 'logitr' package (Helveston, 2023) <doi:10.18637/jss.v105.i10>.
11+
Description: Design and evaluate choice-based conjoint survey experiments. Generate a variety of survey designs, including random designs, full factorial designs, orthogonal designs, D-optimal designs, and Bayesian D-efficient designs as well as designs with "no choice" options and "labeled" (also known as "alternative specific") designs. Conveniently inspect the design balance and overlap, and simulate choice data for a survey design either randomly or according to a multinomial or mixed logit utility model defined by user-provided prior parameters. Conduct a power analysis for a given survey design by estimating the same model on different subsets of the data to simulate different sample sizes. Full factorial and orthogonal designs are obtained using the 'DoE.base' package (Grömping, 2018) <doi:10.18637/jss.v085.i05>. D-optimal designs are obtained using the 'AlgDesign' package (Wheeler, 2022) <https://CRAN.R-project.org/package=AlgDesign>. Bayesian D-efficient designs are obtained using the 'idefix' package (Traets et al, 2020) <doi:10.18637/jss.v096.i03>. Choice simulation and model estimation in power analyses are handled using the 'logitr' package (Helveston, 2023) <doi:10.18637/jss.v105.i10>.
1212
License: MIT + file LICENSE
1313
Encoding: UTF-8
1414
LazyData: true
1515
Roxygen: list(markdown = TRUE)
1616
RoxygenNote: 7.2.3
17+
VignetteBuilder: knitr
1718
Depends:
1819
R (>= 3.5.0)
1920
Suggests:
2021
here,
22+
knitr,
2123
testthat,
2224
tibble
2325
Imports:
26+
AlgDesign,
2427
DoE.base,
2528
fastDummies,
2629
ggplot2,

NEWS.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# cbcTools (development version)
22

3+
# cbcTools 0.5.0
4+
5+
- Further revisions to the `method` argument in the `cbc_design()` function.
6+
- Added the `"random"` and `"dopt"` methods.
7+
- Added restrictions so that orthogonal designs cannot use the `label` argument or restricted profile sets (as either of these would result in a non-orthogonal design).
8+
39
# cbcTools 0.4.0
410

511
- Adjustments made to the `method` argument in the `cbc_design()` function in preparation for potentially adding new design methods.

R/design.R

+509-306
Large diffs are not rendered by default.

R/input_checks.R

+55-28
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,22 @@ check_inputs_restrict <- function(profiles) {
2727
}
2828

2929
check_design_method <- function(method, priors) {
30+
31+
# Check that an appropriate method is used
32+
33+
if (! method %in% c(
34+
'random', 'full', 'orthogonal', 'dopt', 'CEA', 'Modfed'
35+
)) {
36+
stop(
37+
'The "method" argument must be set to "random", "full", ',
38+
'"orthogonal", "dopt", "CEA", or "Modfed"'
39+
)
40+
}
41+
42+
# Check that a Bayesian method is used if priors are used
43+
3044
if (!is.null(priors)) {
31-
if (! method_is_bayesian(method)) {
45+
if (!method_is_bayesian(method)) {
3246
# Set method to 'CEA' if priors are specified and
3347
# user didn't specify an appropriate method.
3448
warning(
@@ -39,6 +53,7 @@ check_design_method <- function(method, priors) {
3953
method <- 'CEA'
4054
}
4155
}
56+
4257
return(method)
4358
}
4459

@@ -56,61 +71,73 @@ check_inputs_design <- function(
5671
priors,
5772
prior_no_choice,
5873
probs,
74+
keep_d_eff,
5975
keep_db_error,
6076
max_iter,
61-
parallel
77+
parallel,
78+
profiles_restricted
6279
) {
6380

81+
# Checks on blocking
82+
6483
if (n_blocks < 1) {
6584
stop('n_blocks must be greater than or equal to 1')
6685
}
6786

6887
if (n_blocks > n_resp) {
69-
stop("Maximum allowable number of blocks is one block per respondent")
88+
stop("Maximum allowable number of blocks is one block per respondent")
7089
}
7190

72-
# If using a Bayesian D-efficient design with a no choice option, user must
73-
# specify a value for prior_no_choice
74-
if (no_choice) {
75-
if (!is.null(priors) & is.null(prior_no_choice)) {
91+
if ((n_blocks > 1) & (method == 'random')) {
92+
stop(
93+
'The "random" method cannot use blocking. Either change the design ',
94+
'method or set "n_blocks = 1"'
95+
)
96+
if ((method == 'full') & profiles_restricted) {
7697
stop(
77-
'If "no_choice = TRUE", you must specify the prior utility ',
78-
'value for the "no choice" option using prior_no_choice'
98+
'The "full" method cannot use restricted profiles when blocking ',
99+
'is used. Either set "n_blocks" to 1 or use an unrestricted ',
100+
'set of profiles'
79101
)
80102
}
81103
}
82104

83-
# The labeled design isn't yet supported for Bayesian D-efficient designs
105+
# Checks on labeled designs
84106

85-
if (!is.null(label) & method_is_bayesian(method)) {
86-
stop(
87-
'The use of the "label" argument is currently not compatible with ',
88-
'Bayesian D-efficient designs'
89-
)
107+
if (!is.null(label)) {
108+
if (!method %in% c('random', 'full')) {
109+
stop(
110+
'Labeled designs are currently only supported with the "random" or ',
111+
'"full" method.'
112+
)
113+
}
90114
}
91115

92-
# Check that an appropriate method is used
116+
# Check on restricted profile sets
93117

94-
if (! method %in% c('full', 'orthogonal', 'CEA', 'Modfed')) {
118+
if (profiles_restricted) {
119+
if (!method %in% c('random', 'full')) {
95120
stop(
96-
'The "method" argument must be set to "full", "orthogonal", ',
97-
'"Modfed", or "CEA"'
121+
'Restricted profile sets can only be used with the "random", "full" ',
122+
'"dopt", or "Modfed" methods'
98123
)
124+
}
99125
}
100126

101127
# Check that priors are appropriate if specified
102128

103129
if (!is.null(priors)) {
104130

105-
# Check that user specified an appropriate method
106-
# This should already be handled
107-
if (! method_is_bayesian(method)) {
108-
stop(
109-
'Since "priors" are specified, the "method" argument must ',
110-
'be either "Modfed" or "CEA" to obtain a Bayesian ',
111-
'D-efficient design'
112-
)
113-
}
131+
# If using a Bayesian D-efficient design with a no choice option,
132+
# user must specify a value for prior_no_choice
133+
134+
if (no_choice & is.null(prior_no_choice)) {
135+
stop(
136+
'If "no_choice = TRUE" with the "CEA" or "Modfed" method, you must ',
137+
'specify the prior utility for the "no choice" option using ',
138+
'"prior_no_choice"'
139+
)
140+
}
114141

115142
# Check that prior names aren't missing
116143
prior_names <- names(priors)

R/power.R

+10-8
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,20 @@ extract_errors <- function(models) {
249249
#' @importFrom rlang .data
250250
#' @export
251251
#' @examples
252+
#' \dontrun{
252253
#' library(cbcTools)
253254
#'
254255
#' # Generate all possible profiles
255256
#' profiles <- cbc_profiles(
256-
#' price = c(1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5),
257+
#' price = c(1, 1.5, 2, 2.5, 3),
257258
#' type = c("Fuji", "Gala", "Honeycrisp"),
258259
#' freshness = c('Poor', 'Average', 'Excellent')
259260
#' )
260261
#'
261262
#' # Make designs to compare: full factorial vs bayesian d-efficient
262-
#' design_full <- cbc_design(
263+
#' design_random <- cbc_design(
263264
#' profiles = profiles,
264-
#' n_resp = 300, n_alts = 3, n_q = 6
265+
#' n_resp = 100, n_alts = 3, n_q = 6
265266
#' )
266267
#' # Same priors will be used in bayesian design and simulated choices
267268
#' priors <- list(
@@ -271,26 +272,27 @@ extract_errors <- function(models) {
271272
#' )
272273
#' design_bayesian <- cbc_design(
273274
#' profiles = profiles,
274-
#' n_resp = 300, n_alts = 3, n_q = 6, n_start = 1, method = "CEA",
275+
#' n_resp = 100, n_alts = 3, n_q = 6, n_start = 1, method = "CEA",
275276
#' priors = priors, parallel = FALSE
276277
#' )
277278
#'
278279
#' # Obtain power for each design by simulating choices
279-
#' power_full <- design_full |>
280+
#' power_random <- design_random |>
280281
#' cbc_choices(obsID = "obsID", priors = priors) |>
281282
#' cbc_power(
282283
#' pars = c("price", "type", "freshness"),
283-
#' outcome = "choice", obsID = "obsID", nbreaks = 10, n_q = 6, n_cores = 2
284+
#' outcome = "choice", obsID = "obsID", nbreaks = 5, n_q = 6, n_cores = 2
284285
#' )
285286
#' power_bayesian <- design_bayesian |>
286287
#' cbc_choices(obsID = "obsID", priors = priors) |>
287288
#' cbc_power(
288289
#' pars = c("price", "type", "freshness"),
289-
#' outcome = "choice", obsID = "obsID", nbreaks = 10, n_q = 6, n_cores = 2
290+
#' outcome = "choice", obsID = "obsID", nbreaks = 5, n_q = 6, n_cores = 2
290291
#' )
291292
#'
292293
#' # Compare power of each design
293-
#' plot_compare_power(power_bayesian, power_full)
294+
#' plot_compare_power(power_bayesian, power_random)
295+
#' }
294296
plot_compare_power <- function(...) {
295297
power <- list(...)
296298
design_names <- unlist(lapply(as.list(match.call())[-1], deparse))

0 commit comments

Comments
 (0)