Written and maintained by CASRAI Editorial Board
Last updated
Quadratic regression analysis fits a parabola, y = b0 + b1x + b2x², to data by least squares. It is still a linear model, because it is linear in the coefficients; the squared term lets the fitted line bend once. A researcher who finishes this page should be able to fit the model in Excel, R or Python, read the output, find the turning point, and judge whether the curve earns its extra term.
What quadratic regression is used for in research
What is a quadratic regression?
A quadratic regression is a regression of an outcome y on a predictor x and its square x². The model is y = b0 + b1·x + b2·x² + ε. If b2 is negative the curve opens downward (an inverted U with a maximum); if b2 is positive it opens upward (a U with a minimum). The coefficients are estimated by ordinary least squares, the same method used for a straight-line fit, so any software that fits a multiple regression can fit it once the x² column exists.
Researchers reach for it when a scatterplot or residual plot shows one bend: dose and response that rise and then plateau or fall, performance against arousal, cost against batch size, growth against time over a limited range. If the plot shows no bend, the straight line is the better model. See scatterplot interpretation for reading the shape first, and the linearity assumption for the residual-plot check that usually prompts adding a squared term. For other kinds of curvature (exponential, saturating, thresholds) see non-linear relationships.
Quadratic regression formula
The fitted equation is ŷ = b0 + b1·x + b2·x². Two quantities follow directly from the coefficients by standard algebra:
- Turning point (vertex): x* = −b1 / (2·b2). The fitted y at that point is b0 + b1·x* + b2·x*².
- Slope at any x: b1 + 2·b2·x. The slope is not constant; b1 alone is the slope only where x = 0.
The turning point is only meaningful if x* lies inside the range of x you observed. A vertex far outside the data is an extrapolation, not a finding.
Worked example (computed for this page)
This is a small invented dataset for illustration, not data from a study. Eight observations: x = 1, 2, 3, 4, 5, 6, 7, 8 and y = 2.1, 4.8, 6.9, 7.4, 7.9, 6.6, 5.0, 2.3. Fitting y on x and x² by least squares (statsmodels 0.15.0, checked against NumPy 2.5.1) gives:
| Term | Estimate | Std. error | t |
|---|---|---|---|
| Intercept (b0) | −1.5536 | 0.2701 | −5.75 |
| x (b1) | 4.1167 | 0.1377 | 29.90 |
| x² (b2) | −0.4548 | 0.0149 | −30.45 |
R² = 0.9946, adjusted R² = 0.9925, residual standard error = 0.194 on 5 degrees of freedom, F(2, 5) = 463.86. The turning point is x* = −4.1167 / (2 × −0.4548) = 4.526, where the fitted maximum is ŷ ≈ 7.76. That lies inside the observed range of 1 to 8, so it can be reported.
Why the curve matters here: a straight-line fit to the same eight points gives R² = 0.0007 and a slope of 0.024. The linear model reports essentially no relationship because the rise and fall cancel. Comparing the two nested models with an F-test on the one added term gives F(1, 5) = 927.09, p < 0.001.
Install or access (official source only)
Quadratic regression needs no special package in any of the three tools; use the official distribution for each and avoid third-party repackaged downloads.
- Excel: available through Microsoft Excel; see the Microsoft Excel support documentation for access and the function reference (including LINEST).
- R: download R from the Comprehensive R Archive Network (CRAN), the R Project’s official distribution. The
lm()andpoly()functions used below are part of base R’s stats package. - Python: install Python from python.org, then add NumPy and statsmodels following their official installation instructions.
How to run quadratic regression in Excel, R and Python
Excel
Excel has no menu item for this. Use the LINEST function, whose syntax is LINEST(known_y's, [known_x's], [const], [stats]). Microsoft’s documentation shows a polynomial fit by raising the x values to a column-number array (its example is cubic, =LINEST(yvalues, xvalues^COLUMN($A:$C))). The quadratic version below is our adaptation of that pattern, not Microsoft’s own example.
- Put x in A2:A9 and y in B2:B9.
- In an empty cell enter
=LINEST(B2:B9, A2:A9^COLUMN($A:$B), TRUE, TRUE). In versions with dynamic arrays the result spills; in older versions select a 5-row by 3-column range first and confirm as an array formula. - Read the first row. LINEST returns coefficients in reverse order, so the three values are b2 (x²), b1 (x), then b0 (the intercept). For the worked example they are −0.4548, 4.1167, −1.5536.
- With
statsset to TRUE, the second row holds the standard errors, the third row starts with R² and the standard error of y, the fourth row starts with F and the residual degrees of freedom, and the fifth row holds the regression and residual sums of squares.
R
The documented signature is poly(x, ..., degree = 1, coefs = NULL, raw = FALSE, simple = FALSE). By default raw = FALSE and poly returns orthogonal polynomials; raw = TRUE returns the plain x and x² columns.
fit_raw <- lm(y ~ poly(x, 2, raw = TRUE), data = d) # coefficients are b0, b1, b2 as in the formula
fit_orth <- lm(y ~ poly(x, 2), data = d) # orthogonal terms; same fitted values
summary(fit_raw)
anova(lm(y ~ x, data = d), fit_raw) # F-test for the quadratic term
The raw and orthogonal fits give identical predictions, R² and F-test; only the individual coefficients differ. Use raw if you want to report b1 and b2 and compute the vertex with the formula above.
Python
NumPy’s Polynomial.fit(x, y, deg, ...) fits in a scaled and shifted basis, so its .coef is not the b0, b1, b2 you would report. The documentation’s remedy is to call .convert().coef for the unscaled coefficients, which come back in increasing order of power (b0, b1, b2). On the worked example Polynomial.fit(x, y, 2).convert().coef returns −1.5536, 4.1167, −0.4548, while the unconverted .coef is 7.7625, 0.0833, −5.5708 and would be misread as the answer.
For standard errors, tests and the F-comparison use statsmodels’ formula interface, which accepts vectorised functions inside the formula:
import statsmodels.formula.api as smf
import statsmodels.api as sm
quad = smf.ols('y ~ x + I(x**2)', data=df).fit()
lin = smf.ols('y ~ x', data=df).fit()
print(quad.summary())
print(sm.stats.anova_lm(lin, quad)) # nested F-test for the quadratic term
We ran this exact code to produce the worked-example table. The statsmodels documentation page on formulas shows vectorised functions such as np.log(Literacy) inside a formula; the squared term is written I(x**2) and was run as shown.
Reading the output
- The x² coefficient (b2) carries the curvature. Its sign gives the direction of the bend and its p-value (or confidence interval) tests whether the bend is distinguishable from zero. Test it first.
- b1 changes meaning once x² is in the model. It is the slope at x = 0, which is often outside the data. Do not interpret it as “the effect of x” on its own.
- R² always rises when a term is added. Compare adjusted R², the nested F-test, or a held-out error, and look at the residual plot (see how to calculate residuals). A quadratic that raises R² by a trivial amount on a small sample is a candidate for overfitting.
- The turning point and its uncertainty. Report x* only when it falls inside the observed range, and say that it is an estimate from a fitted curve.
Raw, centered and orthogonal terms
In the raw model x and x² are strongly correlated; in the worked example the correlation is 0.976. That inflates the standard errors of b1 and b2 individually (see variance inflation factor and multicollinearity) but does not change the fitted curve or the F-test. Two common remedies:
- Centering: replace x with x − mean(x) before squaring. In the worked example (mean 4.5) the correlation between the centered x and its square is 0.000, b2 is unchanged at −0.4548, the intercept becomes 7.7625 (the fitted y at x = 4.5) and the linear coefficient becomes 0.0238 (the slope at x = 4.5). The vertex is then reported as the mean plus −b1c / (2·b2).
- Orthogonal polynomials (the R default in poly): uncorrelated columns, convenient for testing each degree in turn, but their coefficients are not interpretable on the original scale.
Troubleshooting
- Excel shows only one number: LINEST returns an array. Enter it so it spills, or select the output range first in older versions.
- Coefficients in Excel look reversed: they are, by design; LINEST lists the highest power first and the intercept last.
- R coefficients differ from your hand calculation: you used the default orthogonal poly. Add
raw = TRUE. - NumPy coefficients do not match the textbook: you read the scaled
.coef. Use.convert().coef. - R poly() errors about degree: with the default
raw = FALSE, the degree must be less than the number of unique x values, so a quadratic needs at least three distinct x values. - The vertex is outside your data: the fit is locally a monotone curve. Do not report a maximum or minimum.
Free alternatives
| Tool | How to fit the quadratic term (as documented) |
|---|---|
| R | lm(y ~ poly(x, 2, raw = TRUE)) (poly documented in the R stats manual) |
| Python with NumPy | Polynomial.fit(x, y, 2).convert().coef |
| Python with statsmodels | smf.ols('y ~ x + I(x**2)', data=df).fit() |
Excel is covered above because LINEST is the function documented for polynomial fits there.
FAQ
Is quadratic regression the same as polynomial regression?
Quadratic regression is polynomial regression of degree 2. Degree 3 adds an x³ term (the cubic LINEST pattern in Microsoft’s documentation), and so on. Each added degree adds one more possible bend and more risk of overfitting.
Is quadratic regression linear or nonlinear?
It is a linear model, because the outcome is a linear combination of the coefficients, so ordinary least squares applies. The fitted relationship between x and y is curved.
How do I find the maximum or minimum of the fitted curve?
Compute x* = −b1 / (2·b2) from the raw-coefficient fit, then substitute it into the equation to get ŷ. It is a maximum if b2 is negative and a minimum if b2 is positive.
Is there a quadratic regression calculator or Desmos option?
Graphing calculators and online tools can return the same three coefficients for small datasets. For a research analysis you need the standard errors, residuals and model comparison shown above, which the software in this guide provides.
How should I report a quadratic regression?
Report the model form, n, all three coefficients with standard errors or confidence intervals, R² and adjusted R², the test of the quadratic term, whether x was centered, and the turning point only if it falls within the observed range.
Sources
Sources checked 2026-11-07. Worked-example values were computed for this page.
- Microsoft Support, LINEST function.
- R Core Team, poly: Compute Orthogonal Polynomials, R stats manual.
- NumPy documentation, numpy.polynomial.polynomial.Polynomial.fit.
- statsmodels documentation, Fitting models using R-style formulas.







