Causal inferenceChapter 11
Regression as linear algebra
OLS as linear algebra: treatment indicators, group means, and fully interacted regressions.
Contents
In a finite population, potential outcomes are fixed and only the assignment is
random. For fixed
The identities in this chapter hold for each assignment separately. They show that the treatment coefficient in a simple regression equals difference in means, that group indicators recover group means, and that a fully interacted regression separates into within-group fits. Statistical properties follow by combining these identities with results about the corresponding estimators.
Matrices in Mathlib
A design matrix is Matrix (Fin n) (Fin k) ℝ: Fin n → Fin k → ℝ; the type Matrix exists so that * can mean matrix
multiplication rather than pointwise multiplication. Matrix.of is the
relabelling that takes you from the function to the matrix, and it is why
simpleMatrix below is written the way it is.
Three notations do all the work. Xᵀ is the transpose. X * Y is matrix
multiplication, so Xᵀ * X is the Gram matrix — the sums of squares and cross
products, X *ᵥ v is Matrix.mulVec, the matrix–vector
product, whose
theorem gram_apply {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (j l : Fin k) :
(Xᵀ * X) j l = ∑ i, X i j * X i l := by
simp [Matrix.mul_apply, Matrix.transpose_apply]
theorem mulVec_assoc {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (β : Fin k → ℝ) :
(Xᵀ * X) *ᵥ β = Xᵀ *ᵥ (X *ᵥ β) :=
(Matrix.mulVec_mulVec β Xᵀ X).symm
theorem isOLS_unfold {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (y : Fin n → ℝ) (β : Fin k → ℝ) :
IsOLS X y β ↔ (Xᵀ * X) *ᵥ β = Xᵀ *ᵥ y := Iff.rflAll three are definitional, and the walkthrough is mostly about what “definitional” means in each case.
simp [Matrix.mul_apply, Matrix.transpose_apply]gram_apply.Matrix.mul_applyunfolds an entry of a product, , andMatrix.transpose_applysays . Together they turn into — the cross-product of columns and , which is what the Gram matrix collects.(Matrix.mulVec_mulVec β Xᵀ X).symmmulVec_assoc, a term-mode proof rather than a tactic block.Matrix.mulVec_mulVecstates ;.symmturns the equation round, giving . Read right to left it is the one move every proof in this chapter starts with: the normal equations are about the residual , and getting there means peeling off the front.Iff.rflisOLS_unfold, and worth a second look:Iff.rflsays the two sides are the same proposition up to unfolding definitions, so no proof is needed. It is Lean’s way of saying “that is literally the definition” —IsOLS X y βis notation for the normal equations and nothing more.
The precedence trap
*ᵥ binds more tightly than *. The Gram matrix applied to a coefficient
vector must therefore be parenthesized:
(Xᵀ * X) *ᵥ β
Without the parentheses, Lean parses the expression as
Xᵀ * (X *ᵥ β)
The second expression attempts to multiply a matrix by a vector using matrix
multiplication, so it does not type-check. This particular mistake is benign
because Lean rejects it. Chaining is benign too: *ᵥ is right-associative, so
Xᵀ *ᵥ X *ᵥ β means Xᵀ *ᵥ (X *ᵥ β), which mulVec_assoc above says is the
same vector as (Xᵀ * X) *ᵥ β. What does not announce itself is a transpose in
the wrong place: with X *ᵥ β and Xᵀ *ᵥ β type-check and they
are different vectors. Read the parenthesization and the transposes of every
*ᵥ you meet.
Small matrices, and one determinant
Literal vectors are ![a, b, …] and literal matrices are !![a, b; c, d],
given row by row. A Matrix.det_fin_two_of, and it
is the only determinant this chapter needs.
theorem literal_mulVec : (!![(1 : ℝ), 2; 3, 4]) *ᵥ ![1, 1] = ![3, 7] := by
funext i
fin_cases i <;> simp [Matrix.mulVec, dotProduct, Fin.sum_univ_two] <;> norm_num
theorem literal_det : (!![(1 : ℝ), 2; 3, 4]).det = -2 := by
norm_num [Matrix.det_fin_two_of]Neither is a theorem about anything; they are here so that the tactics that make literals compute have been seen once.
funext iliteral_mulVecis an equality of two functionsFin 2 → ℝ, andfunextreduces that to an equality at each indexi. Vectors in Mathlib are functions, so this is how nearly every vector identity begins.fin_cases i <;> simp [Matrix.mulVec, dotProduct, Fin.sum_univ_two] <;> norm_numfin_cases isplitsFin 2into its two elements, giving one goal per entry. In each,simpunfoldsMatrix.mulVecanddotProductto a sum andFin.sum_univ_twowrites that two-term sum out explicitly;norm_numthen does the arithmetic, and . The<;>combinator applies what follows it to every goal the previous tactic left.norm_num [Matrix.det_fin_two_of]literal_det.Matrix.det_fin_two_ofis the formula for the determinant of a literal matrix with rows and , andnorm_numevaluates .
The inverse is total, and that is a trap
Mathlib’s Matrix.inv is a total function. Every square matrix has an
inverse in the sense that A⁻¹ always elaborates; when A.det is not a unit,
A⁻¹ is defined to be the zero matrix. This is the same design decision as
ExerciseEveryone treated: the estimate is zero
Take z to be all of Finset.univ. Then the treatment column equals the
intercept column, the Gram matrix is singular, and ols is the zero vector.
This is a core exercise: it is short, and the fact it establishes is the
reason every later theorem carries a hypothesis.
theorem ols_simpleMatrix_of_all_treated (y : Fin n → ℝ) :
ols (simpleMatrix (Finset.univ : Assignment n)) y = 0 := by
sorryHint
simpleMatrix_gram_det gives you the determinant and simp shows it is not a
unit; then unfold ols, Matrix.nonsing_inv_apply_not_isUnit converts
¬ IsUnit A.det into A⁻¹ = 0, and Matrix.zero_mulVec finishes.
Show solution
theorem ols_simpleMatrix_of_all_treated (y : Fin n → ℝ) :
ols (simpleMatrix (Finset.univ : Assignment n)) y = 0 := by
have hdet : ¬ IsUnit ((simpleMatrix (Finset.univ : Assignment n))ᵀ
* simpleMatrix (Finset.univ : Assignment n)).det := by
rw [simpleMatrix_gram_det]
simp
rw [ols, Matrix.nonsing_inv_apply_not_isUnit _ hdet, Matrix.zero_mulVec]Least squares without a model
The formalization does not define OLS by minimization. It defines it by the normal equations.
def IsOLS {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (y : Fin n → ℝ) (β : Fin k → ℝ) : Prop :=
(Xᵀ * X) *ᵥ β = Xᵀ *ᵥ yIsOLS X y β is a Prop, not a number: it is the plain linear equation
The equivalent geometric statement is the one most statisticians actually carry
in their heads: the residual is orthogonal to every column of
ExerciseNormal equations = orthogonality (core)
Column fun i => X i j, so orthogonality to it is
This is the lemma every later proof goes through: each column of the design matrix contributes one scalar equation about sums over units, and we get to choose columns that isolate the treated and control groups.
theorem isOLS_iff_orthogonal {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (y : Fin n → ℝ)
(β : Fin k → ℝ) :
IsOLS X y β ↔ ∀ j, ∑ i, X i j * (y i - (X *ᵥ β) i) = 0 := by
sorryHint
After unfold IsOLS, Matrix.mulVec_mulVec used right-to-left rewrites
(Xᵀ * X) *ᵥ β as Xᵀ *ᵥ (X *ᵥ β), and funext_iff turns the equality of
vectors into a statement about each entry. Both directions are then the same
simp only [Matrix.mulVec, dotProduct, Matrix.transpose_apply, mul_sub, Finset.sum_sub_distrib] at the hypothesis and the goal, followed by linarith.
Show solution
theorem isOLS_iff_orthogonal {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (y : Fin n → ℝ)
(β : Fin k → ℝ) :
IsOLS X y β ↔ ∀ j, ∑ i, X i j * (y i - (X *ᵥ β) i) = 0 := by
unfold IsOLS
rw [← Matrix.mulVec_mulVec, funext_iff]
constructor
· intro h j
have hj := h j
simp only [Matrix.mulVec, dotProduct, Matrix.transpose_apply, mul_sub,
Finset.sum_sub_distrib] at hj ⊢
linarith
· intro h j
have hj := h j
simp only [Matrix.mulVec, dotProduct, Matrix.transpose_apply, mul_sub,
Finset.sum_sub_distrib] at hj ⊢
linarithThe textbook formula is a definition here, not a theorem, and it is kept
firmly separate from IsOLS.
noncomputable def ols {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (y : Fin n → ℝ) : Fin k → ℝ :=
(Xᵀ * X)⁻¹ *ᵥ (Xᵀ *ᵥ y)Two facts connect them, both short enough to be warm-ups. Existence: when the Gram matrix is invertible the formula does solve the normal equations.
ExerciseThe formula solves the equations (warm-up)
When the Gram matrix is invertible, the textbook formula does solve the normal equations.
theorem ols_isOLS {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (y : Fin n → ℝ)
(h : IsUnit (Xᵀ * X).det) : IsOLS X y (ols X y) := by
sorryHint
Unfold both definitions, then three rewrites: Matrix.mulVec_mulVec collapses
the two matrix–vector products into one, Matrix.mul_nonsing_inv cancels the
inverse, and Matrix.one_mulVec clears up.
Show solution
theorem ols_isOLS {k : ℕ} (X : Matrix (Fin n) (Fin k) ℝ) (y : Fin n → ℝ)
(h : IsUnit (Xᵀ * X).det) : IsOLS X y (ols X y) := by
unfold IsOLS ols
rw [Matrix.mulVec_mulVec, Matrix.mul_nonsing_inv _ h, Matrix.one_mulVec]And uniqueness, which is the no-perfect-collinearity condition doing its job.
ExerciseA nonsingular Gram matrix pins β down (warm-up)
Two solutions of the normal equations coincide when the Gram matrix is nonsingular. Statistically this is the no-perfect-collinearity condition doing its job.
theorem isOLS_unique {k : ℕ} {X : Matrix (Fin n) (Fin k) ℝ} {y : Fin n → ℝ} {β β' : Fin k → ℝ}
(h : (Xᵀ * X).det ≠ 0) (hβ : IsOLS X y β) (hβ' : IsOLS X y β') : β = β' := by
sorryHint
One exact. Matrix.mulVec_injective_of_det_ne_zero says that A *ᵥ · is
injective when A.det ≠ 0, and hβ.trans hβ'.symm gives you two vectors with
the same image.
Show solution
theorem isOLS_unique {k : ℕ} {X : Matrix (Fin n) (Fin k) ℝ} {y : Fin n → ℝ} {β β' : Fin k → ℝ}
(h : (Xᵀ * X).det ≠ 0) (hβ : IsOLS X y β) (hβ' : IsOLS X y β') : β = β' := by
exact Matrix.mulVec_injective_of_det_ne_zero h (hβ.trans hβ'.symm)Put together they give isOLS_iff_eq_ols: under IsUnit (Xᵀ * X).det, “solves
the normal equations” and “equals
Simple regression is the difference in means
Regress the observed outcome on a constant and the treatment indicator. Two
columns: ones, and
noncomputable def simpleMatrix (z : Assignment n) : Matrix (Fin n) (Fin 2) ℝ :=
Matrix.of fun i => ![1, Z z i]Its Gram matrix is the two-by-two array with entries
ExerciseThe Gram matrix (core)
Compute the Gram matrix of the two-column design: it is !![n, n₁; n₁, n₁]
with n₁ = #z.
theorem simpleMatrix_gram (z : Assignment n) :
(simpleMatrix z)ᵀ * (simpleMatrix z) = !![(n : ℝ), (#z : ℝ); (#z : ℝ), (#z : ℝ)] := by
sorryHint
ext j k reduces the matrix equation to its four entries and fin_cases j <;> fin_cases k splits them out; then one simp with Matrix.mul_apply (which
unfolds (A * B) j k into ∑ i, A j i * B i k), simpleMatrix,
Matrix.transpose_apply, sum_Z and Z_mul_self closes all four — the last two
are the whole content.
Show solution
theorem simpleMatrix_gram (z : Assignment n) :
(simpleMatrix z)ᵀ * (simpleMatrix z) = !![(n : ℝ), (#z : ℝ); (#z : ℝ), (#z : ℝ)] := by
ext j k
fin_cases j <;> fin_cases k <;>
simp [Matrix.mul_apply, simpleMatrix, Matrix.transpose_apply, sum_Z, Z_mul_self]Its determinant is simpleMatrix_gram_det), so the regression
is non-degenerate exactly when both groups are non-empty — the same condition
under which the difference in means is defined at all. That is not a
coincidence, and the next theorem says why.
Unpacking the normal equations is a genuine ↔; no rank condition is needed to
read the system, only to solve it.
Proposition. Let
By the orthogonality form of the normal equations,
theorem isOLS_simpleMatrix_iff (z : Assignment n) (y : Fin n → ℝ) (β : Fin 2 → ℝ) :
IsOLS (simpleMatrix z) y β ↔
(∑ i, y i = (n : ℝ) * β 0 + (#z : ℝ) * β 1)
∧ (∑ i ∈ z, y i = (#z : ℝ) * β 0 + (#z : ℝ) * β 1) := by
rw [isOLS_iff_orthogonal, Fin.forall_fin_two]
have expand0 : ∑ i, (y i - (β 0 + Z z i * β 1))
= (∑ i, y i) - ((n : ℝ) * β 0 + (#z : ℝ) * β 1) := by
rw [Finset.sum_sub_distrib, Finset.sum_add_distrib, Finset.sum_const, Finset.card_univ,
Fintype.card_fin, nsmul_eq_mul, ← Finset.sum_mul, sum_Z]
have step1 : ∀ i, Z z i * (y i - (β 0 + Z z i * β 1))
= Z z i * y i - (Z z i * β 0 + Z z i * β 1) := by
intro i; linear_combination (-(β 1)) * Z_mul_self z i
have expand1 : ∑ i, Z z i * (y i - (β 0 + Z z i * β 1))
= (∑ i ∈ z, y i) - ((#z : ℝ) * β 0 + (#z : ℝ) * β 1) := by
rw [Finset.sum_congr rfl fun i _ => step1 i, Finset.sum_sub_distrib, Finset.sum_add_distrib,
← Finset.sum_mul, ← Finset.sum_mul, sum_Z, ← sum_mem_eq_sum_Z_mul]
simp only [simpleMatrix_apply_zero, simpleMatrix_apply_one, simpleMatrix_mulVec,
one_mul, expand0, expand1]
constructor
· rintro ⟨h0, h1⟩; exact ⟨by linarith, by linarith⟩
· rintro ⟨h0, h1⟩; exact ⟨by linarith, by linarith⟩The Lean does exactly that, with the two sums expanded in named haves so that
the final step is a pair of linear rearrangements.
rw [isOLS_iff_orthogonal, …]Trade the matrix equation for the orthogonality condition — the exercise a few sections up. The left-hand side is now “for every column
, the residual is orthogonal to column ”.Goalz : Assignment n y : Fin n → ℝ β : Fin 2 → ℝ ⊢ (∀ (j : Fin 2), ∑ i, simpleMatrix z i j * (y i - (simpleMatrix z *ᵥ β) i) = 0) ↔ ∑ i, y i = ↑n * β 0 + ↑(#z) * β 1 ∧ ∑ i ∈ z, y i = ↑(#z) * β 0 + ↑(#z) * β 1rw […, Fin.forall_fin_two]Fin.forall_fin_twoturns a statement quantified overFin 2into the conjunction of its two instances, so the two normal equations are now visible separately rather than hidden behind a .Goal⊢ ∑ i, simpleMatrix z i 0 * (y i - (simpleMatrix z *ᵥ β) i) = 0 ∧ ∑ i, simpleMatrix z i 1 * (y i - (simpleMatrix z *ᵥ β) i) = 0 ↔ ∑ i, y i = ↑n * β 0 + ↑(#z) * β 1 ∧ ∑ i ∈ z, y i = ↑(#z) * β 0 + ↑(#z) * β 1have expand0 : ∑ i, (y i - (β 0 + Z z i * β 1)) = (∑ i, y i) - ((n : ℝ) * β 0 + (#z : ℝ) * β 1) := by rw [Finset.sum_sub_distrib, Finset.sum_add_distrib, Finset.sum_const, Finset.card_univ, Fintype.card_fin, nsmul_eq_mul, ← Finset.sum_mul, sum_Z]The intercept equation, expanded.
Finset.sum_sub_distribandFinset.sum_add_distribsplit the sum of a difference and of a sum;Finset.sum_constwithFinset.card_univ,Fintype.card_finandnsmul_eq_mulevaluates as ; andFinset.sum_mul, reversed, pulls out of so thatsum_Zcan replace by .have step1 : ∀ i, Z z i * (y i - (β 0 + Z z i * β 1)) = Z z i * y i - (Z z i * β 0 + Z z i * β 1) := by intro i; linear_combination (-(β 1)) * Z_mul_self z iThe one place the dummy’s idempotence is used. Expanding the left-hand side produces a
that the right-hand side writes as , so the two differ by .linear_combinationcloses a goal of the form “these two sides differ by the given multiple of a known equation”, and the known equation isZ_mul_self z i, .have expand1 : ∑ i, Z z i * (y i - (β 0 + Z z i * β 1)) = (∑ i ∈ z, y i) - ((#z : ℝ) * β 0 + (#z : ℝ) * β 1) := by rw [Finset.sum_congr rfl fun i _ => step1 i, Finset.sum_sub_distrib, Finset.sum_add_distrib, ← Finset.sum_mul, ← Finset.sum_mul, sum_Z, ← sum_mem_eq_sum_Z_mul]The slope equation, expanded. Rewrite each term with
step1, split the sums as before, pull and then out with the two reversedFinset.sum_muls so thatsum_Zcan turn both into , and finallysum_mem_eq_sum_Z_mulreversed turns into — the treated-group total.simp only [simpleMatrix_apply_zero, simpleMatrix_apply_one, simpleMatrix_mulVec, one_mul, expand0, expand1]Substitute. The first two
@[simp]lemmas say the columns of the design matrix are and ;simpleMatrix_mulVecsays the fitted value is ;one_mulclears the multiplication by the constant column; andexpand0/expand1replace the two sums by their closed forms. Both sides of the iff are now the same two equations, one written as “difference ” and the other as “left right”.Goal⊢ ∑ i, y i - (↑n * β 0 + ↑(#z) * β 1) = 0 ∧ ∑ i ∈ z, y i - (↑(#z) * β 0 + ↑(#z) * β 1) = 0 ↔ ∑ i, y i = ↑n * β 0 + ↑(#z) * β 1 ∧ ∑ i ∈ z, y i = ↑(#z) * β 0 + ↑(#z) * β 1constructor · rintro ⟨h0, h1⟩; exact ⟨by linarith, by linarith⟩ · rintro ⟨h0, h1⟩; exact ⟨by linarith, by linarith⟩Both directions are the same move twice: destructure the conjunction and move a term across the equals sign, which
linarithdoes. There is no mathematics left at this point.
In words: orthogonality to the column of ones says the residuals sum to zero
over everybody; orthogonality to the treatment column says they sum to zero
over the treated. Subtract the second from the first and you get
TheoremThe slope on a treatment dummy is the difference in means
If
Read the hypotheses. There is no design, no expectation, no assumption about the
potential outcomes: only 0 < #z, #z < n, and IsOLS. The conclusion holds
for every assignment
theorem simpleMatrix_ols_coeff (P : Population n) (z : Assignment n) (β : Fin 2 → ℝ)
(hpos : 0 < #z) (hlt : #z < n) (h : IsOLS (simpleMatrix z) (Yobs P z) β) :
β 0 = meanOn zᶜ P.y0 ∧ β 1 = DiM P z := by
obtain ⟨e0, e1⟩ := (isOLS_simpleMatrix_iff z (Yobs P z) β).mp h
have hcard : (#zᶜ : ℝ) = (n : ℝ) - (#z : ℝ) := by rw [card_compl, Nat.cast_sub hlt.le]
have hz0 : (0 : ℝ) < (#z : ℝ) := by exact_mod_cast hpos
have hzc0 : (0 : ℝ) < (n : ℝ) - (#z : ℝ) := by
have : (#z : ℝ) < (n : ℝ) := by exact_mod_cast hlt
linarith
have hsplit : (∑ i ∈ z, P.y1 i) + (∑ i ∈ zᶜ, P.y0 i) = ∑ i, Yobs P z i := by
rw [Finset.sum_congr rfl fun i hi => (Yobs_of_mem hi).symm,
Finset.sum_congr rfl fun i hi => (Yobs_of_not_mem (by simpa using hi)).symm]
exact Finset.sum_add_sum_compl z _
rw [Finset.sum_congr rfl fun i hi => Yobs_of_mem hi] at e1
rw [← hsplit] at e0
have hb0 : β 0 = (∑ i ∈ zᶜ, P.y0 i) / ((n : ℝ) - (#z : ℝ)) := by
field_simp
linarith
have hb1 : β 1 = (∑ i ∈ z, P.y1 i) / (#z : ℝ) - β 0 := by
field_simp
linarith
exact ⟨by rw [meanOn, hcard, hb0], by rw [DiM, hb1, hb0, hcard]⟩Write
and both collapse because the regressor is binary. Since
which is nonzero exactly when both groups are non-empty. For the numerator,
Dividing,
Finally the observed outcome is
The Lean never forms that ratio. It treats the two normal equations as a linear
system in field_simp/linarith pairs are the two divisions.
obtain ⟨e0, e1⟩ := (isOLS_simpleMatrix_iff z (Yobs P z) β).mp hReplace the hypothesis
IsOLSby the two scalar equations of the previous proposition, namede0(over everybody) ande1(over the treated). From here on the proof is arithmetic with two equations in two unknowns.Goale0 : ∑ i, Yobs P z i = ↑n * β 0 + ↑(#z) * β 1 e1 : ∑ i ∈ z, Yobs P z i = ↑(#z) * β 0 + ↑(#z) * β 1 ⊢ β 0 = meanOn zᶜ P.y0 ∧ β 1 = DiM P zhave hcard : (#zᶜ : ℝ) = (n : ℝ) - (#z : ℝ) := by rw [card_compl, Nat.cast_sub hlt.le]The control count as a real number.
card_complgives in , andNat.cast_sub hlt.leis what allows that truncated subtraction to be cast to as an honest subtraction. This is the first of the two placeshltis needed.have hz0 : (0 : ℝ) < (#z : ℝ) := by exact_mod_cast hpos in , so that the treated group’s denominator is nonzero.have hzc0 : (0 : ℝ) < (n : ℝ) - (#z : ℝ) := by have : (#z : ℝ) < (n : ℝ) := by exact_mod_cast hlt linarithAnd
. These two are the hypothesesfield_simpwill consult below before it is willing to clear a denominator; without them Lean’s would make the statement false, as the pitfall at the end of the chapter shows.have hsplit : (∑ i ∈ z, P.y1 i) + (∑ i ∈ zᶜ, P.y0 i) = ∑ i, Yobs P z i := by rw [Finset.sum_congr rfl fun i hi => (Yobs_of_mem hi).symm, Finset.sum_congr rfl fun i hi => (Yobs_of_not_mem (by simpa using hi)).symm]; exact Finset.sum_add_sum_compl z _The bridge between potential and observed outcomes. Each
Finset.sum_congrrewrites one of the two sums termwise:Yobs_of_memsays the observed outcome of a treated unit is andYobs_of_not_memthat a control’s is , both used backwards so that both sums become sums ofYobs. ThenFinset.sum_add_sum_complglues a sum over and a sum over into a sum over everybody.rw [Finset.sum_congr rfl fun i hi => Yobs_of_mem hi] at e1Apply the same rewriting to the treated equation, so
e1now reads .rw [← hsplit] at e0And to the total equation, in the other direction:
e0now reads . The two equations are now stated in the same vocabulary as the goal.Goale0 : ∑ i ∈ z, P.y1 i + ∑ i ∈ zᶜ, P.y0 i = ↑n * β 0 + ↑(#z) * β 1 e1 : ∑ i ∈ z, P.y1 i = ↑(#z) * β 0 + ↑(#z) * β 1 hcard : ↑(#zᶜ) = ↑n - ↑(#z) hz0 : 0 < ↑(#z) hzc0 : 0 < ↑n - ↑(#z) ⊢ β 0 = meanOn zᶜ P.y0 ∧ β 1 = DiM P zhave hb0 : β 0 = (∑ i ∈ zᶜ, P.y0 i) / ((n : ℝ) - (#z : ℝ)) := by field_simp; linarithThe intercept.
field_simp, usinghzc0, multiplies out the division and leaves — which is exactlye0minuse1, solinarithcloses it. Eliminating between the two normal equations is what isolates the control mean.Goal⊢ β 0 * (↑n - ↑(#z)) = ∑ i ∈ zᶜ, P.y0 ihave hb1 : β 1 = (∑ i ∈ z, P.y1 i) / (#z : ℝ) - β 0 := by field_simp; linarithThe slope.
field_simp, usinghz0this time, leaves , which ise1rearranged;linarithagain. Note that comes out as (treated mean) , and is the control mean, so this is the difference in means — but Lean still has to be shown that.Goalhb0 : β 0 = (∑ i ∈ zᶜ, P.y0 i) / (↑n - ↑(#z)) ⊢ β 1 * ↑(#z) = ∑ i ∈ z, P.y1 i - ↑(#z) * β 0exact ⟨by rw [meanOn, hcard, hb0], by rw [DiM, hb1, hb0, hcard]⟩The two halves of the conclusion. For the first, unfold
meanOnand usehcardto make its denominator syntactically the one inhb0. For the second, unfoldDiMand substitutehb1, thenhb0for the it still contains, thenhcardonce more to line the denominators up.
The same holds for the closed form (ols_simpleMatrix_eq_DiM), because with
both groups non-empty the determinant
Inheriting a sampling property
Now, and only now, does a design appear.
theorem ols_simpleMatrix_unbiased_completeRandomization {n₁ : ℕ} (hle : n₁ ≤ n)
(P : Population n) (hpos : 0 < n₁) (hlt : n₁ < n) :
(completeRandomization n n₁ hle).expect
(fun z => ols (simpleMatrix z) (Yobs P z) 1) = P.tau := by
have hsupp : ∀ z, (completeRandomization n n₁ hle).prob z ≠ 0 →
ols (simpleMatrix z) (Yobs P z) 1 = DiM P z := by
intro z hz
have hcard : #z = n₁ := card_eq_of_prob_ne_zero hz
exact ols_simpleMatrix_eq_DiM P (by omega) (by omega)
rw [(completeRandomization n n₁ hle).expect_congr_of_support hsupp]
exact DiM_unbiased_completeRandomization hle P hpos hltThere is no new argument in that proof and there is not supposed to be. On
every assignment the design can produce,
intro z hz; have hcard : #z = n₁ := card_eq_of_prob_ne_zero hzInside
hsupp: fix an assignment with nonzero probability.card_eq_of_prob_ne_zerois the chapter-7 fact that complete randomization puts all its mass on the assignments of size exactly .Goalz : Assignment n hz : (completeRandomization n n₁ hle).prob z ≠ 0 hcard : #z = n₁ ⊢ ols (simpleMatrix z) (Yobs P z) 1 = DiM P zexact ols_simpleMatrix_eq_DiM P (by omega) (by omega)The algebraic identity, applied to this assignment. Its two hypotheses are
and ; withhcardin context they follow fromhposandhltby pure arithmetic, which is whatomegadoes.rw [(completeRandomization n n₁ hle).expect_congr_of_support hsupp]expect_congr_of_supportswaps the regression coefficient for under the expectation, which is legitimate precisely because they agree on the support. Every trace of regression has now left the goal.Goal⊢ (completeRandomization n n₁ hle).expect (DiM P) = P.tauexact DiM_unbiased_completeRandomization hle P hpos hltAnd that goal is a theorem from chapter 8, quoted verbatim.
That is the whole message. Running lm(y ~ z) does not invoke a model of the
outcomes, does not need homoskedasticity, and does not need normal errors. Under
this design it recomputes an estimator whose sampling distribution is governed
entirely by the randomization — and whose unbiasedness you proved three chapters
ago.
Three units, in numbers
Identities are easier to believe once you have watched one happen. Here is a population of three units with both potential outcomes written down, which we can do because this is a thought experiment rather than data.
noncomputable def exP : Population 3 := ⟨![4, 6, 9], ![1, 2, 3]⟩
def exZ : Assignment 3 := {0, 1}
theorem exZ_card : #exZ = 2 := by decide
theorem exZ_compl : exZᶜ = {2} := by decideThe two decides attached to it evaluate small Finset facts in the kernel:
The estimand and the estimate:
theorem exP_tau : exP.tau = 13 / 3 := by
rw [Population.tau_eq]
norm_num [exP, Population.effect, Fin.sum_univ_three, Matrix.cons_val_two,
Matrix.head_cons, Matrix.tail_cons]
theorem exP_DiM : DiM exP exZ = 2 := by
rw [DiM, exZ_compl, exZ]
norm_num [exP, Matrix.cons_val_two, Matrix.head_cons, Matrix.tail_cons]rw [Population.tau_eq]exP_tau.Population.tau_eqrestates as the mean of the unit-level effects, , which is the form the numbers can be substituted into.norm_num [exP, Population.effect, Fin.sum_univ_three, Matrix.cons_val_two, Matrix.head_cons, Matrix.tail_cons]Substitute and compute.
exPunfolds the population to its two literal vectors,Population.effectto , andFin.sum_univ_threeexpands the sum overFin 3into three terms. The threeMatrix.cons_*lemmas are what read an entry out of a![…]literal — a vector literal is a nestedcons, so getting at its third entry means peeling two of them.norm_numthen does .rw [DiM, exZ_compl, exZ]exP_DiM. Unfold the estimator, then replace by the literal using thedecided fact above and by the literal , so that both sums are over explicit sets.norm_num [exP, Matrix.cons_val_two, Matrix.head_cons, Matrix.tail_cons]And compute:
. Note which potential outcome each group contributes — for the treated units, for the control — which is what makes this the difference in means and not a difference of comparable quantities.
ExerciseCheck a coefficient vector by hand (warm-up)
The observed outcomes are isOLS_simpleMatrix_iff are
theorem exP_isOLS : IsOLS (simpleMatrix exZ) (Yobs exP exZ) ![3, 2] := by
sorryHint
Start with rw [isOLS_simpleMatrix_iff] and constructor; then in each goal
simp with Yobs_eq_ite (which turns the observed outcome into an if),
exZ, exP and Fin.sum_univ_three (which expands the sums over Fin 3), and
norm_num does the arithmetic.
Show solution
theorem exP_isOLS : IsOLS (simpleMatrix exZ) (Yobs exP exZ) ![3, 2] := by
rw [isOLS_simpleMatrix_iff]
constructor <;>
simp [Yobs_eq_ite, exZ, exP, Fin.sum_univ_three] <;> norm_numtheorem exP_ols : ols (simpleMatrix exZ) (Yobs exP exZ) 1 = 2 := by
rw [ols_simpleMatrix_eq_DiM exP (by rw [exZ_card]; norm_num) (by rw [exZ_card]; norm_num),
exP_DiM]rw [ols_simpleMatrix_eq_DiM exP (by rw [exZ_card]; norm_num) (by rw [exZ_card]; norm_num), exP_DiM]One rewrite with two side goals.
ols_simpleMatrix_eq_DiMis the closed-form version of the headline theorem, so applying it turns the left-hand side into , andexP_DiM— just proved — evaluates that as . The twobyblocks discharge its hypotheses and :exZ_cardreplaces by andnorm_numchecks and . Nothing is recomputed; the matrix inverse never appears.
Group indicators return group means
Replace the single dummy by one dummy per group and drop the intercept. This is the saturated, or fixed-effects, regression: with blocks crossed with treatment it is the model a blocked experiment is usually written as.
def groupMatrix {K : ℕ} (g : Fin n → Fin K) : Matrix (Fin n) (Fin K) ℝ :=
Matrix.of fun i k => if g i = k then 1 else 0Every row has exactly one
ExerciseThe fitted value is your own group's coefficient (warm-up)
The fitted value of unit i under the group-indicator design is the
coefficient of i’s own group, β (g i).
theorem groupMatrix_mulVec {K : ℕ} (g : Fin n → Fin K) (β : Fin K → ℝ) (i : Fin n) :
(groupMatrix g *ᵥ β) i = β (g i) := by
sorryHint
One simp with Matrix.mulVec, dotProduct and groupMatrix in the set: the
sum ∑ k, (if g i = k then 1 else 0) * β k collapses by Finset.sum_ite_eq,
which simp will find for you.
Show solution
theorem groupMatrix_mulVec {K : ℕ} (g : Fin n → Fin K) (β : Fin K → ℝ) (i : Fin n) :
(groupMatrix g *ᵥ β) i = β (g i) := by
simp [Matrix.mulVec, dotProduct, groupMatrix]ExerciseThe coefficients are the cell means (core)
If
theorem groupMatrix_ols_coeff {K : ℕ} (g : Fin n → Fin K) (y : Fin n → ℝ) (β : Fin K → ℝ)
(h : IsOLS (groupMatrix g) y β) (k : Fin K) (hk : (groupOf g k).Nonempty) :
β k = meanOn (groupOf g k) y := by
sorryHint
Get the column equation with (isOLS_iff_orthogonal _ _ _).mp h k, prove a
hstep rewriting each summand as if g i = k then y i - β k else 0 by
groupMatrix_mulVec and by_cases hi : g i = k, then ← Finset.sum_filter
turns the sum of an if into a sum over groupOf g k. After
Finset.sum_sub_distrib and Finset.sum_const the equation reads
eq_meanOn_of_sum_eq (fed
Finset.card_pos.mpr hk) plus linarith converts that into
Show solution
theorem groupMatrix_ols_coeff {K : ℕ} (g : Fin n → Fin K) (y : Fin n → ℝ) (β : Fin K → ℝ)
(h : IsOLS (groupMatrix g) y β) (k : Fin K) (hk : (groupOf g k).Nonempty) :
β k = meanOn (groupOf g k) y := by
have hk' := (isOLS_iff_orthogonal _ _ _).mp h k
have hstep : ∀ i, groupMatrix g i k * (y i - (groupMatrix g *ᵥ β) i)
= if g i = k then y i - β k else 0 := by
intro i
rw [groupMatrix_mulVec]
by_cases hi : g i = k <;> simp [groupMatrix, hi]
rw [Finset.sum_congr rfl fun i _ => hstep i, ← Finset.sum_filter,
show Finset.univ.filter (fun i => g i = k) = groupOf g k from rfl,
Finset.sum_sub_distrib, Finset.sum_const, nsmul_eq_mul] at hk'
refine eq_meanOn_of_sum_eq (Finset.card_pos.mpr hk) ?_
linarithThis is the sense in which a saturated regression is non-parametric: it imposes nothing, it reports the cell means. Combine it with chapter 10 and the story about blocked experiments writes itself — a block-by-treatment saturated regression reports the within-block treated and control means, and the blocked estimator is a particular weighted combination of them.
Lin’s fully interacted regression
Regress the outcome on
The algebra that makes it tick is a change of basis. First, a name for “the
simple regression of
def IsSimpleOLSOn (s : Finset (Fin n)) (x y : Fin n → ℝ) (a b : ℝ) : Prop :=
(∑ i ∈ s, (y i - (a + b * x i)) = 0) ∧ (∑ i ∈ s, x i * (y i - (a + b * x i)) = 0)ExerciseThe fitted line passes through the center of mass (core)
The intercept of a within-group fit is
theorem intercept_eq_of_isSimpleOLSOn {s : Finset (Fin n)} {x y : Fin n → ℝ} {a b : ℝ}
(h : IsSimpleOLSOn s x y a b) (hs : 0 < #s) :
a = meanOn s y - b * meanOn s x := by
sorryHint
The first normal equation is h.1. Expand its Finset.sum_sub_distrib, Finset.sum_add_distrib, Finset.sum_const (plus
nsmul_eq_mul) and ← Finset.mul_sum, unfold both meanOns, and let
field_simp and linarith divide by field_simp needs
(0 : ℝ) < #s as a have first.
Show solution
theorem intercept_eq_of_isSimpleOLSOn {s : Finset (Fin n)} {x y : Fin n → ℝ} {a b : ℝ}
(h : IsSimpleOLSOn s x y a b) (hs : 0 < #s) :
a = meanOn s y - b * meanOn s x := by
have hs' : (0 : ℝ) < (#s : ℝ) := by exact_mod_cast hs
have hexp : ∑ i ∈ s, (y i - (a + b * x i))
= (∑ i ∈ s, y i) - ((#s : ℝ) * a + b * ∑ i ∈ s, x i) := by
rw [Finset.sum_sub_distrib, Finset.sum_add_distrib, Finset.sum_const, nsmul_eq_mul,
← Finset.mul_sum]
have h1 := h.1
rw [hexp] at h1
rw [meanOn, meanOn]
field_simp
linarithAnd the design matrix:
noncomputable def interactedMatrix (z : Assignment n) (x : Fin n → ℝ) :
Matrix (Fin n) (Fin 4) ℝ :=
Matrix.of fun i => ![1, Z z i, x i, Z z i * x i]TheoremDecoupling
The fully interacted regression is two separate within-group regressions.
Nothing is assumed about
theorem isOLS_interactedMatrix_iff (z : Assignment n) (x y : Fin n → ℝ) (β : Fin 4 → ℝ) :
IsOLS (interactedMatrix z x) y β ↔
IsSimpleOLSOn z x y (β 0 + β 1) (β 2 + β 3) ∧ IsSimpleOLSOn zᶜ x y (β 0) (β 2) := by
rw [isOLS_iff_orthogonal, forall_fin_four]
set r : Fin n → ℝ := fun i => y i - (interactedMatrix z x *ᵥ β) i with hr
have treated : ∀ i, Z z i * r i = Z z i * (y i - ((β 0 + β 1) + (β 2 + β 3) * x i)) := by
intro i
simp only [hr, interactedMatrix_mulVec]
linear_combination (-(β 1 + β 3 * x i)) * Z_mul_self z i
have treatedx : ∀ i, Z z i * x i * r i
= Z z i * (x i * (y i - ((β 0 + β 1) + (β 2 + β 3) * x i))) := by
intro i
simp only [hr, interactedMatrix_mulVec]
linear_combination (-(x i * (β 1 + β 3 * x i))) * Z_mul_self z i
have control : ∀ i, (1 - Z z i) * r i = (1 - Z z i) * (y i - (β 0 + β 2 * x i)) := by
intro i
simp only [hr, interactedMatrix_mulVec]
linear_combination (β 1 + β 3 * x i) * Z_mul_self z i
have controlx : ∀ i, (1 - Z z i) * (x i * r i)
= (1 - Z z i) * (x i * (y i - (β 0 + β 2 * x i))) := by
intro i
simp only [hr, interactedMatrix_mulVec]
linear_combination (x i * (β 1 + β 3 * x i)) * Z_mul_self z i
have bz1 : ∑ i ∈ z, (y i - ((β 0 + β 1) + (β 2 + β 3) * x i)) = ∑ i, Z z i * r i := by
rw [sum_mem_eq_sum_Z_mul]
exact (Finset.sum_congr rfl fun i _ => treated i).symm
have bz2 : ∑ i ∈ z, x i * (y i - ((β 0 + β 1) + (β 2 + β 3) * x i))
= ∑ i, Z z i * x i * r i := by
rw [sum_mem_eq_sum_Z_mul]
exact (Finset.sum_congr rfl fun i _ => treatedx i).symm
have bc1 : ∑ i ∈ zᶜ, (y i - (β 0 + β 2 * x i)) = (∑ i, r i) - ∑ i, Z z i * r i := by
rw [sum_compl_eq_sum_one_sub_Z_mul, ← Finset.sum_sub_distrib]
refine Finset.sum_congr rfl fun i _ => ?_
rw [← control i]; ring
have bc2 : ∑ i ∈ zᶜ, x i * (y i - (β 0 + β 2 * x i))
= (∑ i, x i * r i) - ∑ i, Z z i * x i * r i := by
rw [sum_compl_eq_sum_one_sub_Z_mul, ← Finset.sum_sub_distrib]
refine Finset.sum_congr rfl fun i _ => ?_
rw [← controlx i]; ring
simp only [interactedMatrix_apply_zero, interactedMatrix_apply_one,
interactedMatrix_apply_two, interactedMatrix_apply_three, one_mul, ← hr]
rw [IsSimpleOLSOn, IsSimpleOLSOn, bz1, bz2, bc1, bc2]
constructor
· rintro ⟨h0, h1, h2, h3⟩
exact ⟨⟨h1, h3⟩, ⟨by rw [h0, h1, sub_zero], by rw [h2, h3, sub_zero]⟩⟩
· rintro ⟨⟨t1, t2⟩, ⟨c1, c2⟩⟩
exact ⟨by linarith, t1, by linarith, t2⟩Write
i.e. exactly when
The new basis is what makes the system block diagonal:
It remains to identify each pair. Because
so the first two equations say that the residuals of the line
The Lean proof is that argument in two halves. Four haves do the collapse of
the fitted value — two on the treated side, two on the control side, each one
an application of rw can use. The final constructor is the
invertibility of that change of basis, one direction at a time.
rw [isOLS_iff_orthogonal, forall_fin_four]Trade the matrix equation for orthogonality to each column, then split the
Fin 4quantifier into its four instances.forall_fin_fouris a private lemma in the file rather thanfin_cases, becausefin_casesleaves the index as an anonymous constructor and the@[simp]lemmas aboutinteractedMatrix z x i 0only fire on the numeral.Goalz : Assignment n x y : Fin n → ℝ β : Fin 4 → ℝ ⊢ ∑ i, interactedMatrix z x i 0 * (y i - (interactedMatrix z x *ᵥ β) i) = 0 ∧ ∑ i, interactedMatrix z x i 1 * (y i - (interactedMatrix z x *ᵥ β) i) = 0 ∧ ∑ i, interactedMatrix z x i 2 * (y i - (interactedMatrix z x *ᵥ β) i) = 0 ∧ ∑ i, interactedMatrix z x i 3 * (y i - (interactedMatrix z x *ᵥ β) i) = 0 ↔ IsSimpleOLSOn z x y (β 0 + β 1) (β 2 + β 3) ∧ IsSimpleOLSOn zᶜ x y (β 0) (β 2)set r : Fin n → ℝ := fun i => y i - (interactedMatrix z x *ᵥ β) i with hrName the residual. This is bookkeeping —
setabbreviates the term everywhere it appears and records the abbreviation ashr— but it is what makes the eight sum identities below readable.have treated : ∀ i, Z z i * r i = Z z i * (y i - ((β 0 + β 1) + (β 2 + β 3) * x i)) := by intro i; simp only [hr, interactedMatrix_mulVec]; linear_combination (-(β 1 + β 3 * x i)) * Z_mul_self z iThe first of the four idempotence rewrites, and the heart of the theorem: on a treated unit the four-parameter fitted value collapses to the two-parameter line
. AfterinteractedMatrix_mulVecexpands the fitted value, the two sides differ by , andlinear_combinationcloses exactly such a goal when handed that multiple ofZ_mul_self z i.Goali : Fin n ⊢ Z z i * (y i - (β 0 + Z z i * β 1 + x i * β 2 + Z z i * x i * β 3)) = Z z i * (y i - (β 0 + β 1 + (β 2 + β 3) * x i))have treatedx : ∀ i, Z z i * x i * r i = Z z i * (x i * (y i - ((β 0 + β 1) + (β 2 + β 3) * x i))) := by … linear_combination (-(x i * (β 1 + β 3 * x i))) * Z_mul_self z iThe same statement with an extra factor of
, for the second treated normal equation. The scalar in thelinear_combinationpicks up the same .have control : ∀ i, (1 - Z z i) * r i = (1 - Z z i) * (y i - (β 0 + β 2 * x i)) := by … linear_combination (β 1 + β 3 * x i) * Z_mul_self z iThe control-side mirror: on a control unit the fitted value collapses to
. The scalar is the same as intreatedwith the sign flipped, because replaces .have controlx : ∀ i, (1 - Z z i) * (x i * r i) = (1 - Z z i) * (x i * (y i - (β 0 + β 2 * x i))) := by … linear_combination (x i * (β 1 + β 3 * x i)) * Z_mul_self z iAnd the same with the extra
. Those are the fourlinear_combinationcalls; each is “multiply by the right scalar”, and together they are the step in the informal proof where the four-parameter fitted value collapses to a two-parameter line inside each group.have bz1 : ∑ i ∈ z, (y i - ((β 0 + β 1) + (β 2 + β 3) * x i)) = ∑ i, Z z i * r i := by rw [sum_mem_eq_sum_Z_mul]; exact (Finset.sum_congr rfl fun i _ => treated i).symmThe first sum conversion. The left-hand side is a normal equation of the within-group regression, a sum over
;sum_mem_eq_sum_Z_mulrewrites it as an indicator-weighted sum over everybody, and thentreated, applied termwise and backwards, turns each summand into . This is what puts the two sides of the eventual iff in the same language.Goal⊢ ∑ i, Z z i * (y i - (β 0 + β 1 + (β 2 + β 3) * x i)) = ∑ i, Z z i * r ihave bz2 : ∑ i ∈ z, x i * (y i - ((β 0 + β 1) + (β 2 + β 3) * x i)) = ∑ i, Z z i * x i * r i := by rw [sum_mem_eq_sum_Z_mul]; exact (Finset.sum_congr rfl fun i _ => treatedx i).symmThe same for the second treated equation, using
treatedx.have bc1 : ∑ i ∈ zᶜ, (y i - (β 0 + β 2 * x i)) = (∑ i, r i) - ∑ i, Z z i * r i := by rw [sum_compl_eq_sum_one_sub_Z_mul, ← Finset.sum_sub_distrib]; refine Finset.sum_congr rfl fun i _ => ?_; rw [← control i]; ringThe control side, and the one place the shape differs.
sum_compl_eq_sum_one_sub_Z_multurns the sum over into , andFinset.sum_sub_distribreversed writes the target difference of two sums as one sum of differences. Comparing termwise,control ireversed replaces the left summand by andringchecks that this is . The result is stated as a difference because the four goals coming out offorall_fin_fourare about the old columns and , not about .Goal⊢ ∑ i, (1 - Z z i) * (y i - (β 0 + β 2 * x i)) = ∑ i, (r i - Z z i * r i)have bc2 : ∑ i ∈ zᶜ, x i * (y i - (β 0 + β 2 * x i)) = (∑ i, x i * r i) - ∑ i, Z z i * x i * r i := by …The same for the second control equation, using
controlx.simp only [interactedMatrix_apply_zero, interactedMatrix_apply_one, interactedMatrix_apply_two, interactedMatrix_apply_three, one_mul, ← hr]Replace the four columns of the design matrix by
, , and — those are the four@[simp]lemmas — clear the multiplication by the constant column withone_mul, and fold the residual back up intorwithhrreversed.rw [IsSimpleOLSOn, IsSimpleOLSOn, bz1, bz2, bc1, bc2]Unfold both within-group conditions into their two normal equations each, then rewrite all four with the conversions just proved. Both sides of the iff are now statements about the same four sums, and the only difference is the basis:
on the left, — written as differences — on the right. (Lean prints the three left-hand residuals in full rather than asr i; they are folded up below to keep the two bases legible.)Goal⊢ univ.sum r = 0 ∧ ∑ i, Z z i * r i = 0 ∧ ∑ i, x i * r i = 0 ∧ ∑ i, Z z i * x i * r i = 0 ↔ (∑ i, Z z i * r i = 0 ∧ ∑ i, Z z i * x i * r i = 0) ∧ ∑ i, r i - ∑ i, Z z i * r i = 0 ∧ ∑ i, x i * r i - ∑ i, Z z i * x i * r i = 0constructor · rintro ⟨h0, h1, h2, h3⟩ exact ⟨⟨h1, h3⟩, ⟨by rw [h0, h1, sub_zero], by rw [h2, h3, sub_zero]⟩⟩Left to right. Of the four hypotheses,
h1andh3(the and equations) are literally the treated pair. For the control pair,rw [h0, h1, sub_zero]substitutes for both sums in andsub_zerofinishes; likewiseh2,h3for the version.· rintro ⟨⟨t1, t2⟩, ⟨c1, c2⟩⟩ exact ⟨by linarith, t1, by linarith, t2⟩Right to left.
t1andt2are again two of the four goals outright; the other two, the and equations, follow by adding: , which islinarith’s job. That the change of basis is invertible is exactly this addition.
Once you see the decoupling, the coefficient on
Corollary (Lin’s coefficient). If
where
By the theorem,
theorem lin_coeff (z : Assignment n) (x y : Fin n → ℝ) (β : Fin 4 → ℝ)
(hpos : 0 < #z) (hlt : #z < n) (h : IsOLS (interactedMatrix z x) y β) :
β 1 = (meanOn z y - (β 2 + β 3) * meanOn z x) - (meanOn zᶜ y - β 2 * meanOn zᶜ x) := by
obtain ⟨htreat, hctrl⟩ := (isOLS_interactedMatrix_iff z x y β).mp h
have hzc : 0 < #zᶜ := by rw [card_compl]; omega
have h1 := intercept_eq_of_isSimpleOLSOn htreat hpos
have h0 := intercept_eq_of_isSimpleOLSOn hctrl hzc
linarithobtain ⟨htreat, hctrl⟩ := (isOLS_interactedMatrix_iff z x y β).mp hSplit the interacted fit into the two within-group fits, using the decoupling theorem in the forward direction. Everything after this is about two simple regressions.
have hzc : 0 < #zᶜ := by rw [card_compl]; omegaThe control group is non-empty.
card_complgives andomegaderives fromhlt; this is needed because the center-of-mass lemma divides by the group size.have h1 := intercept_eq_of_isSimpleOLSOn htreat hposThe treated intercept as a center of mass:
. Only the first of the two within-group normal equations is used, which is why the slope survives in the conclusion as a free quantity rather than being solved for.Goalhtreat : IsSimpleOLSOn z x y (β 0 + β 1) (β 2 + β 3) hctrl : IsSimpleOLSOn zᶜ x y (β 0) (β 2) hzc : 0 < #zᶜ h1 : β 0 + β 1 = meanOn z y - (β 2 + β 3) * meanOn z x ⊢ β 1 = meanOn z y - (β 2 + β 3) * meanOn z x - (meanOn zᶜ y - β 2 * meanOn zᶜ x)have h0 := intercept_eq_of_isSimpleOLSOn hctrl hzcAnd the control intercept,
.linarithSubtract the second from the first. The whole content of the corollary is that subtraction.
Rearranged, the same identity says what adjustment is doing:
ExerciseDifference in means, minus a covariate correction (stretch)
theorem lin_coeff_eq_DiM_sub_adjustment (z : Assignment n) (x y : Fin n → ℝ) (β : Fin 4 → ℝ)
(hpos : 0 < #z) (hlt : #z < n) (h : IsOLS (interactedMatrix z x) y β) :
β 1 = (meanOn z y - meanOn zᶜ y)
- ((β 2 + β 3) * meanOn z x - β 2 * meanOn zᶜ x) := by
sorryHint
One have := lin_coeff z x y β hpos hlt h and a linarith — the exercise is
reading the statement, not proving it.
Show solution
theorem lin_coeff_eq_DiM_sub_adjustment (z : Assignment n) (x y : Fin n → ℝ) (β : Fin 4 → ℝ)
(hpos : 0 < #z) (hlt : #z < n) (h : IsOLS (interactedMatrix z x) y β) :
β 1 = (meanOn z y - meanOn zᶜ y)
- ((β 2 + β 3) * meanOn z x - β 2 * meanOn zᶜ x) := by
have := lin_coeff z x y β hpos hlt h
linarithBoth are stated for an arbitrary response vector y. Feeding them the observed
outcome vector puts the identity in the vocabulary of the rest of the tutorial.
ExerciseGroup means of the observed outcome (warm-up)
On the treated units the observed outcome is meanOn z only looks
at those units, so the two means coincide. The treated case is done for you;
prove the control-group mirror image.
theorem meanOn_Yobs_treated (P : Population n) (z : Assignment n) :
meanOn z (Yobs P z) = meanOn z P.y1 := by
rw [meanOn, meanOn, Finset.sum_congr rfl fun i hi => Yobs_of_mem hi]
theorem meanOn_Yobs_control (P : Population n) (z : Assignment n) :
meanOn zᶜ (Yobs P z) = meanOn zᶜ P.y0 := by
sorryHint
The same three-step rewrite as the treated case — meanOn, meanOn, then
Finset.sum_congr rfl with Yobs_of_not_mem — except that i ∈ zᶜ has to
become i ∉ z before Yobs_of_not_mem applies. simpa using hi does that.
Show solution
theorem meanOn_Yobs_treated (P : Population n) (z : Assignment n) :
meanOn z (Yobs P z) = meanOn z P.y1 := by
rw [meanOn, meanOn, Finset.sum_congr rfl fun i hi => Yobs_of_mem hi]
theorem meanOn_Yobs_control (P : Population n) (z : Assignment n) :
meanOn zᶜ (Yobs P z) = meanOn zᶜ P.y0 := by
rw [meanOn, meanOn, Finset.sum_congr rfl fun i hi => Yobs_of_not_mem (by simpa using hi)]theorem lin_coeff_Yobs (P : Population n) (z : Assignment n) (x : Fin n → ℝ) (β : Fin 4 → ℝ)
(hpos : 0 < #z) (hlt : #z < n) (h : IsOLS (interactedMatrix z x) (Yobs P z) β) :
β 1 = DiM P z - ((β 2 + β 3) * meanOn z x - β 2 * meanOn zᶜ x) := by
rw [lin_coeff_eq_DiM_sub_adjustment z x (Yobs P z) β hpos hlt h,
meanOn_Yobs_treated, meanOn_Yobs_control, DiM_eq_meanOn_sub_meanOn]rw [lin_coeff_eq_DiM_sub_adjustment z x (Yobs P z) β hpos hlt h, …]Apply the rearranged corollary at
. The right-hand side now mentions the group means of the observed outcome; the goal is to recognize their difference as , which is stated in terms of the potential outcomes.Goal⊢ meanOn z (Yobs P z) - meanOn zᶜ (Yobs P z) - ((β 2 + β 3) * meanOn z x - β 2 * meanOn zᶜ x) = DiM P z - ((β 2 + β 3) * meanOn z x - β 2 * meanOn zᶜ x)rw […, meanOn_Yobs_treated, meanOn_Yobs_control, …]The two warm-up lemmas above: on the treated units the observed outcome is
, so its mean over is the mean of over ; on the controls it is .rw […, DiM_eq_meanOn_sub_meanOn]And
DiM_eq_meanOn_sub_meanOnsays is exactly that difference of two subgroup means — it holds byrfl, since that is what the estimator is defined to be. The two sides are now identical.
ExercisePerfect balance means no adjustment (stretch)
If both within-group covariate means are zero, Lin’s coefficient is the plain difference in means, whatever the fitted slopes turn out to be.
The result shows that covariate adjustment is not a correction
for a bias that exists on average — under complete randomization there is none.
It is a correction for the imbalance realized in the assignment you actually
drew. centred_covariate_means in the library adds the other half of Lin’s
recommendation: with
theorem lin_coeff_eq_DiM_of_balanced (P : Population n) (z : Assignment n) (x : Fin n → ℝ)
(β : Fin 4 → ℝ) (hpos : 0 < #z) (hlt : #z < n)
(h : IsOLS (interactedMatrix z x) (Yobs P z) β)
(hx1 : meanOn z x = 0) (hx0 : meanOn zᶜ x = 0) :
β 1 = DiM P z := by
sorryHint
Rewrite with lin_coeff_Yobs P z x β hpos hlt h, then with the two balance
hypotheses hx1 and hx0; what is left is closed by ring.
Show solution
theorem lin_coeff_eq_DiM_of_balanced (P : Population n) (z : Assignment n) (x : Fin n → ℝ)
(β : Fin 4 → ℝ) (hpos : 0 < #z) (hlt : #z < n)
(h : IsOLS (interactedMatrix z x) (Yobs P z) β)
(hx1 : meanOn z x = 0) (hx0 : meanOn zᶜ x = 0) :
β 1 = DiM P z := by
rw [lin_coeff_Yobs P z x β hpos hlt h, hx1, hx0]
ringReading autoformalized Lean
Three ways this chapter’s results get mis-stated. All three type-check.
Dropping the non-degeneracy hypotheses
theorem ols_coeff_is_dim (P : Population n) (z : Assignment n) :
ols (simpleMatrix z) (Yobs P z) 1 = DiM P z := by
sorry
Confusing an identity with unbiasedness
theorem regression_recovers_the_ate {n₁ : ℕ} (hle : n₁ ≤ n) (P : Population n)
(z : Assignment n) (hz : #z = n₁) :
ols (simpleMatrix z) (Yobs P z) 1 = P.tau := by
sorry
A name that promises more than the statement delivers
theorem lin_estimator_is_efficient (z : Assignment n) (x y : Fin n → ℝ)
(β : Fin 4 → ℝ) (hpos : 0 < #z) (hlt : #z < n)
(h : IsOLS (interactedMatrix z x) y β) :
β 1 = (meanOn z y - meanOn zᶜ y)
- ((β 2 + β 3) * meanOn z x - β 2 * meanOn zᶜ x) := by
sorry
The habit this chapter is trying to build is narrower than “read Lean carefully”. It is: when a formalization claims a regression coefficient is an estimator, check the hypotheses that keep both sides non-degenerate; and when it claims a regression coefficient has a property, check that the property is stated about a design, because algebra alone can never give you one.