4  Generalised Linear Models (GLM)

It is possible to generalise the standard regression models you already know so that you can handle cases where your residuals are not Normally distributed. Remember that it’s the errors that matter: in previous statistics classes you may have been told that the distribution of the response variable is what matters the most, but this was an over-simplification. These kinds of models are not just more useful because they fit your data well, they also provide predictions that make more sense. Generalized Linear Models (GLMs) can be used to predict integers (e.g., there should be four or five species over there, not 4.5), fractions and percentages (e.g., 99% of people want pizza for dinner, not 101%), and binary variables (e.g., the patient will live or die, there’s nothing inbetween). GLMs can be a bit scary at first: focus on the concepts and make sure you’ve nailed down the two pieces that are at play: the error structure and the link function. There is a lot of extension material here: I would be quite happy if, for example, you focused only on the Poisson example, or watched the lecture video (which gives a very high-level overview) and then attempted only the exercise and/or to follow along with the code examples below. If you have been following the course so far and found it easy, then great, attempt these exercises, but otherwise consider going back through all the material covered so far, perhaps including the extension exercises there, and reviewing that.

4.2 Error structures

Residuals (errors) are not always Normally distributed. In an ideal world, we would fit models that wouldn’t predict ‘half a species’ (0.5) or any kind of non-integer number of species. GLMs let us specify this by defining the error structure of our data. It’s simply a way of telling R what kinds of errors to expect, and it lets R adjust its model-fitting procedures accordingly1.

Error structures go hand-in-hand with link functions, and the choice of error structure is often what determines the link function you use. Putting that a little more formally, while it is possible to mix-and-match link functions and error structures, there is almost always a canonical link function associated with each error distribution that you’re better-off using. These links have been derived analytically, and are the pairings most likely to give you precise, unbiased predictions. My advice is to let the kind of data you have determine your error structure, and then always to use the canonical link function, although if you are interested it is possible to statistically test which link is most appropriate using \(F\)-tests (ask me if you are interested). The canonical error distribution for the Poisson distribution that were working with today is linear—the variance is directly proportional to the mean. I outline this in more detail below.

4.3 Practical example: count data and the Poisson family

Count data are weird. As we’ve already discussed, you can’t have negative counts (they’re bounded), and they can only be integers (whole numbers, not fractions—discrete data). Additionally, the variance of count data tends to increase with its mean: they show non-constant variance. Putting that another way, if I asked you to guess how many people were in the biology department right now, you might guess something like \(100 \pm 10\). If I asked you to guess how many people in the university right now, you might guess something like \(5000 \pm 1000\)—you definitely wouldn’t guess \(5000 \pm 10\) because you’d understand, intuitively, that the error in your estimate has increased with the mean.

The error distribution that has all these properties is called the Poisson distribution, and is named after the French mathematician Siméon Denis Poisson. Unlike the Normal distribution, which has two parameters (the mean—\(\mu\)—and the variance—\(\sigma^2\)), the Poisson has only one parameter—\(\lambda\) (‘lambda’)—which parameterizes both the mean and variance of the distribution. As a result, it’s almost always positively skewed (it has a long positive tail). It also only ever predicts whole numbers (it’s discrete), which makes it perfect for count data. You can use something like hist(rpois(1000, 5)) to plot a histogram of 1000 draws from a Poisson distribution with a \(\lambda\) of 5; play around with different \(\lambda\) values to get a feel for the distribution.

Let’s simulate some data that represents the abundance of a species across an temperature gradient. The species is more abundant when it gets warmer, and its abundance across sites is drawn from a Poisson distribution. First of all, we’ll model the data using a standard linear regression, just to see what a poor job it does of explaining this data.

# Simulate data
temp.sites <- seq(-25, 25, by=.5)
lambda.sites <- temp.sites + abs(rnorm(length(temp.sites)))
abundance <- numeric(length(lambda.sites))
for(i in seq_along(abundance))
  abundance[i] <- rpois(1, lambda.sites[i])
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
#> Warning in rpois(1, lambda.sites[i]): NAs produced
abundance[is.na(abundance)] <- 0
# Plot data
plot(abundance ~ temp.sites)
# Model data (poorly)
bad.model <- lm(abundance ~ temp.sites)
# Plot the model predictions
abline(coef(bad.model), col="red", lwd=3)

