I am labeling this question as “statistics” because the motivation comes from a statistical application. The tl: dr version is that I want to change the binding of a variable in a closure within a loop. The function itself is defined outside the loop. I don’t have a lot of flexibility in defining the function because I am passing it to derivative evaluation functions in the ForwardDiff
package and those must be unary functions. Right now my approach is to declare the variable’s name as global before I define the function. I’m wondering if there is a more aesthetically pleasing approach.
So the background is that a nonlinear regression model function depends upon parameters and observed data. Generally the parameters are a relatively short vector and the data are columns in a table, like a DataFrame
. One of the simple (but real) examples in Bates and Watts (Wiley, 1988), Nonlinear Regression Analysis and Its Applications is data from an enzyme kinetics experiment
julia> data = DataFrame(conc = repeat([0.02,0.06,0.11,0.22,0.56,1.1], inner=2),
rate = float([76,47,97,107,123,139,159,152,191,201,207,200]))
12×2 DataFrame
│ Row │ conc │ rate │
│ │ Float64 │ Float64 │
├─────┼─────────┼─────────┤
│ 1 │ 0.02 │ 76.0 │
│ 2 │ 0.02 │ 47.0 │
│ 3 │ 0.06 │ 97.0 │
│ 4 │ 0.06 │ 107.0 │
│ 5 │ 0.11 │ 123.0 │
│ 6 │ 0.11 │ 139.0 │
│ 7 │ 0.22 │ 159.0 │
│ 8 │ 0.22 │ 152.0 │
│ 9 │ 0.56 │ 191.0 │
│ 10 │ 0.56 │ 201.0 │
│ 11 │ 1.1 │ 207.0 │
│ 12 │ 1.1 │ 200.0 │
for which the Michaelis-Menten model for reaction rate as a function of substrate concentration would be appropriate.
julia> function MicMen(pars, data)
Vm = pars[1]
K = pars[2]
conc = data.conc
@. Vm * conc / (K + conc)
end
MicMen (generic function with 1 method)
For these data, values Vm = 200.
and K = 0.05
give the predicted response
julia> show(MicMen([200., 0.05], data))
[57.14285714285714, 57.14285714285714, 109.0909090909091, 109.0909090909091, 137.5, 137.5, 162.96296296296296, 162.96296296296296, 183.60655737704917, 183.60655737704917, 191.30434782608697, 191.30434782608697]
I want to use methods from ForwardDiff
to evaluate the Jacobian matrix but the function argument to ForwardDiff.jacobian!
must be a function of pars
only. I can use a closure and generate
f(x) = MicMen(x, data)
in a context where data is defined so that is fine.
However, I also want to do this for several related groups of responses, say saved in a GroupedDataFrame
, where the value of pars
will change between groups. This is for fitting nonlinear mixed-effects models, described in Pinheiro and Bates (Springer, 2000), Mixed-Effects Models in S and S-PLUS. As I loop over the groups I need the binding of data
in the closure to change to each SubDataFrame. I am currently doing this by assigning a global variable _sdf
before defining f
and changing the global variable’s binding in the loop.
Are there better approaches? I feel that I should be able to do something with let
but I can’t seem to work out how to do so.