How many bikes for a warm day?
The forecast says 20 degrees tomorrow. The shop has to decide tonight how many bikes to bring out of the store.
A year of days shows warm days are busier. A straight line through those days turns "busier" into a number you can plan with, and says how far to trust it.
By the end of this lesson
Note
I can fit a line to data and say how good it is.
Key words
| Word | What it means |
|---|---|
| Linear regression | a straight-line rule from one value to another |
| Slope | how much the answer changes when the input goes up by 1 |
| Intercept | the answer when the input is 0 |
| Residual | the true value minus the predicted one |
| Least squares | choosing the line with the smallest total of squared residuals |
| R squared | the share of the change the line explains, from 0 to 1 |
| Test set | rows kept back to check a model |
The cells students fill in are left blank in this preview.
A line is a slope and an intercept
A straight line is hires = intercept + slope * temperature. The slope says how many more hires each extra degree brings. The intercept is the line's guess at 0 degrees.
scikit-learn's LinearRegression finds both from the data. line.py fits hires against temperature alone, on all 365 days.
Fit hires against temperature
docker exec jupyter python3 steps/line.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Least squares picks the line
Every day sits a little above or below the line. That gap is the residual. Square each residual, so a gap below counts as much as one above. Then add them up.
Least squares is the line with the smallest total. Two short sums give it: the slope from how x and y move together, then the intercept from the means.
Worked example: three days and their residuals
| Temperature | Hires | Line says 130 + 13 x | Residual | Squared |
|---|---|---|---|---|
| 5 | 200 | 195 | 5 | 25 |
| 10 | 250 | 260 | -10 | 100 |
| 15 | 330 | 325 | 5 | 25 |
| Total | 0 | 150 |
The cells students fill in are left blank in this preview.
The two sums by hand
# Least squares by hand, for three days.
temps = [5, 10, 15]
hires = [200, 250, 330]
mean_x = sum(temps) / len(temps)
mean_y = sum(hires) / len(hires)
top = 0
bottom = 0
for n in range(len(temps)):
top = top + (temps[n] - mean_x) * (hires[n] - mean_y)
bottom = bottom + (temps[n] - mean_x) ** 2
slope = top / bottom
intercept = mean_y - slope * mean_x
print("slope:", slope, "intercept:", intercept)
# Change 330 to 300 and run again.
# Does the slope go up or down?
Type this in your notebook: the year
import pandas as pd
hires = pd.read_csv("hires.csv")
x = hires["temp_c"]
y = hires["hires"]
slope = ((x - x.mean()) * (y - y.mean())).sum() / ((x - x.mean()) ** 2).sum()
intercept = y.mean() - slope * x.mean()
# Compare with what line.py printed.
round(slope, 1), round(intercept, 1)
Judge a line on unseen days
A line always fits the days it learnt from quite well. The real question is how it does on new days.
So split the data. Fit on about three quarters, the training set, and keep the rest back as a test set. Then measure R squared and the mean error on the test set alone.
Fit on some days, test on others
docker exec jupyter python3 steps/split.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think a high R squared on the data a model learnt from shows it will predict well. A model is always flattered by data it has seen. Judge it on a test set it never saw, and quote that figure.
More inputs can explain more
Temperature alone explains about 60 per cent of the change from day to day. Rain matters too.
model.py fits one line with two inputs, temperature and rain. Each input gets its own slope. A negative slope means that input pulls the answer down.
Fit with temperature and rain
docker exec jupyter python3 steps/model.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Going further
In your notebook, add a third input to the line: a weekend column of 1 and 0, made with hires["weekday"].isin(["Sat", "Sun"]).astype(int).
Fit it on a training set and score it on a test set, as split.py does. Does the test R squared rise? Say in a sentence what the weekend slope means for the shop.