TLDR
Consider nlsLM, a self-starting Gompertz model or use a method to calculate starting values, use it in a group_modify workflow.
Maybe something like this (though upper and lower limits may not be necessary
fit_gomp <- function(data, ...) {
nlsLM(formula = y ~ SSgompertz(x, Asym, b2, b3),
data = data,
lower = c(0,-Inf, -Inf, 0),
upper = c(2, Inf, Inf, 2),
...) %>% tidy()
}
data %>%
group_by(subject, race, target, distractor) %>%
group_modify(~ fit_qomp(data = .x), .keep = TRUE)
Getting starting values
While I haven't used a Gompertz model, consider if you can find a way to get starting values mathematically.
For example, let's say I want to fit a quadratic-plateau model (it only has 3 starting parameters however). First I have a function that defines the equation, which will go inside nls
later.
# y = b0 + b1x + b2x^2
# b0 = intercept
# b1 = slope
# b2 = quadratic term
# jp = join point = critical concentration
quadp <- function(x, b0, b1, jp) {
b2 <- -0.5 * b1 / jp
if_else(
condition = x < jp,
true = b0 + (b1 * x) + (b2 * x * x),
false = b0 + (b1 * jp) + (b2 * jp * jp)
)
}
The second part is to make a fitting function that fits a quadratic polynomial, uses those coefficients as starting values in the nls portion, and fits the nls model.
fit_quadp <- function(data, ...) {
# get starting values from simple quadratic
start <- lm(y ~ poly(x, 2, raw = TRUE), data = data)
start_values <- list(b0 = start$coef[[1]], # intercept
b1 = start$coef[[2]], # slope
jp = median(data$x)) # join-point
# nls model that uses those starting values
nlsLM(formula = y ~ quadp(x, b0, b1, jp),
data = data,
start = start_values,
...
) %>% tidy()
}
The ...
is to add arguments for nls.control if needed.
Analyzing grouped data
As for analyzing grouped data, I use group_modify()
because it returns a data frame whereas group_map()
returns a list. So my basic workflow looks like:
dataset %>%
group_by(grouping_variable_1, grouping_variable_2, ...) %>%
group_modify(~ fit_quadp(data = .x), .keep = TRUE)
Then out comes a table with all the tidy statistics because tidy()
was used in the function. You can consider including a try()
wrapped around the nls()
portion of the function so that if it succeeds on the first two groups but on the third, it'll still continue and you should still get some results.
nlsLM()
Also, if you want to use nlsLM
from minpack.lm
, the algorithm there succeeds more than those available in nls()
. Some worry about false convergence, but I haven't seen it yet in my applications. Also with nlsLM
you may not need to bother with upper and lower limits, though they can still be set.