# Inspect residuals of our model
hist(residuals(bad.model))

Notice how the residuals in our model are very clearly not Normally distributed. OK, now let’s fit a better model to our data, compare its model performance, and then plot the predictions from it.

# Fit a better model
good.model <- glm(abundance ~ temp.sites, family=poisson)
# Inpsect residuals
hist(residuals(good.model))

# Summarize model
summary(good.model)
#> 
#> Call:
#> glm(formula = abundance ~ temp.sites, family = poisson)
#> 
#> Coefficients:
#>             Estimate Std. Error z value Pr(>|z|)    
#> (Intercept) 0.869837   0.081771   10.64   <2e-16 ***
#> temp.sites  0.105878   0.004472   23.68   <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: 1116.2  on 100  degrees of freedom
#> Residual deviance:  197.4  on  99  degrees of freedom
#> AIC: 410.28
#> 
#> Number of Fisher Scoring iterations: 5

The residuals look better here than before, but note that our GLM is still getting strange (but not as strange) residuals from all those zero values we have in our model2. A thing I do want you to notice is that the residuals still look somewhat strangely distributed: by default, R plots the same kinds of residuals as it does for a standard regression. If you want to examine the working residuals (what the GLM is fit to, once your data have been put through the link function) check out the help file for residuals.glm3. When you’re looking at the model summary, ignore anything related to deviance as we’re going to cover this in a later session—for now, it’s sufficient to think of these as related to the \(r^2\) values you know from standard regression. Since our data are not Normally distributed, we can’t use \(r^2\) values any more, but deviance is a generalization of the same essential concept. Our coefficients are all on the same scale as the link function, so to work with them, we need to transform everything. So let’s do that, and compare the fit of our models.

# Make model predictions
predictions <- data.frame(temp.sites=seq(-25, 25, .01))
predictions$good <- predict(good.model, predictions, type="response")
predictions$bad <- predict(bad.model, predictions)
# Compare model predictions
plot(abundance ~ temp.sites, pch=20)
with(predictions, lines(good ~ temp.sites, col="red", lwd=3))
with(predictions, lines(bad ~ temp.sites, col="blue", lwd=3))

First of all, notice how, when transforming my coefficients, I have to add all of them together and then back-transform. You can’t take the exponent of the slope, multiply that by the temperature, and then add that to the exponent of the intercept. The entire regression equation must be transformed together; this is simply how logarithms work, and if the math is not obvious to you just remember then just follow my rule in italics. Notice how I can get R to do my back-transforming for me by specifying type="response" when using the predict function. Finally, there are two things I hope are clear from the plot of our model predictions: (1) the Poisson GLM is doing a much better job of capturing the shape of our data, and (2) it is curved when plotted in linear space because our link function is a logarithm.

4.4 Reviewing the Binomial distribution

The Binomial distribution is one of the most important distributions in mathematics. It underlies a lot of work in the theory of combinations and permutations, which, in turn, underlies a lot of the work in cryptography. You might find it surprising that there are many questions we can ask using special cases of the Binomial distribution that we can’t actually find an answer for!

The Binomial distribution gives the probability of a given event happening a given number of times out of a given number of trials. It can be used to answer questions like “what’s the probability I will get five heads if I toss a coin thirty times” or “how many of these seventy patients would we expect to survive given a 20% mortality rate”. Thus the Binomial distribution is defined by two parameters: the probability of success, \(p\), and the number of trials, \(n\). In the special case where only a single trial occurs, the Binomial distribution simplifies down to something called the Bernoulli distribution. Note that the Binomial distribution is, itself, a special case of the Multinomial distribution; the multinomial is used in cases where there is more than one kind of event that can happen, such as questions like “what’s the probability I’ll roll a 1, 2, 3, 4, or 6 if I roll a die five times”.

