Lecture 18 Maximum Likelihood in R

This R session turns the large-sample theory of lectures 16 and 17 into numbers. We find the maximum likelihood estimate numerically for a model where the likelihood equations cannot be solved explicitly, read off the standard errors from the output of the optimiser, and use simulation to check how often the interval θ^±1.96⁢se(θ^)\hat{\theta}\pm 1.96\,\mathop{\mathrm{se}}\nolimits(\hat{\theta}) from corollary 17.3 contains the truth.

18.1 The Log-Likelihood as an R Function

The model of the day is X1,…,XnX_{1},\dots,X_{n} i.i.d. from the gamma distribution with shape α\alpha and rate β\beta, both unknown. Example 4.9 gave the method of moments estimates α~=X¯2/V\tilde{\alpha}=\bar{X}^{2}/V and β~=X¯/V\tilde{\beta}=\bar{X}/V, with VV the plug-in variance, and section 5.3 noted that the likelihood equations have no closed-form solution. We simulate one sample of size n=40n=40 with α=2.5\alpha=2.5 and β=0.5\beta=0.5. The command dgamma with log = TRUE can be used to evaluate the logarithm of the density at every observation, and the sum of these values is ℓ⁢(α,β)\ell(\alpha,\beta). Since the R optimisers minimise, the function returns the negative log-likelihood, and since they hand over all parameters as one vector, it takes the vector par, shape first and rate second.

alpha <- 2.5
beta <- 0.5
n <- 40
set.seed(2026)
x <- rgamma(n, shape = alpha, rate = beta)
nll <- function(par, data) {
-sum(dgamma(data, shape = par[1], rate = par[2], log = TRUE))
}
nll(c(2.5, 0.5), x)
nll(c(1, 1), x)
## [1] 102.0691
## [1] 217.3799

The true parameter values lead to a much smaller negative log-likelihood than the pair (1,1)(1,1). The command optim can be used to minimise a function of a vector, starting from a given value. From section 16.4 we know that a good starting value is a simple, consistent estimator, such as the method of moments estimate. Before you try the code, predict whether the maximum likelihood estimates will agree with the method of moments estimates to one decimal place.

V <- mean((x - mean(x))^2)
start <- c(mean(x)^2 / V, mean(x) / V)
start
fit <- optim(start, nll, data = x)
fit$par
fit$convergence
fit$counts
## [1] 2.2942540 0.4221649
## [1] 2.258769 0.415672
## [1] 0
## function gradient
## 43 NA

The extra argument data = x is passed on to our function. The result fit is a list: fit$par holds the estimates, α^=2.26\hat{\alpha}=2.26 and β^=0.42\hat{\beta}=0.42, which agree with the start to one decimal place; fit$convergence is 0 if the search finished properly, and any other value means the estimates cannot be trusted; fit$counts records that 4343 evaluations were needed. By default optim uses the Nelder–Mead method, which needs no derivatives; appendix B shows the alternative "BFGS".

18.2 Standard Errors from the Observed Information

From theorem 17.1 and the vector version of this result from lecture 17 we know that the MLE is approximately normally distributed with covariance matrix ℐ︀n⁢(θ0)−1\mathcal{I}_{n}(\theta_{0})^{-1}. As in section 17.3, we can replace ℐ︀n⁢(θ^)\mathcal{I}_{n}(\hat{\theta}) by the observed information, i.e. the matrix of second derivatives of the negative log-likelihood at the maximum. The command optim can be used to compute this matrix, if we ask for it explicitly with hessian = TRUE. The matrix can be inverted using the solve command, the diag command gives the diagonal of this matrix, and the square roots of the diagonal elements are the standard errors. Before you look at the output, predict which of the two standard errors will be larger and by how much.

fit <- optim(start, nll, data = x, hessian = TRUE)
fit$hessian
covmat <- solve(fit$hessian)
se <- sqrt(diag(covmat))
result <- cbind(estimate = fit$par, se = se,
lower = fit$par - 1.96 * se, upper = fit$par + 1.96 * se)
rownames(result) <- c("shape", "rate")
round(result, 3)
cov2cor(covmat)[1, 2]
## [,1] [,2]
## [1,] 22.18712 -96.2299
## [2,] -96.22990 522.9200
## estimate se lower upper
## shape 2.259 0.473 1.333 3.185
## rate 0.416 0.097 0.225 0.606
## [1] 0.8933916

The table prints the results of a maximum likelihood estimate in an easy to read format. Each row corresponds to one parameter, and shows the parameter estimate, the standard error and the interval θ^±1.96⁢se(θ^)\hat{\theta}\pm 1.96\,\mathop{\mathrm{se}}\nolimits(\hat{\theta}) from corollary 17.3. Both intervals capture the true parameter values. The standard error for the shape parameter is approximately five times larger than the standard error for the rate parameter, but, since the estimates differ by a similar factor, the two standard errors are comparable relative to the estimates. The final row of the table, computed using the cov2cor function, shows that the two estimates have correlation 0.890.89. It is easy to show that, if we increase the shape and the rate simultaneously, while keeping the mean α/β\alpha/\beta fixed, the log-likelihood changes only slightly. Thus the ratio α/β\alpha/\beta is well determined by the data, but each parameter individually is determined only poorly. This is the problem of a nuisance parameter as discussed in lecture 14.

