Answer

How do you interpret regression output in R?

Inspired by a question on Cross Validated ·

regressionp-values

The short answer

Each coefficient row gives the change in the outcome per unit of that predictor with the others held fixed (Estimate), its uncertainty (Std. Error), its distance from zero in standard errors (t value = Estimate / SE), and the p-value for a true coefficient of zero. Below the table, the residual standard error is the typical prediction error in the outcome's units, R-squared is the share of variance explained, and the F test checks all slopes at once.

A worked example

The easiest way to learn the output is to rebuild every number yourself. The code below simulates 100 students whose exam score depends on hours of study (true slope 3) and hours of sleep (true slope 2), plus noise with a standard deviation of 8. It fits the model with lm(), prints summary(), and then recomputes each figure from the data by hand. The Python tab reproduces the same table with NumPy and SciPy.

R

set.seed(5135)
n <- 100

# Simulated data: exam score depends on study hours and sleep
hours <- runif(n, 0, 10)
sleep <- rnorm(n, 7, 1)
score <- 50 + 3 * hours + 2 * sleep + rnorm(n, sd = 8)

fit <- lm(score ~ hours + sleep)
summary(fit)

# Rebuild every number by hand
X   <- model.matrix(fit)
res <- residuals(fit)
p   <- ncol(X)                         # coefficients, including intercept
df  <- n - p                           # residual degrees of freedom
rse <- sqrt(sum(res^2) / df)           # residual standard error
se  <- sqrt(diag(rse^2 * solve(t(X) %*% X)))
t   <- coef(fit) / se                  # t value = estimate / SE
pv  <- 2 * pt(-abs(t), df)             # two-sided p-value
round(cbind(estimate = coef(fit), se, t, p = pv), 4)

tss <- sum((score - mean(score))^2)
r2  <- 1 - sum(res^2) / tss
adj <- 1 - (1 - r2) * (n - 1) / df
Fst <- (r2 / (p - 1)) / ((1 - r2) / df)
round(c(rse = rse, df = df, r2 = r2, adj_r2 = adj, F = Fst,
        F_p = pf(Fst, p - 1, df, lower.tail = FALSE)), 4)
round(mean(res), 10)                   # residuals average exactly 0
confint(fit)                           # 95% CIs: estimate +/- t-crit * SE

Python

import numpy as np
from scipy import stats

rng = np.random.default_rng(5135)
n = 100

# Simulated data: exam score depends on study hours and sleep
hours = rng.uniform(0, 10, n)
sleep = rng.normal(7, 1, n)
score = 50 + 3 * hours + 2 * sleep + rng.normal(0, 8, n)

# Every number in R's summary(lm(score ~ hours + sleep)), by hand
X = np.column_stack([np.ones(n), hours, sleep])
beta, *_ = np.linalg.lstsq(X, score, rcond=None)
res = score - X @ beta
p = X.shape[1]                          # coefficients, including intercept
df = n - p                              # residual degrees of freedom
rse = np.sqrt(res @ res / df)           # residual standard error
se = np.sqrt(np.diag(rse**2 * np.linalg.inv(X.T @ X)))
t = beta / se                           # t value = estimate / SE
pv = 2 * stats.t.sf(np.abs(t), df)      # two-sided p-value
for name, b, s, tv, pp in zip(["(Intercept)", "hours", "sleep"], beta, se, t, pv):
    print(f"{name:12s} estimate={b:8.4f} se={s:.4f} t={tv:8.4f} p={pp:.4g}")

print("residual quartiles:", np.round(np.percentile(res, [0, 25, 50, 75, 100]), 4))
tss = np.sum((score - score.mean()) ** 2)
r2 = 1 - res @ res / tss
adj = 1 - (1 - r2) * (n - 1) / df
F = (r2 / (p - 1)) / ((1 - r2) / df)
print(f"rse={rse:.4f} on {df} df, R2={r2:.4f}, adjR2={adj:.4f}, "
      f"F={F:.4f} on {p - 1} and {df} df, p={stats.f.sf(F, p - 1, df):.3g}")
print("mean residual:", round(float(res.mean()), 10) + 0.0)  # 0 up to rounding

tcrit = stats.t.ppf(0.975, df)          # 95% CIs: estimate +/- t-crit * SE
print(np.round(np.column_stack([beta - tcrit * se, beta + tcrit * se]), 4))

The numbers quoted on this page come from one seeded run of the R code. Python draws different random numbers, so its figures differ slightly (for example a slope of 3.19 for hours instead of 3.24), but every hand calculation matches its own fitted model in the same way. The key part of the R summary looks like this:

Residuals:
     Min       1Q   Median       3Q      Max 
-23.7202  -5.0724  -0.2699   5.0583  22.8493 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  44.1002     5.8127   7.587    2e-11 ***
hours         3.2380     0.2895  11.186  < 2e-16 ***
sleep         2.4201     0.7642   3.167  0.00206 ** 

Residual standard error: 7.884 on 97 degrees of freedom
Multiple R-squared:  0.5735,	Adjusted R-squared:  0.5647 
F-statistic:  65.2 on 2 and 97 DF,  p-value: < 2.2e-16

The residuals and the coefficient table

The t test for a coefficient answers a narrow question: is this slope different from zero, given the other predictors in the model? It says nothing about whether the effect is large. The 95% confidence interval from confint() (the estimate plus or minus about 1.98 standard errors here) is usually more informative: [2.66, 3.81] for hours and [0.90, 3.94] for sleep, both containing the true values used in the simulation. Our page on p-values and t values covers the logic of the test in more detail.

The lines below the table

Common misreadings

How to report a multiple regression in APA style (7th edition)

Report the overall model test and fit first, then each coefficient with its interval. Italicize statistical symbols, round to two decimals, and drop the leading zero for p and R², which cannot exceed 1. Using the example above:

"A multiple linear regression showed that study hours and sleep together predicted exam scores, F(2, 97) = 65.20, p < .001, R² = .57, adjusted R² = .56. Holding sleep constant, each additional hour of study was associated with a 3.24-point higher score, b = 3.24, 95% CI [2.66, 3.81], t(97) = 11.19, p < .001. Sleep also predicted scores, b = 2.42, 95% CI [0.90, 3.94], t(97) = 3.17, p = .002."

With several predictors, a table is clearer: one row per predictor with b, SE, the 95% CI, t and p, and R², adjusted R² and the F test in a note or the bottom rows.

Related tools and guides

More answered questions

Working with your own data?

General answers only go so far. Send us your situation and we'll reply by email within two business days.

Ask your question

Written by AskStats with AI assistance. This is general information, not advice for your specific data or study. When the results matter, check your approach with a qualified statistician.