Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
library(knitr) library(ggplot2) library(dplyr) library(tidyr) library(broom)
Problem
When performing regression analysis, you may encounter two or more predictors that are highly correlated. This is called collinearity or multicollinearity. It can cause problems in estimating the regression coefficients and interpreting the results.
In this post, I will show how collinearity can affect regression analysis, how to detect collinearity, and how to deal with it.
Example: one predictor is a linear function of another predictor
Let’s simulate data with the following variables:
X1: a random variable drawn from a normal distribution with mean 0 and standard deviation 1.X2: another random variable drawn from a normal distribution with mean 0 and standard deviation 1, independent ofX1.Z: a variable linearly related toX2, defined asZ = 5*X2 + noise, where the noise is drawn from a normal distribution with mean 0 and standard deviation 1.Y: a linear combination ofX1andX2plus noise, defined asY = 3*X1 + 2*X2 + noise, where the noise is drawn from a normal distribution with mean 0 and standard deviation 1.
set.seed(123) n <- 100 X1 <- rnorm(n, mean = 0, sd = 1) X2 <- rnorm(n, mean = 0, sd = 1) Z <- 5 * X2 + rnorm(n, mean = 0, sd = 1) Y <- 3 * X1 + 2 * X2 + rnorm(n, mean = 0, sd = 1)
The correlation coefficients among the three predictors are:
kable(cor(cbind(X1, X2, Z)), caption="Correlation of the predictors")
| X1 | X2 | Z | |
|---|---|---|---|
| X1 | 1.0000000 | -0.0495321 | -0.0730836 |
| X2 | -0.0495321 | 1.0000000 | 0.9814767 |
| Z | -0.0730836 | 0.9814767 | 1.0000000 |
Now let’s fit two linear regression models: one with X1 and X2 as predictors, and another with X1, X2, and Z as predictors.
We will then examine how the following metrics change: coefficients, standard errors, t-values, p-values, R-squared, adjusted R-squared.
fit1 <- lm(Y ~ X1 + X2) fit2 <- lm(Y ~ X1 + X2 + Z) coef_results <- rbind( tidy(fit1) %>% mutate(model = "fit1"), tidy(fit2) %>% mutate(model = "fit2") ) # move model to the first column coef_results <- coef_results %>% select(model, everything()) # now R-squared and adjusted R-squared rsq_results <- rbind( glance(fit1) %>% mutate(model = "fit1"), glance(fit2) %>% mutate(model = "fit2") ) %>% select(model, everything()) kable(coef_results, caption = "The coefficients and statistics")
| model | term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|---|
| fit1 | (Intercept) | -0.0270771 | 0.1058626 | -0.2557758 | 0.7986656 |
| fit1 | X1 | 2.9521917 | 0.1154749 | 25.5656651 | 0.0000000 |
| fit1 | X2 | 2.0448517 | 0.1090059 | 18.7590842 | 0.0000000 |
| fit2 | (Intercept) | -0.0193261 | 0.1073437 | -0.1800395 | 0.8575011 |
| fit2 | X1 | 2.9445494 | 0.1168766 | 25.1936688 | 0.0000000 |
| fit2 | X2 | 2.3331532 | 0.5743463 | 4.0622761 | 0.0000993 |
| fit2 | Z | -0.0573870 | 0.1122308 | -0.5113302 | 0.6102939 |
kable(rsq_results, caption = "The goodness of fit")
| model | r.squared | adj.r.squared | sigma | statistic | p.value | df | logLik | AIC | BIC | deviance | df.residual | nobs |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| fit1 | 0.9082614 | 0.9063699 | 1.047502 | 480.1763 | 0 | 2 | -145.0117 | 298.0234 | 308.4441 | 106.4342 | 97 | 100 |
| fit2 | 0.9085106 | 0.9056516 | 1.051513 | 317.7673 | 0 | 3 | -144.8757 | 299.7514 | 312.7773 | 106.1452 | 96 | 100 |
As the results show, the first model, fit1, roughly captures the true relationship in the simulated data,
and both coefficients are highly statistically significant.
Adding Z to the regression model produces several changes: (1) the coefficient for X2
becomes smaller, (2) its standard error increases more than fivefold, and (3)
the p-value for the coefficient test consequently increases substantially. Overall,
the model’s R-squared changes little, but its AIC and BIC values increase,
suggesting that the model with three predictors is less favorable than the model with two predictors.
In summary, including a predictor that is highly correlated with an existing predictor reduces the statistical significance of the existing predictor.
Example: two predictors with different levels of correlation
Now let’s simulate datasets containing two variables with different levels of correlation. We will use Cholesky decomposition to simulate the data.
First, let’s write a function that simulates data for a specified correlation coefficient:
simulate_correlated_data<-function(rho, n=100, seed=NULL) {
if(is.null(seed)) {
seed < Sys.time()
}
set.seed(seed)
x <- rnorm(n)
z <- rnorm(n)
y <- rho*x + sqrt(1-rho^2)*z
return(cbind(x,y))
}
Now let’s simulate five datasets with target correlations of 0.1, 0.3, 0.5, 0.8, and 0.95 between the two predictors. We will also make both predictors contribute to the response variable so that we can examine how different levels of correlation affect the regression fit:
n<-100 rhos<-c(0.1, 0.3, 0.5, 0.8, 0.95) simulated_data <- lapply(rhos, simulate_correlated_data, n=n) # let's calculate the correlation coefficients for the simulated data simulated_rhos<-sapply(simulated_data, function(d) cor(d[,"x"],d[, "y"])) kable(cbind(expected_rho=rhos, actual_rho=simulated_rhos))
| expected_rho | actual_rho |
|---|---|
| 0.10 | -0.0271945 |
| 0.30 | 0.3337500 |
| 0.50 | 0.5365344 |
| 0.80 | 0.8167663 |
| 0.95 | 0.9624687 |
The correlation coefficient, rho, in each simulated dataset may not exactly match the specified value because of random sampling, but we can still examine how the level of correlation affects the regression.
fits <- lapply(simulated_data, function(d) {
x1<-d[,"x"]
x2<-d[, "y"]
y <- 10*x1 + 3*x2 + rnorm(n)
fit<-lm(y~x1 + x2)
return(fit)
}
)
coef_results<-lapply(seq_along(rhos),
function(i) {
tidy(fits[[i]]) %>%
mutate(rho = rhos[i]) %>%
select(rho, everything())
}
)
coef_results <- do.call(rbind, coef_results)
kable(coef_results, caption = "Regression statistics with different correlation strengths among predictors")
| rho | term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|---|
| 0.10 | (Intercept) | 0.1545731 | 0.1093261 | 1.4138713 | 0.1606012 |
| 0.10 | x1 | 10.0038407 | 0.1047198 | 95.5296414 | 0.0000000 |
| 0.10 | x2 | 3.0724520 | 0.1118900 | 27.4595781 | 0.0000000 |
| 0.30 | (Intercept) | 0.0349345 | 0.1183168 | 0.2952627 | 0.7684243 |
| 0.30 | x1 | 9.8936718 | 0.1339565 | 73.8573235 | 0.0000000 |
| 0.30 | x2 | 3.0068927 | 0.1113956 | 26.9929282 | 0.0000000 |
| 0.50 | (Intercept) | 0.1417142 | 0.0972655 | 1.4569831 | 0.1483507 |
| 0.50 | x1 | 10.0727790 | 0.1156101 | 87.1271918 | 0.0000000 |
| 0.50 | x2 | 2.9767374 | 0.1173528 | 25.3657122 | 0.0000000 |
| 0.80 | (Intercept) | -0.0609381 | 0.0979742 | -0.6219812 | 0.5354135 |
| 0.80 | x1 | 10.3154854 | 0.1709446 | 60.3440164 | 0.0000000 |
| 0.80 | x2 | 2.7154226 | 0.1638409 | 16.5735364 | 0.0000000 |
| 0.95 | (Intercept) | -0.1158642 | 0.0971670 | -1.1924234 | 0.2360041 |
| 0.95 | x1 | 10.0497393 | 0.3537788 | 28.4068467 | 0.0000000 |
| 0.95 | x2 | 2.9023068 | 0.3443712 | 8.4278435 | 0.0000000 |
Let’s plot all the metrics:
coef_results <- coef_results %>%
mutate(rho = factor(rho)) %>%
pivot_longer(
cols = estimate:p.value,
names_to = "metric",
values_to = "value"
)
plt <- ggplot(coef_results, aes(x=rho, y=value)) +
geom_point(aes(color=term)) +
facet_wrap(~ metric, ncol=2, scales="free_y") +
theme(legend.position = "top")
plt
The plots show the following patterns as the correlation between the predictors increases:
- The estimated coefficients change little, although some variation remains.
- The p-values for both predictors remain very small.
- The test statistics become smaller as rho increases.
- The standard errors increase as rho increases.
- Based on the last two observations, the p-values for the predictors should become larger, i.e., less significant.
In summary, when the predictors in a regression are correlated, the test for each coefficient becomes less statistically significant, even though the magnitudes of the coefficients remain close to the values used to simulate the data.
Takeaways
- When one predictor,
Z, is a linear function of another predictor,X, and has no direct relationship with the response variable, includingZmakes the test for the true predictorXless statistically significant. The coefficient forZmay not be significant because, after accounting forX,Zhas no effect onY. - When two predictors both affect
Y, the statistical significance of their coefficients decreases as the correlation between the predictors increases, even though the magnitudes of the coefficients may remain close to the true values.
Taken together, these examples suggest that you may want to exclude certain variables from your regression unless you know they affect the response or want to examine the effects of other predictors after controlling for those variables.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
