Back to the lesson·Lesson 1 of 8·A model is a comparison
The coefficient is the difference
The same deck as the downloads, rendered as a page. Start the slideshow to present it full screen — arrow keys or a click advance one slide, Escape leaves.
What this lesson covers
- The same comparison, written twice
- What every term means
- Where the two routes differ, and by how much
- Three predictors, three readings
- What the model does not say
- Report it whole
- What comes next
Speaker notes
Fit attendance against a single feeding dummy. The intercept is 85.21% — the mean of schools with no programme. The coefficient is 4.94 points — the difference between the two means, to four decimal places. A regression with one dummy is a two-group comparison and nothing more.The same comparison, written twice — In Python (cont.)
import pandas as pd import numpy as np import statsmodels.formula.api as smf attendance = pd.read_csv("school-attendance-2024.v1.csv") roster = pd.read_csv("school-roster-2024.v1.csv").drop_duplicates( "student_id", keep="last") # two never-de-registered transfers MARKS = {"true": True, "Y": True, "false": False, "N": False} marked = attendance[attendance["present"].isin(MARKS)].copy() marked["attended"] = marked["present"].map(MARKS) per_student = (marked.groupby("student_id")["attended"].mean() .rename("rate").reset_index() .merge(roster, on="student_id"))Speaker notes
The statistics course compared attendance in schools with a feeding programme against schools without one, and got 90.15% against 85.21%. Fit that as a regression and look at what comes back.The same comparison, written twice — In Python (cont.)
model = smf.ols("rate ~ feeding_programme", data=per_student).fit() print(model.summary().tables[1])The same comparison, written twice — In R
library(dplyr) per_student |> lm(rate ~ feeding_programme, data = _) |> summary()The same comparison, written twice
Term Coefficient SE t Intercept 0.8521 0.0070 121.30 feeding_programme[True]0.0494 0.0088 5.63 The same comparison, written twice — In Python
means = per_student.groupby("feeding_programme")["rate"].mean() print(means) print(f"difference: {means[True] - means[False]:.4f}")The same comparison, written twice — In R
per_student |> summarise(rate = mean(rate), .by = feeding_programme)The same comparison, written twice
- Mean without a programme: 0.8521. Mean with: 0.9015. Difference: 0.0494
Speaker notes
Mean without a programme: 0.8521. Mean with: 0.9015. Difference: 0.0494. The intercept is the first number and the coefficient is the third, exactly. This is not an approximation or a coincidence — with one binary predictor and nothing else, least squares has no freedom to do anything but reproduce the two group means.What every term means
- The intercept is the fitted value when every predictor is zero — Here that is
feeding_programme = False, so the… - A coefficient is the change in the outcome for a one-unit change in that predictor, with the others held fixed — For a…
- A standard error is the same standard error the last course computed — The
tcolumn is coefficient over standard…
Speaker notes
Three sentences cover the whole of reading a coefficient table, and they hold for every model in this course. The intercept is the fitted value when every predictor is zero. Here that isfeeding_programme = False, so the intercept is the mean of schools with no programme. It is not "the average attendance" and it is not a baseline in any programme sense — it is a specific group's mean, and which group depends entirely on how the predictors were coded. A coefficient is the change in the outcome for a one-unit change in that predictor, with the others held fixed. For a dummy, "one unit" is False → True, so the coefficient is a difference between two groups. A standard error is the same standard error the last course computed. Thetcolumn is coefficient over standard error, and the interval is the coefficient plus or minus about two standard errors. Nothing new is being introduced.- The intercept is the fitted value when every predictor is zero — Here that is
What every term means — In Python
coef = model.params["feeding_programme[T.True]"] se = model.bse["feeding_programme[T.True]"] print(f"{coef:+.4f} 95% CI [{coef - 1.96*se:+.4f}, {coef + 1.96*se:+.4f}]")What every term means
- +0.0494, 95% CI +0.0322 to +0.0665 — the same interval, arrived at from the same data by a different route
Speaker notes
+0.0494, 95% CI +0.0322 to +0.0665 — the same interval, arrived at from the same data by a different route.Where the two routes differ, and by how much
- Ordinary least squares assumes one residual variance for both groups — It is the pooled-variance t-test written in…
Speaker notes
The regressiontis 5.63 and the Welch t-test in the statistics course gave 5.41. That difference is not an error in either. Ordinary least squares assumes one residual variance for both groups. It is the pooled-variance t-test written in matrix form. Welch's test does not assume it, which is why the last course recommended Welch by default.Where the two routes differ, and by how much — In Python
from scipy import stats fed = per_student[per_student["feeding_programme"]]["rate"] unfed = per_student[~per_student["feeding_programme"]]["rate"] print(f"variances: {fed.var():.5f} and {unfed.var():.5f}") print("pooled t:", stats.ttest_ind(fed, unfed).statistic.round(2)) print("Welch t:", stats.ttest_ind(fed, unfed, equal_var=False).statistic.round(2))Where the two routes differ, and by how much — In R
t.test(rate ~ feeding_programme, data = per_student, var.equal = TRUE) t.test(rate ~ feeding_programme, data = per_student)Where the two routes differ, and by how much
- Regression reproduces the pooled t exactly — When the group variances are close the two barely differ; here they are…
Speaker notes
Regression reproduces the pooled t exactly. When the group variances are close the two barely differ; here they are 0.019 and 0.025, and the gap is the reason Welch exists. A regression cannot be told "use Welch" — the fix is a robust standard error, which lesson 6 introduces for a related reason.Three predictors, three readings
Predictor Coefficient reads as Binary ( feeding_programme)The difference between the two groups Continuous ( age_years)The change per one year Categorical with k levels k−1 coefficients, each a difference against the omitted level Speaker notes
A regression's usefulness starts when the predictor is not binary. Each type reads differently and each is a comparison.Three predictors, three readings — In Python
enrolment = pd.read_csv("school-enrolment-2024.v1.csv") current = enrolment[enrolment["school_year"] == 2024] joined = per_student.merge( current[["student_id", "sex", "age_years", "admin2"]], on="student_id") print(smf.ols("rate ~ admin2", data=joined).fit().params.round(4)) print(joined.groupby("admin2")["rate"].mean().round(4))Three predictors, three readings — In R
lm(rate ~ admin2, data = joined) |> coef() joined |> summarise(rate = mean(rate), .by = admin2)Three predictors, three readings
- A categorical predictor produces one coefficient per level except one — and every coefficient is a comparison against…
- Say the reference level in the table caption — "Against Artibonite" is four words and it is the difference between a…
Speaker notes
A categorical predictor produces one coefficient per level except one, and every coefficient is a comparison against that omitted level. Change which level is omitted and every number in the column changes while the model is identical — which is the first sign that a coefficient means nothing without knowing what it is being compared against. Say the reference level in the table caption. "Against Artibonite" is four words and it is the difference between a readable table and one a reader has to guess at.What the model does not say
- It is not an effect — The 4.9-point coefficient is the difference between two sets of schools that were not randomised…
- It is not a prediction worth making — Regression is taught elsewhere as a prediction machine, and for programme data…
Speaker notes
Two things a coefficient never carries, both of which readers supply for themselves. It is not an effect. The 4.9-point coefficient is the difference between two sets of schools that were not randomised into having a feeding programme. Every regression in this course is a comparison of groups that already differed; lesson 5 is about which of those differences you can and cannot remove. It is not a prediction worth making. Regression is taught elsewhere as a prediction machine, and for programme data that framing does damage: it invites model comparison by fit rather than by whether the comparison is the one the report needs. Fit the model your question implies, then read the coefficient it was fitted for, and treat R² as a diagnostic rather than a score — lesson 8 is about a model with an R² of 0.03 that is the most useful result in its report.Report it whole — Example
Attendance and school feeding, February to April 2024 Linear model, one predictor, 1,200 students. Intercept (no programme) 85.21% Feeding programme +4.94 points 95% CI +3.22 to +6.65 The coefficient is the difference between the two group means and is reported as such. Schools were not randomised into the programme; the comparison is observational. The standard error assumes independent students. It is not, and lesson 6 gives the corrected version.Report it whole
- The last line is a promissory note the course pays off — and writing it is better than not knowing it is owed
Speaker notes
The last line is a promissory note the course pays off, and writing it is better than not knowing it is owed. A model reported with a caveat you can name is in better shape than one reported without.What comes next
- One predictor reproduces a comparison you could have made without a model.
Speaker notes
One predictor reproduces a comparison you could have made without a model. The next lesson adds the second predictor, which is where a regression starts earning its keep — and where "adjusting for" gets a precise meaning that is narrower than most reports assume.