I am estimating several parameters using the mle() function with the L-BFGS-B method in R to data from an experiment in which participants had to make multiple choices. My goal is to obtain population estimates, i.e., pool all the data and find the set of parameters that describes aggregate choices best.
Since each participant provides some of the data, I want to control for heterogeneity in choice error between participants, i.e. control for the notion that person A's choices may be are more erratic than those of person B. As far as I know, I should pursue to cluster the standard errors (SEs) by participant. Whereas I have found packages on how to cluster SEs when using maximum likelihood estimation (MLE) to obtain the coefficients of (non-) linear models, e.g.
I have not found anything that is applicable to (the output of) mle(). My question has been asked before on the internet, https://stat.ethz.ch/pipermail/r-help/2014-July/376336.html but has remained unanswered so far.
Below is the basic model that I estimate and a small portion of the data. You will notice that the array "subject.id" is not utilised at the moment. My question is how I can incorporate it for clustered SEs.
### Functions
LL <- function(n,a,b,s)
{
V = (v(z1,n)-v(z2,n))*w(p,a,b) + v(z2,n)
res = zce - v.inv(V,n)
ll = dnorm(res, 0, s,log=T)
return(-sum(ll))
}
# with:
u <- function(x,n)
{
ifelse(n!=1,util <- x^(1-n)/(1-n), util <- log(x))
return(util)
}
u.inv <- function(x,n)
{
ifelse(n !=1, inv.util <- ((1-n)*(x))^(1/(1-n)), inv.util <- exp(x))
return(inv.util)
}
v = function(x,n){return(1/(u(maxz,n)-u(minz,n))*(u(x,n)-u(minz,n)))}
v.inv = function(x,n){return(u.inv(x*(u(maxz,n)-u(minz,n))+u(minz,n),n))}
w <- function(p,a,b){return(exp(-b*(-log(p))^(1-a)))}
maxz = 135
minz = 0
### Data
z1 <- c(0.1111111, 0.1037037, 0.1222222, 0.1111111, 0.1074074, 0.1666667, 0.1333333, 0.2000000, 0.1333333, 0.1074074,
0.1037037, 0.1111111, 0.1333333, 0.2000000, 0.1222222, 0.1111111, 0.1666667, 0.1333333, 0.1111111, 0.1333333,
0.1111111, 0.1666667, 0.1074074, 0.1333333, 0.1222222, 0.2000000, 0.1037037)
z2 <- c(0.08888889, 0.06666667, 0.07777778, 0.00000000, 0.03333333, 0.09259259, 0.09629630, 0.08888889, 0.06666667,
0.03333333, 0.06666667, 0.08888889, 0.06666667, 0.08888889, 0.07777778, 0.00000000, 0.09259259, 0.09629630,
0.00000000, 0.09629630, 0.08888889, 0.09259259, 0.03333333, 0.06666667, 0.07777778, 0.08888889, 0.06666667)
p <- c(0.5, 0.9, 0.5, 0.9, 0.9, 0.1, 0.1, 0.1, 0.5, 0.9, 0.9, 0.5, 0.5, 0.1, 0.5, 0.9, 0.1, 0.1, 0.9, 0.1, 0.5, 0.1, 0.9, 0.5, 0.5, 0.1, 0.9)
zce <- c(0.11055556, 0.10277778, 0.11000000, 0.10833333, 0.10185185, 0.11666667, 0.13240741, 0.14166667, 0.13166667,
0.07222222, 0.08796296, 0.09944444, 0.09500000,0.10833333, 0.09444444, 0.05277778, 0.10925926, 0.11759259,
0.05833333, 0.10277778, 0.09277778, 0.10925926, 0.06111111, 0.08833333, 0.09222222, 0.12500000, 0.09166667)
subject.id <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4)
### mle()
fit <- mle(LL,
start = list(n = 0.1,a=0.1,b=0.1,s=0.1),
method = "L-BFGS-B",
lower = list(n=-Inf,a = -Inf, b = 0.0001, s=0.0001),
upper = list(n=0.9999,a = 0.9999, b = Inf, s=Inf),
control = list(maxit = 500, ndeps = rep(0.000001,4)),
nobs=length(z1))
Some papers within the field I am working in have employed a seemingly simple method to account for individual heterogeneity in errors, but colleagues of mine have pointed out potential issues with this method (which induced me to post this question here). Next, I will explain this method and the critique I have heard regarding it and would be grateful for any further comments.
In order to account for individual heterogeneity in errors, previous literature has estimated an error variance s for each participant individually, like this:
### New LL
LL.id <- function(n,a,b,s1,s2,s4)
{
V = (v(z1,n)-v(z2,n))*w(p,a,b) + v(z2,n)
res = zce - v.inv(V,n)
sigma <- as.numeric(mget(paste("s",subject.id,sep="")))
ll = dnorm(res, 0, sigma,log=T)
return(-sum(ll))
}
### New mle()
fit.id <- mle(LL.id,
start = list(n = 0.1,a=0.1,b=0.1,s1=0.1,s2=0.1,s4=0.1),
method = "L-BFGS-B",
lower = list(n=-Inf,a = -Inf, b = 0.0001, s1=0.0001,s2=0.0001,s4=0.0001),
upper = list(n=0.9999,a = 0.9999, b = Inf, s1=Inf,s2=Inf,s4=Inf),
control = list(maxit = 500, ndeps = rep(0.000001,6)),
nobs=length(z1))
Potential issues with this method:
Using this method means there is a "huge" amount of free parameters that must be estimated, resulting in a model that is overfitted. Moreover, it becomes less likely that the global optimum is found by the optimisation algorithm, as there are simply too many dimensions.
It is undesirable that for each set of additional observations (i.e. adding the responses of an additional participant) there is an additional free parameter that must be estimated (this is, apparently, somehow related to the incidental parameters problem?).
To summarise, my questions boil down to this: is it possible to cluster SEs by participant for the mle that I am running?, if yes, how?, and will it be a better way to estimate the parameters compared to the alternative method that I presented?
I welcome any type of advice!
Update 1
For my actual estimation I have a few more parameters and a lot more observations (up to ±5000). As Achim rightly points out, it takes the estfun.mle some time with more data to complete calculation, which is why I have added parallel computing using mclapply from the "parallel" package, which works for mac and linux. For windows I recommend https://www.r-bloggers.com/implementing-mclapply-on-windows-a-primer-on-embarrassingly-parallel-computation-on-multicore-systems-with-r/:
library(parallel)
library(numDeriv)
library(stats4)
estfun.mle <- function(x, ...) {
form <- formals(x@minuslogl)
if(names(form)[length(form)] != "i" | !is.null(form[[length(form)]])) {
stop("cannot compute gradient contributions, last argument of minuslogl() must be i = NULL")
}
func <- function(par, i = NULL) -do.call(x@minuslogl, c(as.list(par), list(i = i)))
func2 <- function(i){grad(func, coef(x), i = i)}
save1 <- mclapply(1:nobs(x), func2, mc.cores = numcores)
save2 <- do.call(rbind,save1)
return(save2)
}
rms
packagerobcov
function shows how to do robust cluster-adjusted standard errors if you have a method that computes the score matrix. This may give you some programming hints. $\endgroup$robcov
to adapt to your needs. $\endgroup$