The Binomial distribution is well-suited to cases where we have bounded data, since it can only produce estimates between 0 and 1 (inclusively). Examples of this kind of data are percentages (99% of people love statistics!) and fractions (49 out of 50 people in the class love statistics!). The Binomial also has non-constant variance: it shows a quadratic relationship with the value of mean (lesser closer to 0 and 1, higher closer to 0.5). This often maps onto how we would want our model to perform: our model probably does worse with intermediate cases, but predicts the extremes much better. Figure 4.1 shows the expectations for the variances of the three distributions we are covering in this class.

Figure 4.1: A graphical overview of the variances of the Generalized Linear Model families covered in this class. In this class we cover models from the Normal (Gaussian) family (in black; sections 1–4), Poisson (blue; session 5), and Binomial (red; this session). While the Normal family can essentially any constant variance (denoted by \(\sigma\), as discussed in lecture 1), the Poisson has a single parameter (\(\lambda\)) that defines both its mean and variance. The variance of the Binomial, which we cover today, is defined as \(np(1-p)\) where \(n\) is the number of trials and \(p\) is the probability of success (thus \(1-p\) is the probability of failure, and is sometimes denoted \(q\)). See also Table 4.1

4.6 Special cases of the Binomial

Classes in Generalized Linear Models can become very complex when the Binomial family is tackled, because there are two ‘special cases’ of the Binomial family that are usually treated as separate problems. They’re not, and in the hopes of simplifying things for you I want to present them as what they really are: special cases of the application of a Binomial GLM.

Binary response variables.
Data are often presented as binary response variables: infected or healthy, a coin came up heads or it didn’t, a student liked the class or they didn’t. Such binary data can be modeled with a Binomial GLM because such data can still be modeled as drawn from a Binomial distribution. The only difference now is that, in each case, the number of trials (\(n\)) is 1: that is the only difference. Indeed, in many cases data that could be presented in the format described above (successes vs. failures) can be re-coded as a binary response variable by ‘expanding’ out the data. For example, if I tossed 100 coins and got 48 heads, that could either be coded as 48 successes and 52 failures (two pieces of data) or expanded out to be 100 pieces of data, each describing the outcome of a single coin toss (success/fail, head/tail, 1/0, or true/false). It would give exactly the same answer in a GLM because it’s exactly the same mathematics.

Percentage response variables.
Percentages are really just fractions: a number of times something happened divided by the total number of times something could have happened6. Knowing how many times something could have happened is extremely important. Imagine I told you I tested my die and it rolled a six 50% of the time: would you be surprised? You probably would be if I told you I’d rolled it 1000 times, but if I’d only rolled it twice you probably wouldn’t be. This problem permeates through into all statistics: if you don’t know the numerator (1 or 500 in my die example) and the denominator (2 or 1000 in my die example) underlying some percentage data, you are missing critical information that will help you do your job better. Thus, whenever possible, work with the raw counts that generated the percentages. If you know the percentage and the number of trials, it’s possible to reconstruct the raw data (\(successes=\frac{n}{\%}\)) and you should do so.

We do not, however, live in a perfect world, and the reality is that as a practicing statistician you will rarely be called in to analyze datasets that are perfectly formatted and set up exactly as you would wish. In the event that you are given a percentage, and do not know the underlying counts that it came from, it is still possible to work with that data. However, it’s not possible to model those data using a Binomial GLM: without any information about the number of trials (\(n\)) it simply isn’t possible. Your best bet is to transform your response variable, using something like an arc-sine transformation, and model that using a standard linear regression7. If I may give you a word of caution and/or advice, however: if the person who gave your the percentage won’t tell you the number of trials, be very hesitant about moving forward analyzing that data. I can’t really imagine how that’s the kind of data one could lose, and I would want a very good explanation of why that data wasn’t available. I am perhaps a tad paranoid because I briefly worked in industry, where such information would be witheld deliberately because of… politics.

4.7 How to tell if a GLM fits well

