The Law of Iterated Expectations

What it says

\[E(Y) = E\big[\,E(Y \mid X)\,\big].\]

The overall mean of \(Y\) equals the average of the within-group means of \(Y\), weighted by how common each group is.

  • Plain version: to get the average height in a country, take the average height in each region, then average those regional averages, weighting each region by its population. You land on the same number as averaging every person directly.
  • The inner \(E(Y \mid X)\) is a number that depends on \(X\) (a different average in each group). The outer \(E[\cdot]\) averages those numbers over the distribution of \(X\).

Why it is true (discrete case)

Start from the right-hand side and expand, one step at a time:

  • \(E(Y \mid X = x) = \sum_y y \, P(Y = y \mid X = x)\) — the mean of \(Y\) inside group \(x\).
  • Weight each group by \(P(X = x)\) and add up: \(\sum_x P(X=x) \sum_y y \, P(Y=y \mid X=x)\).
  • Since \(P(Y=y \mid X=x)\,P(X=x) = P(X=x, Y=y)\), this is \(\sum_x \sum_y y \, P(X=x, Y=y)\).
  • Sum over \(x\) first: \(\sum_y y \, P(Y=y) = E(Y)\).

So “average the group means, weighted by group size” collapses back to the grand mean. Nothing is assumed — it is just careful bookkeeping of a joint distribution.

Why you care in econometrics

This is the quiet engine behind the unbiasedness proof, and it settles a fact you will use constantly:

If \(E(u \mid X) = E(u)\) (mean independence), then \(\mathrm{Cov}(X, u) = 0\).

Here is the whole argument, and the law is the middle step:

\[\mathrm{Cov}(X, u) = E(Xu) - E(X)E(u), \qquad E(Xu) = E\big[X \, E(u \mid X)\big] = E\big[X \, E(u)\big] = E(X)E(u).\]

So \(\mathrm{Cov}(X,u) = 0\). The step \(E(Xu) = E[X\,E(u\mid X)]\) is the law of iterated expectations (pulling \(X\) out of the inner average because it is fixed once you condition on it). This is exactly why the zero conditional mean assumption is stronger than “uncorrelated.”

See it

Three groups A, B, C. Set each group’s mean of \(Y\) (bar height) and each group’s share of the population (bar width). The dashed red line is \(E(Y) = \sum_x E(Y \mid X=x)\,P(X=x)\) — the average bar height, weighted by width. Move any slider and watch the group means and the weights recombine into the single overall mean.

#| standalone: true
#| viewerHeight: 520

library(shiny)

ui <- fluidPage(
  tags$head(tags$style(HTML("
    .eq-box { background:#f0f4f8; border-radius:6px; padding:14px;
              margin-top:14px; font-size:13.5px; line-height:1.7; }
    .eq-box b { color:#2c3e50; }
  "))),
  sidebarLayout(
    sidebarPanel(
      width = 3,
      sliderInput("mA", "Mean of Y in A:", min = 0, max = 10, value = 3, step = 0.5),
      sliderInput("mB", "Mean of Y in B:", min = 0, max = 10, value = 6, step = 0.5),
      sliderInput("mC", "Mean of Y in C:", min = 0, max = 10, value = 8, step = 0.5),
      sliderInput("pA", "Share in A:", min = 0.05, max = 0.85, value = 0.5, step = 0.05),
      sliderInput("pB", "Share in B:", min = 0.05, max = 0.85, value = 0.3, step = 0.05),
      uiOutput("box")
    ),
    mainPanel(
      width = 9,
      plotOutput("bars", height = "430px")
    )
  )
)

server <- function(input, output, session) {

  d <- reactive({
    pA <- input$pA; pB <- input$pB; pC <- 1 - pA - pB
    validate(need(pC > 0.02, "Shares in A and B are too big — leave room for group C (A + B < 1)."))
    m <- c(input$mA, input$mB, input$mC)
    p <- c(pA, pB, pC)
    list(m = m, p = p, EY = sum(m * p))
  })

  output$bars <- renderPlot({
    s <- d(); par(mar = c(4.5, 4.5, 3, 1))
    w <- s$p; left <- cumsum(c(0, w))[1:3]
    cols <- c("#5b9bd5", "#70ad47", "#ed7d31")
    plot(NA, xlim = c(0, 1), ylim = c(0, 10.5),
         xlab = "population share  P(X = x)", ylab = "E(Y | X = x)",
         main = "Average the group means, weighted by group size")
    for (k in 1:3) rect(left[k], 0, left[k] + w[k], s$m[k],
                        col = adjustcolor(cols[k], 0.6), border = "white")
    for (k in 1:3) text(left[k] + w[k]/2, s$m[k] + 0.5, c("A","B","C")[k], cex = 1.2, font = 2)
    abline(h = s$EY, col = "#c0392b", lwd = 2.5, lty = 2)
    text(0.5, s$EY + 0.5, sprintf("E(Y) = %.2f", s$EY), col = "#c0392b", cex = 1.15, font = 2)
  })

  output$box <- renderUI({
    s <- d()
    tags$div(class = "eq-box",
      HTML(sprintf(
        "<b>Group means E(Y|X):</b><br>A = %.1f, B = %.1f, C = %.1f<br><br><b>Weights P(X):</b><br>%.2f, %.2f, %.2f<br><br><b>E[E(Y|X)]</b> = %.1f&middot;%.2f + %.1f&middot;%.2f + %.1f&middot;%.2f<br>= <b>%.2f = E(Y)</b>",
        s$m[1], s$m[2], s$m[3], s$p[1], s$p[2], s$p[3],
        s$m[1], s$p[1], s$m[2], s$p[2], s$m[3], s$p[3], s$EY)))
  })
}

shinyApp(ui, server)

The dashed line is not a fourth number you compute separately — it is what you get by pouring all three bars into one and levelling them off by width. That levelling is \(E[E(Y\mid X)]\), and it always equals \(E(Y)\).


Connections

  • Zero Conditional Mean — the law is the step that turns \(E(u \mid X) = E(u)\) into \(\mathrm{Cov}(X, u) = 0\).
  • Regression & the CEF — \(E(Y \mid X)\) as a function of \(X\) is the conditional expectation function; this law is its most-used property.
  • Sampling Distribution of OLS — conditioning on \(X\) and then averaging over it is exactly the unbiasedness argument.