18.3 Starting Values

The gamma log-likelihood function has only one peak, so the choice of starting value matters little. Before you try the code, make a prediction: what happens if we start with very different values, e.g. with shape 3030 and rate 3030? Will optim still find the maximum? Will it take more than 4343 evaluations to find the maximum, when we used the “good” start?

fit_far <- optim(c(30, 30), nll, data = x)
fit_far$par
fit_far$counts
## [1] 2.2571696 0.4154238
## function gradient
## 97 NA

The search reaches the same maximum, up to two decimal places, but it needs more than twice as many evaluations. When the likelihood has several local maxima, as in section 5.3, a poor start can end at the wrong peak, since an optimiser only finds a local maximum. Thus we always start from a consistent estimator, and we re-run optim from a different start to see whether the solution changes.

18.4 Coverage of the Wald Interval

While corollary 17.3 shows that the interval θ^±1.96⁢se(θ^)\hat{\theta}\pm 1.96\,\mathop{\mathrm{se}}\nolimits(\hat{\theta}) covers the truth with probability approaching 0.950.95, it says nothing about moderate nn. To check the coverage for the gamma shape and rate, we use simulation in this section, following the approach to coverage introduced in lecture 12. The complete script for our simulation is given in the following box, with the lines shuffled (except for the bodies of the function and of the loop). Before you read the rest of this section, try to do the following: (1) write down the lines in a way which forms a working script, and (2) predict the coverages for n=20n=20: will they be above or below 0.950.95? Which of the two, shape or rate, will be worse?

data.frame(n = ns, shape = coverage[, 1], rate = coverage[, 2])
fit_gamma <- function(n, alpha, beta) {
x <- rgamma(n, shape = alpha, rate = beta)
V <- mean((x - mean(x))^2)
start <- c(mean(x)^2 / V, mean(x) / V)
fit <- optim(start, nll, data = x, hessian = TRUE)
c(fit$par, sqrt(diag(solve(fit$hessian))))
}
set.seed(2026)
for (i in seq_along(ns)) {
res <- replicate(5000, fit_gamma(ns[i], alpha, beta))
lower <- res[1:2, ] - 1.96 * res[3:4, ]
upper <- res[1:2, ] + 1.96 * res[3:4, ]
coverage[i, 1] <- mean(lower[1, ] <= alpha & alpha <= upper[1, ])
coverage[i, 2] <- mean(lower[2, ] <= beta & beta <= upper[2, ])
}
alpha <- 2.5
coverage <- matrix(0, nrow = length(ns), ncol = 2)
ns <- c(20, 100)
beta <- 0.5

Every name must exist before it is used: the function and the four assignments come first, with coverage after ns; then the seed, then the loop, and the table last. The one new detail is the return value, the two estimates followed by the two standard errors, which replicate stacks as the columns of a matrix; thus res[1:2, ] holds the estimates and res[3:4, ] the standard errors. The script in working order is R/S18-coverage.R; it fits the model 50005000 times for each sample size, takes a few seconds, and prints the following table.

## n shape rate
## 1 20 0.9624 0.9604
## 2 100 0.9508 0.9526

At n=100n=100 both coverages are within simulation error of the nominal 0.950.95, as expected from corollary 17.3, and at n=20n=20 both are slightly above it: the interval is a little too wide rather than too narrow.

18.5 A Task to Take Home

The following task is in the style of a practical report. Use your student ID to seed the random number generator, simulate a gamma sample of size 3030 with a shape and rate of your choice, and fit the model by maximum likelihood. For each parameter, report the estimate and its standard error.

Before the next session, you should type the commands used in this session, and should check that you get the same results as the text.

Summary.
  • •
    ​

    The written documentation for optim, solve and the Hessian matrix can be found in appendix B.

  • •
    ​

    The negative log-likelihood can be represented as an R function for one parameter vector, using the density with log = TRUE, and optim can be used to find its minimum, starting from a given point. It is important to check fit$convergence to see whether the optimisation was successful.

  • •
    ​

    If hessian = TRUE, the optimiser returns the observed information matrix. The inverse of this matrix can be used to approximately determine the covariance matrix of the MLE, and the square roots of the diagonal elements can be used to determine the standard errors reported in section 17.3. The results of a fit can be printed in a table which shows the estimates, standard errors and intervals for each parameter.

  • •
    ​

    A good starting point for the optimisation procedure is a consistent estimator, e.g. based on the method of moments. Since an optimiser finds a local maximum, it is important to re-run the optimisation procedure with a different starting point.

  • •
    ​

    For the gamma distribution, with shape and rate parameters, the coverage of the Wald interval is slightly above 95%95\% for n=20n=20 and close to 95%95\% for n=100n=100.