Answer

What is quantile regression and when should you use it?

Inspired by a question on Cross Validated ·

regressionquantile regression

The short answer

Ordinary regression describes how the average outcome changes with a predictor. Quantile regression does the same for a chosen percentile, such as the median or the 90th. Use it when the top and bottom of the distribution move differently, or when the tails are what you care about. It compares distributions, so it does not by itself tell you what happens to one particular person or item.

What it estimates

Least squares regression models the mean of the outcome given the predictors. Quantile regression models a conditional quantile instead. For the 90th percentile (written tau = 0.9), the fitted line estimates the value below which 90% of outcomes fall at each value of the predictor, and its slope says how much that percentile changes per unit of the predictor.

The fitting rule is a tilted version of absolute error. Points above the line are penalised with weight tau and points below it with weight 1 - tau, and the line that minimises the total sits where about a fraction tau of the points fall below it. With tau = 0.5 the weights are equal, which gives median regression, also called least absolute deviations.

One handy property: quantiles survive increasing transformations. If you model log income, exponentiating the fitted 90th percentile line gives the 90th percentile of income itself. Means do not behave this way, which is why a back-transformed mean from a log model needs a correction.

When it is worth using

If the whole distribution simply shifts by the same amount, every quantile has the same slope and ordinary regression tells you the same story more efficiently. Very high or low quantiles also need plenty of data, because only a small share of the observations lies beyond the line, so estimates at tau = 0.99 are far noisier than at the median.

An example in R and Python

These simulated sale prices have a middle that grows steadily and a spread that widens with time. On the log scale the true slopes are about 0.011 at the 10th percentile, 0.030 at the median and 0.049 at the 90th. The R version uses the quantreg package, the standard tool, and the Python tab uses scikit-learn with its penalty switched off. The figures below come from one seeded R run. Python's random numbers differ from R's, so its figures differ slightly but show the same pattern.

R

library(quantreg)   # the standard package for quantile regression
set.seed(2026)

# Simulated sale prices: the middle grows steadily, the spread widens over time
n <- 800
years <- runif(n, 0, 15)    # years since the data begin
log_price <- 12.5 + 0.03 * years + (0.25 + 0.015 * years) * rnorm(n)

ols <- lm(log_price ~ years)
round(coef(ols), 4)         # one slope, for the mean only

taus <- c(0.1, 0.5, 0.9)
fit <- rq(log_price ~ years, tau = taus)
round(coef(fit), 4)         # a separate line for each quantile

# Percentile bootstrap, resampling whole sales
boot <- replicate(300, {
  i <- sample(n, replace = TRUE)
  coef(rq(log_price[i] ~ years[i], tau = taus))[2, ]
})
round(apply(boot, 1, quantile, c(0.025, 0.975)), 4)

# Is the 90th percentile rising faster than the 10th?
gap <- coef(fit)[2, 3] - coef(fit)[2, 1]
round(c(gap = gap, quantile(boot[3, ] - boot[1, ], c(0.025, 0.975))), 4)

# Percent growth per year implied by each slope
round(100 * (exp(coef(fit)[2, ]) - 1), 2)

Python

import numpy as np
from sklearn.linear_model import LinearRegression, QuantileRegressor

rng = np.random.default_rng(2026)

# Simulated sale prices: the middle grows steadily, the spread widens over time
n = 800
years = rng.uniform(0, 15, n)    # years since the data begin
log_price = 12.5 + 0.03 * years + (0.25 + 0.015 * years) * rng.normal(size=n)
X = years.reshape(-1, 1)

ols = LinearRegression().fit(X, log_price)
print("OLS:", round(ols.intercept_, 4), round(ols.coef_[0], 4))

taus = [0.1, 0.5, 0.9]

def slopes(X, y):
    out = []
    for tau in taus:     # alpha=0 turns off the penalty, matching R's rq()
        m = QuantileRegressor(quantile=tau, alpha=0, solver="highs").fit(X, y)
        out.append(m.coef_[0])
    return np.array(out)

fit = slopes(X, log_price)
print("slopes:", np.round(fit, 4))

# Percentile bootstrap, resampling whole sales
boot = np.empty((300, 3))
for b in range(300):
    i = rng.integers(0, n, n)
    boot[b] = slopes(X[i], log_price[i])
print(np.round(np.percentile(boot, [2.5, 97.5], axis=0), 4))

# Is the 90th percentile rising faster than the 10th?
gap = fit[2] - fit[0]
print("gap:", round(gap, 4),
      np.round(np.percentile(boot[:, 2] - boot[:, 0], [2.5, 97.5]), 4))

# Percent growth per year implied by each slope
print(np.round(100 * (np.exp(fit) - 1), 2))
(Intercept)       years 
    12.5193      0.0284 
            tau= 0.1 tau= 0.5 tau= 0.9
(Intercept)  12.2180  12.5261  12.8292
years         0.0056   0.0283   0.0508
      tau= 0.1 tau= 0.5 tau= 0.9
2.5%   -0.0014   0.0198   0.0411
97.5%   0.0165   0.0353   0.0629
   gap   2.5%  97.5% 
0.0452 0.0331 0.0570 
tau= 0.1 tau= 0.5 tau= 0.9 
    0.56     2.87     5.21

Reading the output

The mean slope is 0.0284 and the median slope is 0.0283, so if you only looked at the centre you would conclude that prices rise a little under 3% a year. The quantile slopes tell a fuller story: the 90th percentile climbs by about 5.21% a year and the 10th by about 0.56%, and the interval for the 10th percentile, [-0.0014, 0.0165], includes zero.

To say the slopes differ, look at their difference rather than at whether each one is significant on its own. Here the 90th percentile slope exceeds the 10th by 0.0452, with a bootstrap interval of [0.0331, 0.0570]. Note that separately fitted quantile lines can cross, especially near the edges of the data, which would be impossible for true quantiles. Crossing is a warning to simplify the model or stay within the range of the data.

Quantiles describe groups, not individuals

A common mistake is to read the 90th percentile line as the path of an item that starts at the 90th percentile. The line compares the distribution at one time with the distribution at another. Items can swap places in between, so reading it as an individual trajectory adds an assumption that ranks stay fixed, and the data alone cannot confirm that.

If the same units are measured more than once, that repeated information is usually the better guide to individual change. A model of within-unit change, such as fixed effects, a random-effects model, or a repeat-sales model for property prices, compares each unit with itself and so removes stable differences between units. Two cautions apply. Units measured repeatedly may not represent the rest, because items that are resold or that stay in a study can differ from those that do not. And quantile regression for longitudinal data exists but is harder to fit and interpret (Koenker, 2004), so it needs more care than a pooled fit. With repeated measures, also resample whole units when you bootstrap, not single rows.

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

State which quantiles you fitted, why, and how the intervals were obtained, then report each slope with its interval. Using the example above, with slopes rounded to three decimals because they are small:

"We fitted quantile regressions of log sale price on years at the 10th, 50th and 90th percentiles (N = 800), with 95% percentile bootstrap confidence intervals based on 300 resamples. The 90th percentile rose faster (b = 0.051, 95% CI [0.041, 0.063]) than the median (b = 0.028, 95% CI [0.020, 0.035]) and the 10th percentile (b = 0.006, 95% CI [-0.001, 0.017]). The slopes at the 90th and 10th percentiles differed by 0.045, 95% CI [0.033, 0.057]."

With several quantiles and predictors, a table with one column per quantile is easier to read, and a plot of each slope against tau shows the pattern at a glance. In the method section, name the software and the type of interval, since different standard error methods can give different results.

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.