For multiple regressions, you already know the answer: the \(r^2\) of a model is its ability to predict the data on which it was fit. A number closer to 1 is better than a number closer to 0, and what value is absolutely good (is 20% good?) depends on your field and question. Of course, if a model isn’t statistically significant (as measured by an \(F\)-test; see below for a refresher) then it doesn’t really matter whether the \(r^2\) is good or not, but if you find you have a good \(r^2\) and no significance then you’ve almost certainly done something very peculiar and so should think about whether you have enough data to fit a model full stop.

For everything else, the problem is more complex. There is no such thing as an \(r^2\) for anything other than a multiple regression (i.e., a GLM with the identity link). People commonly calculate a form of pseudo-\(r^2\) where they take model predictions and correlate the original data with those: that’s fine and everything, but it’s not an \(r^2\) and so it’s not clear what that means. Real statisticians use the deviance of a model: it is what is minimized to fit your model to data, and its definition is determined by your error distribution. Table 4.1 lists the most common ones; you don’t need to memorize them, but it can be useful to know what they are.

You will have noticed that the deviance for Normal errors is just the sum of the squares; the sums of squares are, indeed, a special case of the deviance. That’s why bad statisticians often plop the deviance into the \(r^2\) formula I’ve already given you, and use that to calculate an \(r^2\). Such people are silly, because they don’t propagate the changes through into the other parts of the \(r^2\) equation (because it can’t be done) and so real statisticians like to make fun of such people in the pub after conferences. Remember that, during the fitting process, each datapoint is weighted according to the error distribution (see Figure 4.1): thus each datapoint is no longer equally important, but this change isn’t sensibly propagated through the mathematics if you just divide the null by the residual deviance. Thus the ratio of the two numbers is not quite meaningless, but it’s not an \(r^2\) value. Real statisticians report the null deviance (the amount of deviance in their data to begin with), their residual deviance (the amount left behind by their model), and plot the raw data with the model predictions. This is sufficient.

Table 4.1: Some common deviance equations. The error structure, deviance equation, and relationship between the predicted response and its error, are given in each column, respectively. \(y\) is the response variable, \(\bar{y}\) the mean value of \(y\), \(\mu\) the predicted response value, \(n\) is number of trials, \(p\) is the probability of success, and \(log\) is the natural logarithm. Note, in particular, the last column: this expected variation is why it’s often not possible to get an \(r^2\) from non-Gaussian GLMs: the expectation of error is not the same for all predictions, thus not all error is equally important, thus not all deviance is equally important, thus you cannot simply sum deviance as where the deviance ‘comes from’ matters. No equation is given for the logistic deviance, since this is a special case of the Binomial. See also Figure 4.1.
Error structure Deviance equation Variance function
Gaussian (Normal) \(\sum(y - \bar{y})^2\) \(\propto 1\) (i.e., a constant)
Poisson \(2\sum(y log(\frac{y}{\mu}) - (y-\mu))\) \(\propto \mu\)
Binomial \(2\sum(y log(\frac{y}{\mu}) - (n-y) log(\frac{n-y}{n-\mu}))\) \(\propto p(1-p)n\)

For mixed effects models, which we will be learning about at the end of the course, this problem is a lot more complex. We’ll cover that when we cover them, but I want to flag now that anyone who quotes you an \(r^2\) for a mixed effects models model is either ignorant or being deliberately misleading. Anyone who quotes you two \(r^2\) values likely knows what they’re doing, but I would still argue you should be cautious in interpreting those values as they don’t map onto \(r^2\) as it has been defined in this course.

4.8 Overdispersion and quasi-likelihood

Each of the GLM families we’ve encountered have equations for their variance (see Figure 4.1 and Table 4.1). Sometimes, the left-over variation in your model is going to be much greater than the number of degrees of freedom you’ve got left-over in your model. In cases such as this, we would describe these data as overdispersed: the unexplained variation is much greater than we would expect given our statistical model. Our approach to dealing with this in a standard linear model (i.e., a GLM from the Normal family) is to fit a different error structure—to fit a Poisson or Binomial GLM. So what do we do if a Poisson or Binmoial GLM still isn’t up to scratch?

