Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Lecture 10: Robust Regression

Open In Colab

This notebook covers robust regression: fitting a line through data points such that the sum of the absolute prediction errors is as small as possible, which makes the line far less sensitive to outliers than ordinary least squares. The absolute value is not linear, so the model needs a trick that splits each error into a positive and a negative part; the same trick linearizes absolute values in other models. Like the applications of Lecture 9 and Multi-Period Inventory Planning, the problem is introduced in the same steps: a practical motivation, the generic model built up with the four modeling steps, the model for a concrete example, its solution in pulp, and possible extensions.

Learning outcomes

On completion of this notebook, you will be able to:

  • formulate a robust regression problem as an LO model;

  • linearize absolute values in a model with the e=e+−e−e = e^+ - e^- split;

  • adapt the model to quantile regression.

Robust Regression

Practical Motivation

A factory wants to predict how long a production order will take from its size, to promise delivery dates. It fits a line through the data of past orders. A few of those orders took far longer than usual, for example because a machine broke down. Ordinary least-squares (OLS) regression minimizes the sum of squared prediction errors, so such outliers have a large influence and pull the line toward them, just as a single extreme value pulls the mean. Minimizing the sum of absolute errors instead gives a line that is far less sensitive to outliers, the line analogue of the median. This is called robust regression (or least absolute deviation regression).

Modeling

Problem Definition and Example

Given nn data points (xi,yi)(x_i, y_i), i=1,…,ni = 1, \dots, n, find the line a+bxa + bx that minimizes the sum of the absolute prediction errors ∣yi−(a+bxi)∣|y_i - (a + b x_i)|.

As an example, take the five data points

ii12345
xix_i246810
yiy_i54937

which we store as numpy arrays and plot in Figure 1.

Source
Loading...

Figure 1:The five data points of the example.

Decision Variables

We choose the line, so its intercept aa and slope bb are decision variables. Both may be negative, so unlike in most models so far they have no sign constraint. It helps to also introduce the prediction error of each data point as a variable,

ei=yi−(a+bxi),i=1,…,n.e_i = y_i - (a + b x_i), \quad i = 1, \dots, n.

Objective

We want to minimize ∑i=1n∣ei∣\sum_{i=1}^{n} |e_i|. The absolute value is not linear, so this is not yet an LO model. There is a standard trick to fix this. Write each error as the difference of two non-negative variables,

ei=ei+−ei−,ei+,ei−≥0,e_i = e_i^+ - e_i^-, \quad e_i^+, e_i^- \ge 0,

and replace ∣ei∣|e_i| by ei++ei−e_i^+ + e_i^- in the objective, which is linear:

min⁡∑i=1n(ei++ei−).\min \sum_{i=1}^{n} (e_i^+ + e_i^-).

Why is this allowed? The same eie_i can be written as ei+−ei−e_i^+ - e_i^- in many ways, for example 2=2−0=5−32 = 2 - 0 = 5 - 3. But if both ei+e_i^+ and ei−e_i^- were positive, we could lower both by the same amount: eie_i stays the same and the objective goes down. So at the optimum, at least one of the two is 0, and then ei++ei−=∣ei∣e_i^+ + e_i^- = |e_i|. If ei+>0e_i^+ > 0, the data point lies ei+e_i^+ above the line; if ei−>0e_i^- > 0, it lies ei−e_i^- below the line.

Figure 2 shows this for the example and the line with a=3a = 3 and b=0.5b = 0.5, which is not the optimal one. The line crosses the yy-axis at height a=3a = 3 and rises b=0.5b = 0.5 for every unit that xx increases. Data points 1 and 3 lie above the line, with e1+=1e_1^+ = 1 and e3+=3e_3^+ = 3; data points 2, 4 and 5 lie below it, with e2−=1e_2^- = 1, e4−=4e_4^- = 4 and e5−=1e_5^- = 1. All other ei+e_i^+ and ei−e_i^- are 0, and the objective value of this line is 1+1+3+4+1=101 + 1 + 3 + 4 + 1 = 10.

Loading...

Figure 2:The data points of the example and the line 3+0.5x3 + 0.5x (not optimal). A data point above the line has a positive error ei+e_i^+, a data point below it a positive ei−e_i^-. The line starts at height a=3a = 3 on the yy-axis and rises by 2b=12b = 1 when xx increases by 2.

Constraints

The only constraints connect the errors to the line:

ei+−ei−=yi−(a+bxi),i=1,…,n,e_i^+ - e_i^- = y_i - (a + b x_i), \quad i = 1, \dots, n,

