diff --git a/DESCRIPTION b/DESCRIPTION index 6db9707..2fc24ec 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: conjointTools Title: Tools For Designing Conjoint Survey Experiments -Version: 0.0.1 +Version: 0.0.2 Maintainer: John Helveston -Date: 2020-11-25 +Date: 2020-12-09 Authors@R: c( person(given = "John", family = "Helveston", @@ -27,6 +27,7 @@ RoxygenNote: 7.1.1 Imports: AlgDesign, rlang, - logitr (>= 0.0.4) + utils, + logitr (>= 0.0.5) Remotes: jhelvy/logitr diff --git a/NEWS.md b/NEWS.md index f91806f..4df3c6c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,11 +1,27 @@ + +# conjointTools 0.0.2 + +Interactions! + +## Summary of larger updates: + +- Added support for creating interactions among variables +- Removed summary plot function to drop ggplot dependencies + +## Summary of smaller updates: + +- Added a few new examples on how to use the interactions argument. + + + # conjointTools 0.0.1 -* Created new functions for creating different experiment designs: +- Created new functions for creating different experiment designs: - Full factorial - "D", "A", and "I" optimal designs -* Modified previous functions for creating a coded survey from a given experiment design -* Changed the output of the sampleSizer function to show the sample size (rather than the number of observations, which can be different since respondents may answer more than one choice question). +- Modified previous functions for creating a coded survey from a given experiment design +- Changed the output of the sampleSizer function to show the sample size (rather than the number of observations, which can be different since respondents may answer more than one choice question). # conjointTools 0.0.0.9000 -* Added a `NEWS.md` file to track changes to the package. +- Added a `NEWS.md` file to track changes to the package. diff --git a/R/sampleSizer.R b/R/sampleSizer.R index 3494f72..3f309c2 100644 --- a/R/sampleSizer.R +++ b/R/sampleSizer.R @@ -16,7 +16,8 @@ #' #' @param survey The choice survey data frame exported from the `makeSurvey()` function. #' @param parNames A vector of the names of the parameters to be estimated in the model. Must be the same as the column names in the `survey` argument. -#' @param parTypes A vector determining the type of each variable: "c" for continuous, or "d" for discrete. Continuous variables will be linearly coded whereas discrete variables will be dummy coded with one level removed for identification. Defaults to `NULL`, in which case all parameters are coded as discrete. +#' @param parTypes A vector determining the type of each variable: "c" for continuous, or "d" for discrete. Continuous variables will be linearly coded whereas discrete variables will be dummy coded with one level removed for identification. Defaults to `NULL`, in which case all parameters are coded as "d" for discrete. +#' @param interactions A logical value for adding interactions between all parameters in `parNames`. Defaults to `FALSE`. #' @param nbreaks The number of different sample size groups. #' @param randPars A named vector whose names are the random parameters and values the distribution: `'n'` for normal or `'ln'` for log-normal. Defaults to `NULL`. #' @param options A list of options to control the model estimation. @@ -24,21 +25,55 @@ #' @export #' @examples #' \dontrun{ +#' library(conjointTools) +#' +#' # A simple conjoint experiment about apples, with one attribute (price) +#' # modeled as continuous +#' +#' # Make the design of experiment +#' doe <- makeDoe( +#' levels = c(3, 3, 3), +#' varNames = c("price", "type", "freshness"), +#' type = "full" +#' ) +#' +#' # Make the survey +#' survey <- makeSurvey( +#' doe = doe, # Design of experiment +#' nResp = 1000, # Total number of respondents (upper bound) +#' nAltsPerQ = 3, # Number of alternatives per question +#' nQPerResp = 6 # Number of questions per respondent +#' ) +#' +#' # Compute sample sizes +#' results <- sampleSizer( +#' survey = survey, +#' parNames = c('price', 'type', 'freshness'), +#' parTypes = c('c', 'd', 'd'), # Set continuous vs. discrete variables +#' nbreaks = 10 +#' ) +#' +#' # Preview results +#' head(results) +#' +#' # Plot results +#' library(ggplot2) +#' +#' ggplot(results) + +#' geom_point(aes(x = size, y = se, color = coef), +#' fill = "white", pch = 21) + +#' scale_y_continuous(limits = c(0, NA)) + +#' labs(x = 'Number of observations', +#' y = 'Standard Error', +#' color = "Variable") + +#' theme_bw() #' } -sampleSizer = function(survey, parNames = NULL, parTypes = NULL, nbreaks = 10, - randPars = NULL, options = list(message = FALSE)) { - # Add random choices to the survey - survey$choice <- generateChoices(survey) - # Set parNames - if (is.null(parNames)) { - parNames <- names(survey)[!grepl("ID", names(survey))] - parNames <- parNames[-which(parNames == "choice")] - } - # Set continuous or discrete parameter types - if (!is.null(parTypes)) { - cpars <- parNames[which(parTypes == "c")] - survey[,cpars] <- as.numeric(survey[,cpars]) - } +sampleSizer = function(survey, parNames = NULL, parTypes = NULL, + interactions = FALSE, nbreaks = 10, randPars = NULL, + options = list(message = FALSE)) { + inputs <- setupInputs(survey, parNames, parTypes, interactions) + survey <- inputs$survey + parNames <- inputs$parNames # Loop through subsets of different sample sizes # In each iteration, estimate a model and record the standard errors maxObs <- max(survey['obsID']) @@ -57,7 +92,31 @@ sampleSizer = function(survey, parNames = NULL, parTypes = NULL, nbreaks = 10, return(do.call(rbind, standardErrors)) } -generateChoices <- function(survey) { +setupInputs <- function(survey, parNames, parTypes, interactions) { + # Set parNames + if (is.null(parNames)) { + parNames <- names(survey)[!grepl("ID", names(survey))] + parNames <- parNames[-which(parNames == "choice")] + } + # Recode survey variables as continuous or discrete + if (!is.null(parTypes)) { + cpars <- parNames[which(parTypes == "c")] + dpars <- parNames[which(parTypes == "d")] + survey[,cpars] <- lapply(survey[cpars], as.numeric) + survey[,dpars] <- lapply(survey[dpars], as.factor) + } + # Add interactions + if (interactions) { + ints <- t(utils::combn(parNames, 2)) + ints <- paste(ints[,1], ints[,2], sep = "*") + parNames <- c(parNames, ints) + } + # Add random choices to the survey + survey$choice <- generateChoices(survey, parNames, parTypes) + return(list(survey = survey, parNames = parNames)) +} + +generateChoices <- function(survey, parNames, parTypes) { nrows <- table(survey['obsID']) choices <- list() for (i in 1:length(nrows)) { diff --git a/README.Rmd b/README.Rmd index d78e3c9..1c6750f 100644 --- a/README.Rmd +++ b/README.Rmd @@ -30,6 +30,7 @@ This package contains tools for designing choice based conjoint survey experimen The current version is not yet on CRAN, but you can install it from Github using the **devtools** library: + ```{r, eval=FALSE} devtools::install_github("jhelvy/conjointTools") ``` @@ -44,12 +45,14 @@ library(conjointTools) Use the `makeDoe()` function to create a design of experiment Generate a full factorial design of experiment with three attributes, each with 2 levels: + ```{r} doe <- makeDoe(levels = c(2, 2, 2)) doe ``` You can also give your variables names based on the attributes. Here is an example of a full factorial design of experiment about apples: + ```{r} doe <- makeDoe( levels = c(2, 2, 2), @@ -59,6 +62,7 @@ doe ``` The `makeDoe()` function can also generate "D", "A", and "I" optimal fractional factorial designs: + ```{r} doe <- makeDoe( levels = c(2, 2, 2), @@ -79,6 +83,7 @@ doe <- makeDoe( varNames = c("price", "type", "freshness") ) ``` + ```{r} survey <- makeSurvey( doe = doe, # Design of experiment @@ -107,6 +112,7 @@ results <- sampleSizer( survey = survey, parNames = c('price', 'type', 'freshness'), parTypes = c('c', 'd', 'd'), # Set continuous vs. discrete variables + interactions = TRUE, # Add interactions between each attribute nbreaks = 10 ) @@ -133,7 +139,7 @@ ggplot(results) + - Date First Written: *October 23, 2020* - Most Recent Update: `r format(Sys.Date(), format="%B %d %Y")` - License: [MIT](https://github.com/jhelvy/conjointTools/blob/master/LICENSE.md) -- [Latest Release](https://github.com/jhelvy/conjointTools/releases/latest): 0.0.1 +- [Latest Release](https://github.com/jhelvy/conjointTools/releases/latest): 0.0.2 ## Citation Information diff --git a/README.md b/README.md index 7e83c5a..e095511 100644 --- a/README.md +++ b/README.md @@ -137,18 +137,19 @@ results <- sampleSizer( survey = survey, parNames = c('price', 'type', 'freshness'), parTypes = c('c', 'd', 'd'), # Set continuous vs. discrete variables + interactions = TRUE, # Add interactions between each attribute nbreaks = 10 ) # Preview results head(results) -#> size se coef -#> 1 100 0.09239510 price -#> 2 100 0.09404767 type_2 -#> 3 100 0.09376153 freshness_2 -#> 4 200 0.06612120 price -#> 5 200 0.06657439 type_2 -#> 6 200 0.06603612 freshness_2 +#> size se coef +#> 1 100 0.1611530 price +#> 2 100 0.3110412 type_2 +#> 3 100 0.3118941 freshness_2 +#> 4 100 0.1897302 price*type_2 +#> 5 100 0.1887158 price*freshness_2 +#> 6 100 0.1891048 type_2*freshness_2 ``` View a plot of the results @@ -170,12 +171,12 @@ ggplot(results) + ## Version and License Information - Date First Written: *October 23, 2020* - - Most Recent Update: December 06 2020 + - Most Recent Update: December 09 2020 - License: [MIT](https://github.com/jhelvy/conjointTools/blob/master/LICENSE.md) - [Latest Release](https://github.com/jhelvy/conjointTools/releases/latest): - 0.0.1 + 0.0.2 ## Citation Information @@ -188,16 +189,16 @@ citation("conjointTools") #> #> To cite conjointTools in publications use: #> -#> John Paul Helveston. conjointTools: Tools for designing conjoint -#> survey experiments. (2020) +#> John Paul Helveston, Martin Lukac, Alberto Stefanelli (2020). +#> conjointTools: Tools For Designing Conjoint Survey Experiments. #> #> A BibTeX entry for LaTeX users is #> #> @Manual{, -#> title = {conjointTools: Tools for designing conjoint survey experiments.}, -#> author = {John Paul Helveston}, +#> title = {conjointTools: Tools For Designing Conjoint Survey Experiments}, +#> author = {John Paul Helveston and Martin Lukac and Alberto Stefanelli}, #> year = {2020}, -#> note = {R package version 0.0.1}, +#> note = {R package version 0.0.2}, #> url = {https://jhelvy.github.io/conjointTools/}, #> } ``` diff --git a/inst/CITATION b/inst/CITATION index 8760ecf..7432e42 100644 --- a/inst/CITATION +++ b/inst/CITATION @@ -5,7 +5,7 @@ citEntry( title = "conjointTools: Tools For Designing Conjoint Survey Experiments", author = c("John Paul Helveston", "Martin Lukac", "Alberto Stefanelli"), year = "2020", - note = "R package version 0.0.1", + note = "R package version 0.0.2", url = "https://jhelvy.github.io/conjointTools/", textVersion = "John Paul Helveston, Martin Lukac, Alberto Stefanelli (2020). conjointTools: Tools For Designing Conjoint Survey Experiments." ) diff --git a/inst/example/interactions.R b/inst/example/interactions.R new file mode 100644 index 0000000..664c57e --- /dev/null +++ b/inst/example/interactions.R @@ -0,0 +1,99 @@ +library(conjointTools) +library(ggplot2) + +# Compute and compare standard errors for different sample sizes + +# Example 1 ------------------------------------------------------------------- + +# A simple conjoint experiment about apples +# The price attribute is continuous. +# ALL interactions between each attribute are estimated +# Full factorial design + +# Make the design of experiment +doe <- makeDoe( + levels = c(3, 3, 3), + varNames = c("price", "type", "freshness"), + type = "full" +) + +# Make the survey +survey <- makeSurvey( + doe = doe, # Design of experiment + nResp = 1000, # Total number of respondents (upper bound) + nAltsPerQ = 3, # Number of alternatives per question + nQPerResp = 6 # Number of questions per respondent +) + +# Compute sample sizes +results <- sampleSizer( + survey = survey, + parNames = c('price', 'type', 'freshness'), + parTypes = c('c', 'd', 'd'), + interactions = TRUE, # Add interactions between each attribute + nbreaks = 10 +) + +# Preview results +head(results) + +# Plot results +library(ggplot2) +results$int <- ifelse(grepl("\\*", results$coef), TRUE, FALSE) +ggplot(results) + + geom_point(aes(x = size, y = se, color = coef), + fill = "white", pch = 21) + + facet_wrap(vars(int)) + + scale_y_continuous(limits = c(0, NA)) + + labs(x = 'Number of observations', + y = 'Standard Error', + color = "Variable") + + theme_bw() + +# Example 2 ------------------------------------------------------------------- + +# A simple conjoint experiment about apples +# The price attribute is continuous. +# ALL interactions between each attribute are estimated +# D-efficient partial factorial design + +# Make the design of experiment +doe <- makeDoe( + levels = c(3, 3, 3), + varNames = c("price", "type", "freshness"), + type = "D", + nTrials = 15 +) + +# Make the survey +survey <- makeSurvey( + doe = doe, # Design of experiment + nResp = 1000, # Total number of respondents (upper bound) + nAltsPerQ = 3, # Number of alternatives per question + nQPerResp = 6 # Number of questions per respondent +) + +# Compute sample sizes +results <- sampleSizer( + survey = survey, + parNames = c('price', 'type', 'freshness'), + parTypes = c('c', 'd', 'd'), + interactions = TRUE, # Add interactions between each attribute + nbreaks = 10 +) + +# Preview results +head(results) + +# Plot results +library(ggplot2) +results$int <- ifelse(grepl("\\*", results$coef), TRUE, FALSE) +ggplot(results) + + geom_point(aes(x = size, y = se, color = coef), + fill = "white", pch = 21) + + facet_wrap(vars(int)) + + scale_y_continuous(limits = c(0, NA)) + + labs(x = 'Number of observations', + y = 'Standard Error', + color = "Variable") + + theme_bw() diff --git a/inst/example/sampleSize.R b/inst/example/sampleSize.R index 85cd030..d6535b4 100644 --- a/inst/example/sampleSize.R +++ b/inst/example/sampleSize.R @@ -1,4 +1,5 @@ library(conjointTools) +library(ggplot2) # Compute and compare standard errors for different sample sizes @@ -24,7 +25,14 @@ results <- sampleSizer(survey) head(results) # Plot results -sampleSizePlot(results) +ggplot(results) + + geom_point(aes(x = size, y = se, color = coef), + fill = "white", pch = 21) + + scale_y_continuous(limits = c(0, NA)) + + labs(x = 'Number of observations', + y = 'Standard Error', + color = "Variable") + + theme_bw() @@ -60,4 +68,11 @@ results <- sampleSizer( head(results) # Plot results -sampleSizePlot(results) +ggplot(results) + + geom_point(aes(x = size, y = se, color = coef), + fill = "white", pch = 21) + + scale_y_continuous(limits = c(0, NA)) + + labs(x = 'Number of observations', + y = 'Standard Error', + color = "Variable") + + theme_bw() diff --git a/man/figures/README-unnamed-chunk-10-1.png b/man/figures/README-unnamed-chunk-10-1.png index d480591..450bca3 100644 Binary files a/man/figures/README-unnamed-chunk-10-1.png and b/man/figures/README-unnamed-chunk-10-1.png differ diff --git a/man/sampleSizer.Rd b/man/sampleSizer.Rd index 79f435d..21ffda9 100644 --- a/man/sampleSizer.Rd +++ b/man/sampleSizer.Rd @@ -8,6 +8,7 @@ sampleSizer( survey, parNames = NULL, parTypes = NULL, + interactions = FALSE, nbreaks = 10, randPars = NULL, options = list(message = FALSE) @@ -18,7 +19,9 @@ sampleSizer( \item{parNames}{A vector of the names of the parameters to be estimated in the model. Must be the same as the column names in the \code{survey} argument.} -\item{parTypes}{A vector determining the type of each variable: "c" for continuous, or "d" for discrete. Continuous variables will be linearly coded whereas discrete variables will be dummy coded with one level removed for identification. Defaults to \code{NULL}, in which case all parameters are coded as discrete.} +\item{parTypes}{A vector determining the type of each variable: "c" for continuous, or "d" for discrete. Continuous variables will be linearly coded whereas discrete variables will be dummy coded with one level removed for identification. Defaults to \code{NULL}, in which case all parameters are coded as "d" for discrete.} + +\item{interactions}{A logical value for adding interactions between all parameters in \code{parNames}. Defaults to \code{FALSE}.} \item{nbreaks}{The number of different sample size groups.} @@ -45,6 +48,48 @@ rather a blank design of experiment with no observed choices. } \examples{ \dontrun{ +library(conjointTools) + +# A simple conjoint experiment about apples, with one attribute (price) +# modeled as continuous + +# Make the design of experiment +doe <- makeDoe( + levels = c(3, 3, 3), + varNames = c("price", "type", "freshness"), + type = "full" +) + +# Make the survey +survey <- makeSurvey( + doe = doe, # Design of experiment + nResp = 1000, # Total number of respondents (upper bound) + nAltsPerQ = 3, # Number of alternatives per question + nQPerResp = 6 # Number of questions per respondent +) + +# Compute sample sizes +results <- sampleSizer( + survey = survey, + parNames = c('price', 'type', 'freshness'), + parTypes = c('c', 'd', 'd'), # Set continuous vs. discrete variables + nbreaks = 10 +) + +# Preview results +head(results) + +# Plot results +library(ggplot2) + +ggplot(results) + + geom_point(aes(x = size, y = se, color = coef), + fill = "white", pch = 21) + + scale_y_continuous(limits = c(0, NA)) + + labs(x = 'Number of observations', + y = 'Standard Error', + color = "Variable") + + theme_bw() } } \keyword{logit,}