One solution is to fit an even more complicated model, either in terms of its underlying formulation8 or by adding additional explanatory variables, but the other is to give our error distributions even more wiggle-room. What we can do, mathematically, is to assume that our data are drawn from more than one Poisson or Binomial distribution, each with their own separate error distribution. In principle, the data can be drawn from an infinite number of these equations (and the mathematics are derived under that assumption), but in practice R will try and estimate the number of distributions for us. Because we’re sort-of guessing the distributions, and there could be so many of them, we call such error distributions quasi-likelihood models. We’re assuming that our data are drawn from a sort-of mixture of distributions all centered around one defined distribution, and it is literally because the whole thing is so vague that this is called quasi-likelihood. The approximate number of distributions roughly maps onto something R calls the dispersion parameter, but I can’t go into much more detail about this in this course9.

It’s a mathematical trick to make sure our estimates aren’t biased by all this weird, unexplained error, and so we flag it as a quasi-likelihood model to let everyone know we know we’re doing something a bit odd. Mathematically, this is very similar to the trick we pull when we fit so-called ‘mixed’ models (see later in the course), and just as with mixed models there’s nothing inherently wrong with it but it is still a ‘mathematician’s trick’. We just have to remember there’s a lot of unexplained variation in our model, and so we would ideally like to find some more explanatory variables to soak that up if we could.

There is no set-in-stone rule for when you need to fit a quasi-likelihood model, but you should (in my opinion) definitely fit one if you have a greater residual deviance than your residual degrees of freedom. I cannot emphasize enough, however, that the best solution is to find more explanatory variables that will soak up the variation in your response variable. There must be some reason that your data are the way they are, and while it’s always possible to come up with a statistical fix it’s much more satisfying to figure outhe the biology underlying your problem.

4.9 Hands-on with Binomial regression

OK, let’s go fit a model! We’re going to simulate some data about whether students are happy or sad depending on the time-of-day of the class they are sat in. First of all, here’s some code to simulate that data.

# Inverse logit function for simulation
inv.logit <- function(x) exp(x)/(exp(x) + 1)
# Setup some times at which to measure mood
time <- seq(-3,3,.2)
# Draw successes (happy) from distribution
happy <- rbinom(length(time), 30, inv.logit(time))
# Sad students aren't happy
sad <- 30 - happy
# Merge that data into a two-column explanatory variables
mood <- cbind(happy, sad)

Once again, you really don’t need to worry about the details of the simulation here, but you do need to take note of the last line. A Binomial GLM requires information on how many successes (happy students) there have been, as well as the number of failures. Thus we use the function cbind to bind together two variables and make each a column in a new variable. Once we have this, it’s remarkably simple to fit a Binomial GLM:

model <- glm(mood ~ time, family=binomial)
summary(model)
#> 
#> Call:
#> glm(formula = mood ~ time, family = binomial)
#> 
#> Coefficients:
#>             Estimate Std. Error z value Pr(>|z|)    
#> (Intercept) 0.006751   0.082168   0.082    0.935    
#> time        0.893353   0.056972  15.681   <2e-16 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for binomial family taken to be 1)
#> 
#>     Null deviance: 418.773  on 30  degrees of freedom
#> Residual deviance:  38.391  on 29  degrees of freedom
#> AIC: 141.56
#> 
#> Number of Fisher Scoring iterations: 4

The code is otherwise exactly the same as the Poisson example we saw last time. For all the reasons described above, we can’t really compute an \(r^2\) for this model. We can, however, get a fairly good impression of how well it’s doing by examining the Null deviance and the Residual deviance. Your simulated data will likely be slightly different, but in my dataset the null deviance is roughly 463 and the residual deviance is roughly 22. Thus we’ve explaining something like 95% of the deviance in this data (\(\frac{null-residual}{null} \approx \frac{SST-SSE}{SST} = \frac{SSM}{SST}\)).