together with ei+,ei−≥0e_i^+, e_i^- \ge 0. The variables aa and bb are free.

Complete LO Model

min⁡∑i=1n(ei++ei−)s.t.ei+−ei−=yi−(a+bxi),i=1,…,nei+,ei−≥0,i=1,…,n.\begin{aligned} \min \quad & \sum_{i=1}^{n} (e_i^+ + e_i^-) \\ \text{s.t.} \quad & e_i^+ - e_i^- = y_i - (a + b x_i), \quad i = 1, \dots, n \\ & e_i^+, e_i^- \ge 0, \quad i = 1, \dots, n. \end{aligned}

Modeling the Example

min⁡∑i=15(ei++ei−)s.t.e1+−e1−=5−(a+2b)e2+−e2−=4−(a+4b)e3+−e3−=9−(a+6b)e4+−e4−=3−(a+8b)e5+−e5−=7−(a+10b)ei+,ei−≥0,i=1,…,5.\begin{aligned} \min \quad & \sum_{i=1}^{5} (e_i^+ + e_i^-) \\ \text{s.t.} \quad & e_1^+ - e_1^- = 5 - (a + 2b) \\ & e_2^+ - e_2^- = 4 - (a + 4b) \\ & e_3^+ - e_3^- = 9 - (a + 6b) \\ & e_4^+ - e_4^- = 3 - (a + 8b) \\ & e_5^+ - e_5^- = 7 - (a + 10b) \\ & e_i^+, e_i^- \ge 0, \quad i = 1, \dots, 5. \end{aligned}

Solving the Example in pulp

A variable without lowBound is free in pulp (its bounds default to −∞-\infty and ∞\infty), which is what we need for the intercept and the slope. The data points are the numpy arrays xs and ys from above, which pulp’s expressions accept like ordinary numbers.

status: Optimal
line: y = 4.50 + 0.25 x
sum of absolute errors: 8.0
Loading...

Figure 3:The five data points of the example and the line y=4.5+0.25xy = 4.5 + 0.25x that minimizes the sum of absolute errors.

The optimal line passes exactly through the first and the last data point. That is no coincidence: as in every LO problem, there is an optimal solution in a corner point, and for this model that means a line through (at least) two of the data points. Its objective value is 8, lower than the 10 of the line in Figure 2.

Extensions

Quantile Regression

Weighting the two parts of the error differently tilts the line toward the upper or lower points. For a quantile level 0<p<10 < p < 1, the model becomes

min⁡p∑i=1nei++(1−p)∑i=1nei−s.t.ei+−ei−=yi−(a+bxi),i=1,…,nei+,ei−≥0,i=1,…,n.\begin{aligned} \min \quad & p \sum_{i=1}^{n} e_i^+ + (1 - p) \sum_{i=1}^{n} e_i^- \\ \text{s.t.} \quad & e_i^+ - e_i^- = y_i - (a + b x_i), \quad i = 1, \dots, n \\ & e_i^+, e_i^- \ge 0, \quad i = 1, \dots, n. \end{aligned}

This is quantile regression. With p=0.5p = 0.5, both parts get the same weight, which gives the same optimal lines as robust regression (the objective is just halved). With p=0.9p = 0.9, a point above the line costs 9 times as much as a point below it, so the line moves up until about 90% of the data points lie below it. This is useful for promising delivery times that are met in 90% of the cases. Only the objective changes in the pulp model:

p = 0.5: y = 4.50 + 0.25 x
p = 0.9: y = 3.00 + 1.00 x
Loading...

Figure 4:Quantile regression on the example data for p=0.5p = 0.5 (the robust regression line) and p=0.9p = 0.9.

With only five data points, the p=0.9p = 0.9 line passes through data points 1 and 3, and the other three lie below it. With more data, roughly 10% of the points would lie above the line.

Absolute Values in Other Models

The same x=x+−x−x = x^+ - x^- split linearizes any ∣x∣|x| that appears in an objective that is minimized with a non-negative coefficient (or maximized with a non-positive one). The trick also works when the absolute value is a penalty rather than the whole objective. For the product-mix problem, suppose we dislike making the two products in very different quantities and add −∣x−y∣-|x - y| to the profit. Introduce δ+,δ−≥0\delta^+, \delta^- \ge 0 with x−y=δ+−δ−x - y = \delta^+ - \delta^- and subtract δ++δ−\delta^+ + \delta^- from the objective.

References

  • Koole, G. (2019). An Introduction to Business Analytics. §6.7 “Modeling Tricks” (quantile regression). Spreadsheet material replaced with pulp.

  • PuLP documentation: https://coin-or.github.io/pulp/