User Curve Functions

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(bdots)
# Make smaller for cran
cohort_unrelated$Subject <- as.numeric(cohort_unrelated$Subject)
cohort_unrelated <- as.data.table(cohort_unrelated)
cohort_unrelated <- cohort_unrelated[Subject < 10, ]

We saw in the general overview when first generating our model fits with bdotsFit that we we could specify the curve with the argument curveType. Presently, the bdots package contains three options for this: doubleGauss, logistic, and polynomial. Documentation is included for each of these curves.

library(bdots)

fit <- bdotsFit(data = cohort_unrelated,
                subject = "Subject",
                time = "Time",
                y = "Fixations",
                group = c("DB_cond", "LookType"),
                curveType = doubleGauss(concave = TRUE),
                cores = 2)

Note that each of these is a function in their own right and must be passed in as a call object. Curve functions that include arguments further specifying the type of curve, i.e., doubleGauss(concave = TRUE) and polynomial(degree = n), should include these when the call is passed into bdotsFit as seen in the example above.

Because each of the functions exists independently of bdotsFit, users can specify their own curve functions for the fitting and bootstrapping process. The purpose of this vignette is to demonstrate how to do so. If you find that you have a curve function that is especially useful, please create a request to have it added to the bdots package here.

We will examine the doubleGauss function in more detail to see how we might go about creating our own. First, let's identify the components of this function

doubleGauss

There are four things to note:

  1. Arguments : In addition to the argument concave = TRUE, which specifies the curve, we also have dat, y, time, params = NULL, and .... These are the names that must be used for the function to be called correctly. The first represents a data.frame or data.table subset from the data argument to bdotsFit, while y and time correspond to their respective arguments in bdotsFit and should assume that the arguments are passed in as character. It's important to remember to set params = NULL, as this is only used during the refitting step.
  2. Body : As can be seen here, when params = NULL, the body of the function computes the necessary starting parameters to be used with the gnls fitting function. In this case, the function dgaussPars handles the initial parameter estimation and returns a named numeric. When params is not NULL, it's usually a good idea to verify that it is the correct length and has the correct parameter names.
  3. Formula : Care must be exercised when creating the formula object, as it must be quoted. One may use bquote and str2lang to substitute in the character values for y and time. Alternatively, if this is to only be used for a particular data set, one can simply use quote with the appropriate values used for y and time, as we will demonstrate below. Finally, the quoted formula should contain a single attribute parnames which has the names of the parameters used.
  4. Return Value : All of the curve functions should return a named list with two elements: a quoted formula and params, a named numeric with the parameters.

Briefly, we can see how this function is used by subsetting the data to a single subject and calling it directly.

## Return a unique subject/group permutation
dat <- cohort_unrelated[Subject == 1 & DB_cond == 50 & LookType == "Cohort", ]
dat
## See return value
doubleGauss(dat = dat, y = "Fixations", time = "Time", concave = TRUE)

We will now create an entirely new function that is not included in bdots to demonstrate that it works the same; the only change we will make is to substitute in the values for y and time without using str2lang. For our data set here, the corresponding values to y and time are "Fixations" and "Time", respectively

doubleGauss2 <- function (dat, y, time, params = NULL, concave = TRUE, ...) {

  if (is.null(params)) {
    ## Instead of defining our own, just reuse the one in bdots
    params <- bdots:::dgaussPars(dat, y, time, concave)
  }
  else {
    if (length(params) != 6) 
      stop("doubleGauss requires 6 parameters be specified for refitting")
    if (!all(names(params) %in% c("mu", "ht", "sig1", "sig2", 
                                  "base1", "base2"))) {
      stop("doubleGauss parameters for refitting must be correctly labeled")
    }
  }

    ## Here, we use Fixations and Time directly
    ff <- bquote(Fixations ~ (Time < mu) * (exp(-1 * (Time - mu)^2 / 
                  (2 * sig1^2)) * (ht - base1) + base1) + (mu <= Time) * 
                  (exp(-1 * (Time - mu)^2/(2 * sig2^2)) * (ht - base2) + base2))
    return(list(formula = ff, params = params))
}

same_fit_different_day <- bdotsFit(data = cohort_unrelated,
                                   subject = "Subject",
                                   time = "Time",
                                   y = "Fixations",
                                   group = c("DB_cond", "LookType"),
                                   curveType = doubleGauss2(concave = TRUE),
                                   cores = 2)

Seeds have not yet been implemented, so there is some possibility that the resulting parameters are slightly different; however, using the coef function, we can roughly confirm their equivalence

## Original fit
head(coef(fit))

## "New" fit
head(coef(same_fit_different_day))


Try the bdots package in your browser

Any scripts or data that you put into this service are public.

bdots documentation built on Jan. 7, 2023, 1:18 a.m.