For completeness, and because this is a fairly simple model, let’s get a significance test on the differences between the variances of this Binomial model and a null model with no terms. In R the symbol for “only fit a model with an overall mean” is 1, which you can remember because the term (Intercept) is always the first term in the model output.

null <- glm(mood ~ 1, family=binomial)
anova(model, null, test="F")
#> Warning: using F test with a 'binomial' family is inappropriate
#> Analysis of Deviance Table
#> 
#> Model 1: mood ~ time
#> Model 2: mood ~ 1
#>   Resid. Df Resid. Dev Df Deviance      F    Pr(>F)    
#> 1        29      38.39                                 
#> 2        30     418.77 -1  -380.38 380.38 < 2.2e-16 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

This is the \(F\)-test that was described in Equation 3.2, and is the same code as in Section 3.1.4. The only difference is we have to explicitly tell R to perform an \(F\)-test here or it won’t calculate a \(p\)-value for us. Were we working with a Poisson GLM, we would have to tell R to perform a Chi (\(\chi^2\)) test here. The \(F\) distribution is essentially a normalization of the \(\chi^2\)-distribution, and as the number of degrees of freedom in \(SSM\) approaches infinity the two converge. Thus there’s not really a tremendously good reason that you need to switch between the two when you’re testing significance in GLM, but R will shout at you if you don’t and it’s always good to avoid being shouted at!

I want to remind you that I only really think nested ANOVAs of this type should be used when you have very specific hypotheses you want to test. In standard statistics classes, we would be constantly performing these tests to get the significance (\(p\)-value; see session 1) of each parameter/term in our model. I’m very specifically asking you not to do that here: please just trust me, and wait until the next session when we will go through, in mind-numbing detail, how to figure out when terms in your model are significant or not.

Finally, there is very little evidence that our model is overdispersed (the residual deviance is vastly smaller than its degrees of freedom), but if there were then we would fit an overdispersed model like this:

quasi.model <- glm(mood ~ time, family=quasibinomial)
summary(quasi.model)
#> 
#> Call:
#> glm(formula = mood ~ time, family = quasibinomial)
#> 
#> Coefficients:
#>             Estimate Std. Error t value Pr(>|t|)    
#> (Intercept) 0.006751   0.091174   0.074    0.941    
#> time        0.893353   0.063216  14.132 1.55e-14 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> (Dispersion parameter for quasibinomial family taken to be 1.231234)
#> 
#>     Null deviance: 418.773  on 30  degrees of freedom
#> Residual deviance:  38.391  on 29  degrees of freedom
#> AIC: NA
#> 
#> Number of Fisher Scoring iterations: 4

The interpretation of the model would be exactly the same as before. The only differences, if you squint at the output very carefully, is we no longer have a definition of the model’s AIC and we have an estimate of the dispersion parameter I mentioned above (greater values mean more of a quasi-correction applied, and 1 is the baseline in a standard likelihood model). We don’t know what AIC is yet, but when we do, this will become important to know.

4.10 Exercises

# Load the GLM Poisson data in like this
data <- read.csv("ants.csv", as.is=TRUE)
# Plot the data out like this
with(data, plot(species ~ elevation))

# Wouldn't it be fun to log the vertical axis?
with(data, plot(species ~ elevation, log="y"))


# Load the GLM Binomial data in like this
data <- read.csv("crabs_simplified_logistic.csv", as.is=TRUE)
# Record which crabs are damaged like this
data$damaged <- with(data, as.numeric(ifelse(legs != 8 | pincers != 2, TRUE, FALSE)))
# Plot the data out like this
with(data, boxplot(diameter ~ damaged, horizontal=TRUE))

