Answer
When can you drop the intercept from a linear regression?
The short answer
Rarely, and never because the fit looks better. Dropping the intercept forces the line through the origin, which biases the slope unless the true line passes through zero. The higher R-squared is an artefact: without an intercept it is measured against zero, not against the mean of y. Leave it out only when theory says y must be zero when the predictors are zero, the data reach near that point, and the data agree.
The short answer
The intercept is the predicted value of y when every predictor equals zero. Removing it does not just delete one uninteresting number: it adds the constraint that the fitted line must pass through the point (0, 0). If that constraint is false, every other coefficient shifts to accommodate it, and the slope you report is wrong.
Keeping the intercept costs one degree of freedom and nothing else. Removing it when it should be there can cost you an unbiased slope, residuals that average zero, and an honest R-squared. That asymmetry is why the default is to keep it, even when its estimate is not significantly different from zero and even when zero is outside the range of your data.
Why R-squared jumps when you remove the intercept
R-squared is 1 minus the residual sum of squares divided by a total sum of squares. With an intercept, the total is measured around the mean of y: the model is compared with a flat line at ȳ. Without an intercept, R's summary(), Python's statsmodels and Stata, among others, measure the total around zero instead, so the model is compared with the line y = 0. Because the sum of y² is far larger than the sum of squared deviations from the mean whenever the values of y sit well away from zero, the ratio shrinks and R-squared climbs towards 1.
The two R-squared values therefore answer different questions and cannot be compared. To compare the models fairly, look at their residual sums of squares, or compute the usual (centred) R-squared for both. Since the no-intercept model is the full model with one coefficient fixed at zero, its residual sum of squares can never be smaller: on the same yardstick, removing the intercept can only make the fit worse or leave it unchanged.
See it in R and Python
The example simulates 50 points from a line with intercept 5 and slope 2, with x between 10 and 20, then fits the model with and without an intercept (y ~ 0 + x and y ~ x - 1 both remove it in R). The Python version fits the same two models with NumPy's least squares and reproduces R's definitions, including the zero-based R-squared R reports for a model without an intercept.
R
set.seed(7948)
n <- 50
x <- runif(n, 10, 20) # x never comes close to 0
y <- 5 + 2 * x + rnorm(n, sd = 3) # the true line has intercept 5
with_int <- lm(y ~ x)
no_int <- lm(y ~ 0 + x) # same as y ~ x - 1
# Slopes: the no-intercept fit bends the slope to pass through (0, 0)
round(c(slope_with = coef(with_int)[["x"]], slope_without = coef(no_int)[["x"]]), 3)
round(confint(with_int), 2) # does the intercept's CI include 0?
# R-squared as each model reports it
round(c(r2_with = summary(with_int)$r.squared, r2_without = summary(no_int)$r.squared), 3)
# Same yardstick for both: residual sums of squares and centered R-squared
rss <- c(with = sum(resid(with_int)^2), without = sum(resid(no_int)^2))
tss_centered <- sum((y - mean(y))^2)
round(rss, 1)
round(1 - rss / tss_centered, 3)
# Residuals no longer average zero without an intercept
round(c(mean_resid_with = mean(resid(with_int)), mean_resid_without = mean(resid(no_int))), 3)
# Formal check: the no-intercept model is nested in the full one
anova(no_int, with_int)Python
import numpy as np
from scipy import stats
rng = np.random.default_rng(7948)
n = 50
x = rng.uniform(10, 20, n) # x never comes close to 0
y = 5 + 2 * x + rng.normal(0, 3, n) # the true line has intercept 5
X_with = np.column_stack([np.ones(n), x])
X_without = x[:, None] # no column of ones = no intercept
b_with, *_ = np.linalg.lstsq(X_with, y, rcond=None)
b_without, *_ = np.linalg.lstsq(X_without, y, rcond=None)
res_with = y - X_with @ b_with
res_without = y - X_without @ b_without
print("slopes:", round(b_with[1], 3), round(b_without[0], 3))
# 95% CI for the intercept, as confint() gives it
df = n - 2
s2 = res_with @ res_with / df
se = np.sqrt(np.diag(s2 * np.linalg.inv(X_with.T @ X_with)))
tq = stats.t.ppf(0.975, df)
print("intercept CI:", np.round([b_with[0] - tq * se[0], b_with[0] + tq * se[0]], 2))
# R-squared as each model reports it (R uses an uncentered total without an intercept)
rss = np.array([res_with @ res_with, res_without @ res_without])
tss_centered = np.sum((y - y.mean()) ** 2)
print("reported R2:", round(1 - rss[0] / tss_centered, 3), round(1 - rss[1] / np.sum(y ** 2), 3))
# Same yardstick for both
print("RSS:", np.round(rss, 1), "centered R2:", np.round(1 - rss / tss_centered, 3))
print("mean residuals:", round(res_with.mean(), 3), round(res_without.mean(), 3))
# Nested-model F test, as anova(no_int, with_int)
F = (rss[1] - rss[0]) / (rss[0] / df)
print("F =", round(F, 4), "p =", round(stats.f.sf(F, 1, df), 5)) slope_with slope_without
2.015 2.326
2.5 % 97.5 %
(Intercept) -0.23 9.47
x 1.68 2.35
r2_with r2_without
0.755 0.991
with without
478.9 515.6
with without
0.755 0.737
mean_resid_with mean_resid_without
0.000 0.158
Analysis of Variance Table
Model 1: y ~ 0 + x
Model 2: y ~ x
Res.Df RSS Df Sum of Sq F Pr(>F)
1 49 515.56
2 48 478.95 1 36.618 3.6698 0.06137 .
The figures come from one seeded run of the R code. Python's random numbers differ from R's, so its figures differ slightly (for example slopes of 2.068 and 2.297, and a reported R-squared of .823 with the intercept against .994 without), but they show the same pattern.
What the example shows
- R-squared rose from .755 to .991 when the intercept was removed, yet the residual sum of squares went up, from 478.9 to 515.6. On the same centred total the no-intercept model explains less (.737 against .755).
- The slope moved away from the truth. The true slope is 2. With the intercept the estimate was 2.015; forced through the origin it became 2.326, because the line had to tilt to reach (0, 0) from data that sit between x = 10 and 20.
- Residuals no longer average zero (0.158 instead of 0.000). With an intercept, least squares guarantees they sum to zero; without one, the fit can be systematically too high or too low.
- A non-significant intercept is not permission to drop it. Its 95% CI ran from −0.23 to 9.47 (p = .061), though the true intercept is 5. Far from x = 0 the test has little power, and a non-significant result is not evidence of zero.
When removing the intercept is justified
Fitting through the origin (sometimes called regression through the origin) is reasonable only when all three of these hold:
- Theory fixes the value at zero. The relationship is known to be proportional, so zero input must give exactly zero output, for example an amount of a substance and the mass it adds, or a calibration that is physically anchored at zero.
- The data reach near zero. Even a truly proportional relationship can bend outside the range you observed, so a line forced through a point far from the data is an extrapolation you are imposing.
- The data agree. Fit the model with the intercept as well and check that its estimate is small with a narrow interval. If the two slopes differ noticeably, trust the model with the intercept.
One common case is not really a removed intercept. With a categorical predictor, y ~ 0 + group estimates one mean per group instead of a baseline and differences. The fitted values and residuals are identical to y ~ group; only the coefficients' meaning changes, and R-squared is again reported against zero, so ignore it. If the problem is that zero is a meaningless value of x, centre the predictor instead: that makes the intercept interpretable without constraining the fit. The DASS blog has more on building and checking regression models.
How to report a regression without an intercept in APA style (7th edition)
If you do fit through the origin, say so in the method section and justify it, because readers will otherwise assume a standard model. Report the comparison with the full model and the centred fit rather than the zero-based R-squared. Using the example above:
"Because the intercept was not significantly different from zero, 95% CI [−0.23, 9.47], F(1, 48) = 3.67, p = .061, a model through the origin was considered. It was rejected because the slope changed from 2.015 to 2.326 and the centred R² fell from .755 to .737."
When a no-intercept model is justified by theory, write, for example, "The regression was constrained through the origin because [reason]; the slope was b = x.xx, 95% CI [LL, UL]. The R² reported by the software is computed about zero and is not comparable with R² from models with an intercept."
Related tools and guides
- APA 7 formatter for regression
- Is R-squared useful or misleading?
- Centering vs standardizing predictors
- How to interpret regression output in R
- Does a non-significant result support the null?
- Simple linear regression (Wikipedia)
More answered questions
- Which interaction terms belong in a regression or mixed model?
- Bagging, boosting and stacking: how do these ensemble methods differ?
- Training, validation and test sets: what is each one for?
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 questionWritten 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.