Fit models to data
This page provides tips and recommendations for fitting linear models to data.Here is one important difference between model fitting in R and most other packages. In R, the ANOVA table (but not the coefficients table) tests terms sequentially (in SAS jargon, R uses "type 1 sums of squares"). ANOVA table results will differ from those given in the coefficients table if the data design is not completely balanced. Also, the P-values in the ANOVA table will change if you change the order in which terms are entered in the model.
There are good reasons for doing it this way, but there are also ways around it, if necessary, as detailed below.
Fit a linear model to data
Linear models are implemented in the lm method in R. You can pass a data frame to the lm command and indicate using the formula which variables you want to fit:z <- lm(response ~ explanatory, data = mydata)
The resulting object (which I've named z) is an lm object containing all the results. You use additional commands to pull out these results. Here are some of the most useful commands to extract results from the lm object:TipsT
summary(z) # coefficients table with parameter estimates
plot(z) # plots of residuals, q-q, leverage
coef(z) # model coefficients (means, slopes, intercepts)
confint(z) #
confidence intervals for parameters
resid(z) # residuals
fitted(z) # predicted values
abline(z)
# adds simple linear regression line to
scatter plot
predict(z,newdata=x1) # predicted values for new observations
anova(z) # ANOVA table (** terms tested sequentially **)
anova(z1,z2) # compare fits of 2 models, "full" vs "reduced"
Examples of linear models
The simplest linear model fits a constant, the mean, to a single variable (from which one can obtain a confidence interval for a mean and a test of the null hypothesis that the mean is zero -- equivalent to a one-sample t test).z <- lm(y ~ 1)
The most familiar linear model is the linear regression of y on x.
z <- lm(y ~ x)
To fit a linear regression model without an intercept term (regression through the origin) add "-1" to the model statement:
z <- lm(y ~ x - 1)
If A is a categorical variable (factor or character) rather than a numeric variable then the following linear model is the same as a single factor ANOVA.
z <- lm(y ~ A)
More complicated models include more variables and their interactions. For example, the following is a multiple regression:
z1 <- lm(y ~ x1 + x2) # no interaction between x1 and x2
z2 <- lm(y ~ x1 + x2 + x1:x2) # interaction term present
z <- lm(y ~ x + A) # no interation
z <- lm(y ~ x + A + x:A) # interaction term present
Simple linear regression
Fit the model to data:z <- lm(y ~ x)
Add the regression line to a scatter plot.
plot(x,y)
abline(z)
The following, more complicated procedure also adds a regression line to a scatter plot, but the approach is more useful in the long term. The order(x) in brackets is needed to draw the lines from left to right (otherwise the lines go back and forth between the unordered x values).
plot(x,y)
yhat <- predict(z)
lines(x[order(x)],yhat[order(x)])
Add confidence bands and prediction intervals to a scatter plot. These might not fit onto the plot in their entirety unless you adjust the limits of the y-axis using the ylim option in the plot command.
plot(x,y)
lines(x[order(x)],yhat[order(x)])
yhat <- as.data.frame(predict(z,interval="confidence"),
level=0.95)
lines(x[order(x)],yhat$lwr[order(x)],lty=2)
lines(x[order(x)],yhat$upr[order(x)],lty=2)
yhat <- as.data.frame(predict(z,interval="prediction"),
level=0.95)
lines(x[order(x)],yhat$lwr[order(x)],lty=3)
lines(x[order(x)],yhat$upr[order(x)],lty=3)
Here are some useful commands for checking assumptions:
plot(z) # residual plots, etc: keep hitting enter
hist(resid(z)) # histogram of residuals
Here are some useful commands for parameter estimation
summary(z) # estimates of slope, intercept, SE's
confint(z, level=0.95) # conf. intervals for slope and intercept
To obtain the ANOVA table
anova(z)
Single-factor ANOVA
Before fitting a linear model to the data, check that the categorical variable is a factor. If not, make it a factor, as this will help later when we fit the model.is.factor(A) # or
class(A) # result should be "factor" if A is a factor
A <- factor(A) # convert categorical variable A to a factor
Before fitting a linear model to the data, check how R has sorted the groups (alphabetically, by default).
levels(A) # result will look like: "a" "b" "c" "d"
For reasons that will become clear below, it is often useful to rearrange the order of groups so that any control group is first, followed by treatment groups*. Let's assume that there are four groups and that group "c" is the control. Change the order in R as follows:
A <- factor(A, levels=c("c","a","b","d"))
*NB: Do not use the command "ordered" for this purpose, as this will change the type of factor you are working with and the way the linear model is fitted.Or, equivalently, if your variable A is in a data frame, use
mydat$A <- factor(mydat$A, levels=c("c","a","b","d")
Fit the model to data. y is the numeric response variable and A is the categorical explanatory variable (character or factor).
z <- lm(y ~ A)
z <- lm(y ~ A, data=mydata) # if variables are in a data frame
Visualize the fitted model in a graph that includes the data points.
- A fast but crude way to add the fitted values to
the plot (the lines indicating fitted values may be interrupted)
stripchart(y ~ A, vertical=TRUE, method="jitter", pch=16, col="red")
stripchart(fitted(z) ~ A, vertical=TRUE, add=TRUE, pch="------", method="jitter") - A slightly more tedious way that provides finer control.
stripchart(y ~ A, vertical=TRUE, method="jitter",
pch=16, col="red")
yhat<-tapply(fitted(z),A,mean)
for(i in 1:length(yhat)){
lines(c(i-.2,i+.2),rep(yhat[i],2))
}
plot(z) # residual plots, etc
hist(resid(z)) # histogram of residuals
See how R is representing the categorical variable and the constant in the linear model by using indicator (dummy) variables.
model.matrix(z)
Parameter estimation. By default*, R uses the intercept term in the model to estimate the mean of the first group (hopefully, you've set this to be the control group). Remaining parameters estimate the difference between the mean of each group and the first (control) group. Ignore the P-values because they are invalid for tests of the treatment effects except in special cases (i.e., planned contrasts).
summary(z) # parameter estimates
confint(z, level=0.95) # conf. intervals for parameters
* Warning to users of SPlus: the default is different from that in R.To obtain the ANOVA table
anova(z)
You can change the way that R models the categorical variable. For example, a useful alternative approach uses a model in which the parameters represent the population means of the different groups. To accomplish this, refit the linear model with a "- 1" in the formula (this suppresses the intercept).
z <- lm(y ~ A - 1, data=mydata)
model.matrix(z) # optional:
shows how R represents categories
summary(z) #
estimates of population means with SE's*
confint(z, level=0.95) # conf. intervals for means*
anova(z)
# results will be the same as before
*The standard errors and confidence intervals for the fitted means are not the same as those you would obtain if you calculated them separately on the raw data for each group separately. This is because the fitted means use the MSresidual from all the data to calculate standard errors. This should generally result in smaller SE's and narrower confidence intervals. The calculation is valid if the assumption of equal variances within different groups is met.
Fit more than one variable
To show the procedures I use an example of a linear model having a numeric response variable (y) and two explanatory variables, one categorical (A) and the other numeric (x). This combination of variables is known as ANCOVA, or analysis of covariance. Procedures are similar for multiple regression (all variables numeric).As in the previous section, check that the categorical variable is a factor. If not, convert it to a factor, as this will help later when we fit the model.
A <- factor(A) # convert categorical variable A to a factor
Also check how R has sorted the groups of the categorical variable (alphabetically, by default).
levels(A) # result will look like: "a" "b" "c" "d"
If necessary, change the order* of the groups so that any control group comes first, followed by treatment groups:
A <- factor(A, levels=c("c","a","b","d"))
*Do not use the command "ordered" for this purpose, as this will change the type of factor you are working with and the way the linear model is fitted.Fit the model to data. Generally, fitting the model without an interaction term is preferred, if you can assume that any interaction is weak or absent.
z <- lm(y ~ x + A)
# no interaction term included, or
z <- lm(y ~ x + A + x:A) # interaction term present
Visualize the fitted model in a graph that includes the data points.
plot(x,y,pch=as.numeric(A))
legend(locator(1), as.character(levels(A)),
pch=1:length(levels(A))) # click on plot to place
legend
groups<-levels(A) # stores group names
for(i in 1:length(groups)){
xi<-x[A==groups[i]] # grabs x-values for group i
yhati<-fitted(z)[A==groups[i]] # grabs yhat's for group i
lines(xi[order(xi)],yhati[order(xi)]) # connects the dots
}
Check model assumptions:
plot(z) # residual plots, etc
hist(resid(z)) # histogram of residuals
Parameter estimation.
summary(z) # parameter estimates
confint(z, level=0.95) # confidence intervals for parameters
The ANOVA table. Notice that the anova command fits terms in the model sequentially1 from the top down ("type I sums of squares"). P-values will therefore differ from those in the coefficients table if the design is unbalanced. The results obtained will then also depend on the order in which you entered the variables in the model formula. Try changing the order to see if this changes your results.
anova(z)
To exert control over the desired test, fit explicit "full" and "reduced" models yourself and then compare the two model fits using
anova(z1,z2) # compare fits of two models, "full" vs "reduced"
1 In SAS jargon, R uses "type 1 sums of squares". Each term is tested by adding it to a model that includes only previous terms in the sequence, with interactions fitted after component variables have been entered. The default in most other statistical packages is instead to use "type III sums of squares", in which each term is tested by adding it to a model that includes all other terms (this can have weird consequences, e.g., testing an individual variable with its interactions present). Click here for a discussion of the pros and cons of different testing approaches.
AIC and related quantities
Then apply the function of interest to the fitted model object, z.logLik(z) # log-likelihood of the model fit
AIC(z) # Akaike Information Criterion
extractAIC(z) # same, but differs from AIC(z) by a constant
AIC(z,k=log(n)) # Bayesian Information Criterion
(n=sample size)
The function AIC can be used to compare two or more models fitted to the same data. The model with the smallest AIC values is the "best". The same response variable must be fitted in each case, but the explanatory variables can differ.
z1 <- lm(y ~ x1 +x 2 + x3) # first model
z2 <- lm(y ~ x3 + x4) # second model
c(AIC(z1),AIC(z2)) # vector of AIC values
AIC(z1,z2) # same but returns a data frame
What matters is not AIC itself but the difference in AIC between fitted models. The model with the smallest AIC will have a difference of zero.
x <- c(AIC(z1),AIC(z2),AIC(z3)) # stores AIC values in a vector
delta <- x - min(x) # AIC differences
Finally, we can use these quantities to calculate the Akaike weights for each of a set of fitted models. The model with the highest weight is the "best". The magnitude of the weight of a given model indicates the weight of evidence in its favor, given that one of the models in the set being compared is the best model. The weights are used in model averaging, a technique in multimodel inference. Weights should sum to 1.
x <- c(AIC(z1),AIC(z2),AIC(z3)) # stores AIC values in a vector
delta <- x - min(x) # AIC differences
L <- exp(-0.5*delta) # likelihoods of models
w <- L/sum(L) # Akaike weights
A 95% confidence set for the “best” model can be obtained by ranking the models and summing the weights until that sum is ≥ 0.95.
