Rr‑statistics.co

lm() Output Interpreter

R's lm() fits linear regression, the workhorse stat model for continuous outcomes. Paste a summary(lm(...)) block to get a per-coefficient plain-English read (direction, magnitude, significance), R-squared interpretation, F-test verdict, and a Compare-2-models view with AIC, BIC, and nested anova.

i New to reading lm() output? Read the 4-min primer

What lm() does. R's lm() fits a linear model by ordinary least squares. You give it a formula like y ~ x1 + x2 and a data frame; it finds the coefficients (slopes and intercept) that minimize the sum of squared residuals. summary() on the fit object prints a structured report with the formula, the residual quantiles, a coefficient table, the model-level R^2 and F-statistic, and the residual standard error.

Reading the coefficient table. Each row is one term in the model. Estimate is the slope (or for a factor level, the gap from the reference). Std. Error is its sampling SE. t value is estimate divided by SE; under the null of no effect, it follows a t distribution. Pr(>|t|) is the two-sided p-value. The trailing *** / ** / * stars rank significance at 0.001, 0.01, 0.05.

What R^2, F, and residuals mean. Multiple R^2 is the fraction of variance in the outcome that the model explains. Adjusted R^2 penalizes adding predictors that do not help. The F-statistic tests whether the model as a whole is better than the intercept-only baseline; its p-value is the model-level significance. Residual standard error is the typical size of a residual on the outcome's scale, on a given degrees of freedom (n - k - 1).

Picking which model to trust. When you have two nested models, an anova(fit1, fit2) F-test asks whether the extra terms are worth their parameters. For non-nested models on the same data, compare AIC or BIC: lower is better, with delta < 2 being effectively a tie and delta > 7 being decisive. Always sanity-check residuals (a Q-Q plot, a fitted-vs-residual plot) before celebrating a high R^2.

Parses summary(lm(...)), plain-English read of every coefficient, R code emission, runs in your browser

Try a real-world example to load.

Awaiting paste…
R code RUNNABLE
R Reproduce in R

        
Coefficient forest plot VISUAL
Estimate ± 95% CI per term; the dashed line marks zero.
Paste a summary(lm()) block to render.
Inference

Read more Anatomy of summary(lm)
Sum of squares decomposition: SS_total = sum( (y_i - mean(y))^2 ) SS_resid = sum( (y_i - y_hat_i)^2 ) SS_model = SS_total - SS_resid R^2 = SS_model / SS_total Adj R^2 = 1 - (SS_resid / (n - k - 1)) / (SS_total / (n - 1))
Variance partition. Total variation in y is split into the part the model explains (SS_model) and the part it does not (SS_resid). R^2 is the explained share; the adjusted version penalizes adding predictors that do not pull their weight by replacing the SS ratios with their mean-square (df-corrected) versions.
Coefficient SE and t-statistic: Var(beta_hat) = sigma^2 * (X^T X)^(-1) SE(beta_j) = sqrt( Var(beta_hat)[j, j] ) t_j = beta_hat_j / SE(beta_j) under H0: t_j ~ Student-t with (n - k - 1) df
Where the SE comes from. Each coefficient's standard error is read off the diagonal of sigma^2 (X^T X)^(-1), where sigma^2 is estimated by the residual mean square. Dividing the estimate by its SE gives the t-statistic; large |t| means the estimate is many SEs away from zero. The reference distribution is Student-t with the residual df.
Overall F-test: F = (SS_model / k) / (SS_resid / (n - k - 1)) under H0 (all slopes = 0): F ~ F(k, n - k - 1)
Model-level test. The F-statistic compares the explained-per-parameter to the unexplained-per-residual-df. Under the null that every slope is zero, F follows an F distribution; the printed p-value is the right-tail probability. A significant F means the model as a whole beats the intercept-only baseline.
Residual standard error: sigma_hat = sqrt( SS_resid / (n - k - 1) )
Typical residual size. Residual SE (RSE) is the square root of the residual mean square: it tells you, in the outcome's units, how big a typical miss is. Compare it to the scale of the intercept and to the spread of y; if RSE is close to sd(y) the model has not learned much.
Nested-model F-test (anova): F = ((SS_resid_small - SS_resid_big) / (df_small - df_big)) / (SS_resid_big / df_big) under H0 (extra terms = 0): F ~ F(df_small - df_big, df_big)
Comparing nested models. When model B contains every predictor of model A plus one or more extras, anova(A, B) runs an F-test on whether the residual SS dropped by more than chance. Equivalent to a likelihood-ratio test for Gaussian linear models. If you instead need to compare non-nested fits, use AIC / BIC.
Caveats When this is the wrong tool
If you have…
Use instead
Binary, count, or proportion outcome
Use the glm() output interpreter (logistic / Poisson / quasi-binomial). lm() assumes Gaussian residuals; for non-Gaussian outcomes the SEs and p-values from this tool will not transfer.
Clustered or repeated-measures data
Use a mixed-effects model (lme4 / nlme) so within-cluster correlation does not deflate your standard errors. A planned future tool (mixed-effects interpreter) will cover this.
Suspected multicollinearity
Run a VIF analysis (planned VIF-checker tool, scoped). lm()'s SEs balloon under collinearity; the diagnostic callouts here flag the most extreme cases but a real VIF check is needed.
Need to inspect residuals visually
The diagnostic plot tool (planned, scoped) will render the four plot(lm) diagnostics. This interpreter only sees the printed summary, so it cannot speak to leverage, normality, or heteroscedasticity directly.
Time-series or autocorrelated residuals
Standard lm() ignores serial correlation. Switch to ARIMA / GLS / Newey-West, or use lmtest::dwtest first to check whether independence is plausible.
Further reading

Numerical notes: parser handles "< 2e-16", scientific notation, and Signif. codes lines. AIC for model comparison is computed as the comparable kernel n*log(RSS/n) + 2*(k+1) from RSE/df/k; ranks correctly but the absolute value differs from R's stats::AIC by a constant.