Site icon R-bloggers

[Statistics] Collinearity in regression

[This article was first published on R on Zhenguo Zhang's Blog, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Zhenguo Zhang’s Blog https://fortune9.netlify.app/2026/10/16/statistics-collinearity-in-regression/ –
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 of X1.
  • Z: a variable linearly related to X2, defined as Z = 5*X2 + noise, where the noise is drawn from a normal distribution with mean 0 and standard deviation 1.
  • Y: a linear combination of X1 and X2 plus noise, defined as Y = 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")
Table 1: 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")
Table 2: 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")
Table 2: 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")
Table 3: 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, including Z makes the test for the true predictor X less statistically significant. The coefficient for Z may not be significant because, after accounting for X, Z has no effect on Y.
  • 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.

– https://fortune9.netlify.app/2026/10/16/statistics-collinearity-in-regression/ –
To leave a comment for the author, please follow the link and comment on their blog: R on Zhenguo Zhang's Blog.

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.
Exit mobile version