How to Write and Contribute Designers

First, install the development version of DesignLibrary in R:

devtools::install_github("DeclareDesign/DesignLibrary", keep_source = TRUE)

A designer is a function that returns designs. For example, two_arm_designer() generates simple two-arm designs as DeclareDesign objects.

In addition to using our pre-made designers, you can contribute your own designers to the DesignLibrary, using the following guidelines.

Essential and Optional Features

At a bare minimum, your designer must:

Optionally, if you want your designer to work with our online shiny inspector then you will need to add the following attributes to your designer:

  • shiny_arguments - the parameters the shiny app should be able to show diagnoses for

  • tips - explanations of the optional parameters, given through shiny_arguments

  • description - a brief description of the design in html code

A Minimal Example

The following code creates a designer that creates a simple two-arm experiment of size, N, assigning units to treatment with probability prob. Note that the code is added to the attribute of the design using construct_design_code.

my_designer <- function(N = 100,
                        prob = .5){
  if(0 > prob | 1 < prob) stop("prob must be in [0,1]")
  if(1 > N) stop("design must have at least two units")
  {{{ 
    population <- declare_population(N = N, noise = rnorm(N))
    potential_outcomes <- declare_potential_outcomes(Y ~ Z + noise)
    assignment <- declare_assignment(prob = prob)
    estimand <- declare_estimand(ATE = mean(Y_Z_1 - Y_Z_0))
    estimator <- declare_estimator(Y ~ Z, estimand = estimand)
    reveal <- declare_reveal(Y,Z)
    my_design <- population +
      potential_outcomes +
      estimand +
      assignment +
      reveal +
      estimator
  }}}
  attr(my_design, "code") <- DesignLibrary:::construct_design_code(designer = my_designer,
                                                   args =  match.call.defaults())
  my_design
}

We’ll discuss features of this designer below.

Including Code in Design Attributes

We’ve devised an easy way to include code in design objects returned by a designer. But you can use your own method!

Our method for adding code

One easy way to add code to the designs that your designer returns is to use our triple braces method. Any checks (such as those that look to see whether prob is between 0 and 1) come before the opening triple braces, {{{ . Then, all the code needed to build the design goes between the triple braces:

{{{ 
    population <- declare_population(N = N, noise = rnorm(N))
    potential_outcomes <- declare_potential_outcomes(Y ~ Z + noise)
    assignment <- declare_assignment(prob = prob)
    estimand <- declare_estimand(ATE = mean(Y_Z_1 - Y_Z_0))
    estimator <- declare_estimator(Y ~ Z, estimand = estimand)
    reveal <- declare_reveal(Y,Z)
    my_design <- population +
      potential_outcomes +
      estimand +
      assignment +
      reveal +
      estimator
}}}

Our function construct_design_code goes into the designer and extracts all of the code between {{{ and }}} . Then, match.call.defaults() checks what arguments the user gave the function, and adds them to the top of the extracted code in a list that looks like this:

N <- 100                                                       
prob <- 0.5  

Thus, the following code in the example above adds the code that generated it to any design made by my_designer:

attr(my_design, "code") <- construct_design_code(designer = my_designer,
                                                 args =  match.call.defaults())

An alternative method

Here’s one example of an alternative way to embed the code that created it in a design returned by a designer:

my_designer <- function(N = 100,
                        prob = .5){
  design_code <- paste0(
    "population <- declare_population(N = ",N,", noise = rnorm(N))
    potential_outcomes <- declare_potential_outcomes(Y ~ Z + noise)
    assignment <- declare_assignment(prob = ",prob,")
    estimand <- declare_estimand(ATE = mean(Y_Z_1 - Y_Z_0))
    estimator <- declare_estimator(Y ~ Z, estimand = estimand)
    reveal <- declare_reveal(Y,Z)
    my_design <- population +
      potential_outcomes +
      estimand +
      assignment +
      reveal +
      estimator")
  my_design <- eval(parse(text = design_code))
  attr(my_design, "code") <- design_code
  my_design
}

Methods like this can be helpful when you need to build long strings of code on the fly (say, in a multi-arm experiment, or an study with a variable number of estimands).

Attributes Needed for Shiny Integration

If you want your designer to work with our shiny app, there are three attributes you should give to your designer (note: these are not attributes in the design – they get added after to the designer function after it is defined).

First, you need to provide the shiny_arguments – a list of scalar-valued vectors whose names correspond to some subset of the arguments in your designer. These will be the parameters that the shiny app varies over:

attr(my_designer,"shiny_arguments") <-
  list(
    N = c(50, 100, 10000),
    prob = c(.25, .50, .75)
    )

Next, we need to provide the tips, which will be descriptions that pop up and explain to users what the different parameters correspond to. This should list be the same length and have the same names as shiny_arguments.

attr(my_designer, "tips") <-
  list(
    N = "Sample size",
    prob = "Probability of assignment to treatment"
  )

Finally, you can add a brief description in html that will sit atop the shiny app.

attr(my_designer, "description") <- "
<p> A design of sample size <code>N</code> and probability of assignment <code>prob</code>.
"

Contributing Designers

The designer should be added to the DesignLibrary using a pull request.

How to document your designer

The documentation must be written as Roxygen comments and come at the top of the designer code; It should begin with a title and short description of what your designer does. When relevant, mention key limitations of your designer and add notes that can help others understand your code better.

#' Create a design
#'
#' This designer builds a design with \code{N} units. 
#'
#' Key limitations: ate cannot be specified
#' 
#' Note: Units are assigned to treatment with probability \code{prob} using complete random assignment  
#'

Next, list and describe all arguments needed in the design code following the syntax, @param name Description. You should also specify the output of your designer using the tag @return and provide an example that contains executable code.

#' @param N A integer. Sample size
#' @param prob A number within the interval [0,1]. Probability of assigment to treatment.
#' @return A design.
#' @examples
#' To make a design using default arguments:
#' my_design <- my_designer()
#'

You can also add keywords and reference other related designs and designers.

#' @concept two arm design
#' @seealso \code{\link{my_design}} \code{\link{two_arm_designer}} 

Finally, give yourself credit for your work.

#' 
#' @author \href{https://declaredesign.org/}{DeclareDesign Team}
#' @export