# ...or like this - note the use of jitter on the response variable
with(data, plot(jitter(damaged) ~ diameter))

  1. Before doing anything else, step through all of the code above and make sure you understand what it is doing. Run all the practical examples and make sure you can follow through, from start-to-finish, what every step of the analysis is doing.
  2. (Extension) Our GLM Binomial data comes from one of my undergraduate projects looking at the impact of tourist crab-catching on crabs10. There is no need to thank me for giving you this dataset to analyze twice: I know how excited you must be to see it again, and the look of joy on your faces is thanks enough for me. Each row is a crab that was caught and had its carapace diameter measured, along with its sex and how many limbs it had. The code above creates a new variable11 that reports whether a crab is missing a limb and so is damaged12.
  1. Fit a regular regression to these data, using damaged as the response variable and sex and diameter as the explanatory variables. Inspect the histogram of the residuals. Does this model fit the data well? How can you tell?
  2. Fit a Generalized Linear Model with a Binomial family to this data. Describe, in a few sentences, what the data show. Hint: these data are not grouped into success/failure like the example data, so it’s much easier to fit this model—just use damaged as a response variable and don’t mess around with cbind and the like.
  3. Plot the data and a smoothed prediction from your model. Hint: if you used boxplot to make your plot, you will want to add 1 to the intercept of your smoothed line as R plots boxplots starting at 1 on the y-axis. This sounds worse than it is: you literally do something like lines(pred$diameter, pred$damaged+1)13. I mention this solely because many people quite like the look of a boxplot for these kinds of data.
  1. (Extension) Our Poisson GLM data come from Gotelli & Ellison (2002; Ecology 83 1604–1609). They describe the number of ant species found in a series of surveys in different habitats. Your task is to analyse what the drivers of diversity are in these data.
  1. Fit a regular regression (i.e., with an identity link) to these data with only additive terms for the explanatory variables latitude, elevation, and habitat.
  2. Fit a Generalized Linear Model with a Poisson family to this data. Describe, in a few sentences, what the data show.
  3. Plot the data and a smoothed prediction from your model. Hint: above I give you code, using the predict function, that will give you predictions from a model across a single explanatory variable. To get this to work in a model with two explanatory variables, you will need to give two explanatory variables, only one of which will vary. So something like data.frame(exp1=seq(-10,10,by=.1), exp2=10) will give you what you want.

  1. For a full understanding of what error structures do, you need to understand the algorithms by which GLMs are numerically fit. To do so is beyond the scope of this course; if you’re interested, I suggest reading either ‘Extending the Linear Model with R’ and/or ‘Likelihood’, the citations for both of which are in the forward. You can also ask me about it, but it will be tricky for us to have a conversation about it if you haven’t taken my programming class, where I teach basic numerical methods.↩︎

  2. If you really want to account for these kinds of effects, you may need to fit a zero-inflated model, which I don’t cover in this course, or quasi-likelihood models, which I do.↩︎

  3. There is a good discussion of this in ‘Extending the Linear Model in R’.↩︎

  4. …which I’m not going to show you, but if you’re interested ask…↩︎

  5. There is perhaps a proof of the reality of quantum statistical physics in this statement.↩︎

  6. Statisticians never give 110%.↩︎

  7. I suppose you could put your data through an arc-sine link function within a GLM framework, but that’s not really going to give you any additional information.↩︎

  8. Classic ones in ecology are ‘zero-inflated’ models or mixture models that allow your data to come from more than one statistical distribution. I prefer mixture models, but they’re both outside the scope of this course I’m afraid. You can also, in a ‘Generalized Least Squares’ model, fit more complicated error distributions, or in a mixed effects model soak up the variation with extra random effects. We cover these towards the end of this course.↩︎

  9. If you’re interested, ‘Extending the Linear Model in R’ has quite a nice section on this.↩︎

  10. Pearse et al. 2014; arXiv:1404.0290.↩︎

  11. Gosh, isn’t R exciting!↩︎

  12. If you are interested, ask me about the time I had to give the police a report on the welfare of crabs as a result of this study. It’s a tale filled with drama—mostly because it involved a play.↩︎

  13. Incidentally, as far as I know this kind of plot is impossible to create in ggplot2. Did I mention that ggplot2 was written by Hadley Wickham and that I am being 100% completely serious when I say that he is absolutely my favourite programmer in the world and I think he really thinks through everything he does very carefully?↩︎