set.seed(12)
x <- rpois(n = 3, lambda = 3)
x[1] 1 5 6
optim()August 9, 2026
This post contains notes on using R’s optim() function. This was originally an informal presentation to colleagues at the University of Virginia in March 2023.
Simulate data from a Poisson distribution.
Estimate the mean:
Given the data, we think the most likely value of lambda is 4. (The true value is 3.) The mean in this case happens to be the maximum likelihood estimator.
The Poisson probability mass function, where \(\lambda\) is the mean:
\[ P(X = x|\lambda) = \frac{e^{-\lambda}\lambda^x}{x!}\] For example, probability of obtaining 5 when \(\lambda = 3\):
\[P(X = 5|\lambda = 3) = \frac{e^{-3}3^5}{5!} \approx 0.101 \]
“By hand” in R:
Easier to use the dpois() function:
In the maximum likelihood approach, we find the value of \(\lambda\) that maximizes the product of the probabilities for the data we observed:
\[P(X = 1|\lambda)P(X = 5|\lambda)P(X = 6|\lambda)\]
We could try different values manually:
[1] 0.0001175112
Or with less code and R’s vectorization:
4 is more likely than 2 according to these products. The products themselves are not important. We simply want to find the maximum value for a given \(\lambda\).
For computing purposes, it’s easier to take the log of the probability distribution and then sum. (Recall: taking log of products turns into the sum of logs.) The dpois() function includes the log argument to make this easy.
[1] -9.048977
[1] -6.731211
Again the values are not important, we just want the lambda with the highest log likelihood.
We could try lots of lambdas and find the lambda that returns the highest log likelihood as follows:
[1] 4
Textbooks often visualize this with a plot:
It can be shown using some calculus that the simple mean of the values is the maximum likelihood estimator.
optim()Let’s simulate data from a Poisson distribution where lambda is conditional according to a function of 0.6 + 1.2*x1 + 1.4*x2 - 0.9*x1*x2. We use exp() to ensure the function returns a positive value for lambda, which is required for a Poisson distribution.
[1] 4 15 1 9 1 0
Imagine we don’t know what process generated this data but we think it came from a Poisson distribution with lambda conditional on the following function: b0 + b1*x1 + b2*x2 + b3*x1*x2 (which is correct). And we wish to use maximum likelihood to estimate the parameters: b0, b1, b2, and b3.
We could again try different values:
[1] -105837.7
[1] -152969.1
[1] -221947.3
We could also turn into a function to make trying values easier.
[1] -105837.7
[1] -152969.1
[1] -317848.3
But instead of trying numbers, let’s use an optimization algorithm. The base R optim() provides this service. It offers 6 different optimization algorithms. The default is one called “Nelder-Mead”. See the help page for more details and references.
Now optim() minimizes the objective function so we need to modify our function to return the negative log-likelihood.
Now we’re ready to use optim(). The only catch is we need to supply initial values for the function to be optimized. Let’s start with 1 for all parameters.
$par
[1] 0.6082687 1.1790786 1.3808820 -0.9042680
$value
[1] 698.3295
$counts
function gradient
181 NA
$convergence
[1] 0
$message
NULL
We can extract the parameters as follows:
If we set hessian = TRUE we can get estimated standard errors for the estimated parameters.
[,1] [,2] [,3] [,4]
[1,] 2544.174 1789.987 1397.558 -1618.187
[2,] 1789.987 4734.917 -1618.186 -1984.270
[3,] 1397.558 -1618.186 4852.198 2112.786
[4,] -1618.187 -1984.270 2112.786 7794.589
Have to “solve”, or take the inverse of, the Hessian matrix to get the variance, and then take square root of the diagonals to get the standard errors.
This is pretty much what we get when we use glm() to estimate the model parameters. It uses an optimization algorithm called “iteratively reweighted least squares (IWLS)”. See this page for more details and R code.
Call:
glm(formula = y ~ x1 + x2 + x1:x2, family = poisson)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 0.60887 0.04212 14.46 <2e-16 ***
x1 1.17901 0.02524 46.72 <2e-16 ***
x2 1.38003 0.02793 49.42 <2e-16 ***
x1:x2 -0.90403 0.01594 -56.70 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 5096.1 on 399 degrees of freedom
Residual deviance: 409.3 on 396 degrees of freedom
AIC: 1404.7
Number of Fisher Scoring iterations: 5
There is also the mle() function from the {stats4} package. The function it minimizes requires separate arguments. It has a summary method that returns standard errors of the estimated parameters.
Maximum likelihood estimation
Call:
mle(minuslogl = ll2, start = c(1, 1, 1, 1))
Coefficients:
Estimate Std. Error
[1,] 0.6088984 0.04211905
[2,] 1.1789938 0.02523835
[3,] 1.3800267 0.02792639
[4,] -0.9040193 0.01594304
-2 log L: 1396.657
optim()Insects lay eggs, but not all the eggs survive.

We could model this with a binomial distribution:
\[\text{survivors|eggs laid} \sim \text{binomial}(\text{eggs laid},p)\]
But we could also model number of eggs laid with a Poisson distribution:
\[\text{eggs laid} \sim \text{Poisson}(\lambda)\]
This is an example of a mixture model from Statistical Inference (Casella and Berger). 1
How could we fit such a model and estimate the parameters p and \(\lambda\)? This would be difficult to do using functions such as glm(). However we could use optim() with a custom log likelihood function.
First let’s define our likelihood functions and try different values by hand. We have two probability distributions:
Recall binomial is like a count distribution but capped at size:
Sum binomial log likelihood using observed data and different values for p, probability of surviving. We don’t need to do this. Only showing this to motivate use of optim().
[1] -1731.607
[1] -1124.161
Sum Poisson log likelihood using observed data and different values for lambda.
[1] -17693.09
[1] -13970.32
We can sum these likelihoods and try different values for p and lambda.
[1] -19424.7
[1] -14885.27
Now let optim() do the work for us. Remember, we need to make the function negative since optim() minimizes functions.
[1] 25.4177659 0.3024007
Standard errors of the estimated values:
Note: covariance is 0.
According to our model, eggs are drawn from a Poisson distribution with a mean (lambda) of about 25.4 (se 0.25), and the probability of survival is about 0.3 (se 0.004). Therefore expected number of eggs to survive on average is around 25.4 * 0.3 = 7.622
How I simulated the data.