cassionData Analysis

Back to the lessonLesson 3 of 8Outcomes that are yes or no

An odds ratio of 0.43 for a risk ratio of 0.58

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.

Slides · PDFSlides · PowerPoint

  1. Slide 1 / 27

    What this lesson covers

    • Fit it, and read what it prints
    • Why they differ, and when it gets worse
    • The trap that catches analysts, not just readers
    • When the odds ratio is the right number
    • The sentence to write, and three not to
    • Report it whole
    • What comes next
    Speaker notes
    Cases reporting a disability complete a referral at 26.4% against 45.5%. That is 58% as likely, and the logistic model prints 0.43. The two numbers describe the same data, only one of them is what a reader thinks they are reading, and the gap widens exactly when the outcome is common.
  2. Slide 2 / 27

    Fit it, and read what it prints — In Python (cont.)

    import pandas as pd
    import numpy as np
    import statsmodels.formula.api as smf
    
    referrals = pd.read_csv("protection-referrals-2024.v1.csv")
    referrals["disability"] = referrals["disability_reported"].map(
        {"true": 1, "Yes": 1, "false": 0, "No": 0})
    
    consenting = referrals[referrals["consent_to_refer"]].copy()
    consenting["completed"] = (consenting["referral_accepted"]
                               & consenting["days_to_first_service"].notna()).astype(int)
    d = consenting.dropna(subset=["disability", "case_category", "age_band",
                                  "sex", "service_requested", "admin1"])
    
    crude = smf.logit("completed ~ disability", data=d).fit(disp=0)
    print(np.exp(crude.params).round(3))
  3. Slide 3 / 27

    Fit it, and read what it prints — In Python (cont.)

    print(np.exp(crude.conf_int()).round(3))
  4. Slide 4 / 27

    Fit it, and read what it prints — In R

    library(dplyr)
    
    crude <- glm(completed ~ disability, data = d, family = binomial())
    exp(cbind(OR = coef(crude), confint(crude)))
  5. Slide 5 / 27

    Fit it, and read what it prints

    • Odds ratio 0.430, 95% CI 0.308 to 0.601
    Speaker notes
    Odds ratio 0.430, 95% CI 0.308 to 0.601. Now compute what the data plainly say.
  6. Slide 6 / 27

    Fit it, and read what it prints — In Python

    rates = d.groupby("disability")["completed"].agg(["sum", "size", "mean"])
    print(rates.round(4))
    p1, p0 = rates.loc[1, "mean"], rates.loc[0, "mean"]
    print(f"risk ratio        {p1 / p0:.3f}")
    print(f"risk difference   {p1 - p0:+.4f}")
    print(f"odds ratio        {(p1/(1-p1)) / (p0/(1-p0)):.3f}")
  7. Slide 7 / 27

    Fit it, and read what it prints — In R

    d |> summarise(k = sum(completed), n = n(), rate = mean(completed), .by = disability)
  8. Slide 8 / 27

    Fit it, and read what it prints

    GroupCompletednRate
    Disability reported5219726.4%
    Not reported6291,38445.5%
  9. Slide 9 / 27

    Fit it, and read what it prints

    MeasureValueReads as
    Risk ratio0.58158% as likely to complete
    Risk difference−19.1 points19 fewer completions per 100 cases
    Odds ratio0.430—
  10. Slide 10 / 27

    Fit it, and read what it prints

    • The model printed 0.430 and the answer is 0.581 — Both are correct; they are answers to different questions, and only…
    Speaker notes
    The model printed 0.430 and the answer is 0.581. Both are correct; they are answers to different questions, and only one of them is the question anyone asked.
  11. Slide 11 / 27

    Why they differ, and when it gets worse — In Python

    for p0 in (0.02, 0.10, 0.25, 0.45, 0.70):
        p1 = 0.6 * p0                      # a true risk ratio of 0.6 throughout
        odds = (p1 / (1 - p1)) / (p0 / (1 - p0))
        print(f"baseline {p0:5.0%}   risk ratio 0.60   odds ratio {odds:.3f}")
    Speaker notes
    An odds is p / (1 − p). When p is small the denominator is near 1 and odds are close to risks, so the two ratios nearly agree. When p is large they do not.
  12. Slide 12 / 27

    Why they differ, and when it gets worse — In R

    # One true risk ratio, five baselines, five different odds ratios.
  13. Slide 13 / 27

    Why they differ, and when it gets worse

    Baseline riskTrue risk ratioOdds ratio
    2%0.600.59
    10%0.600.57
    25%0.600.53
    45%0.600.45
    70%0.600.31
  14. Slide 14 / 27

    Why they differ, and when it gets worse

    • The odds ratio is not a fixed distortion of the risk ratio — it depends on the baseline — At a 2% outcome the two are…
    • Programme outcomes are not rare — Referral completion is 43%, attendance is 88%, enrolment is 97%, and at those…
    Speaker notes
    The odds ratio is not a fixed distortion of the risk ratio — it depends on the baseline. At a 2% outcome the two are interchangeable, which is why the odds ratio survives in epidemiology, where outcomes are rare. Programme outcomes are not rare. Referral completion is 43%, attendance is 88%, enrolment is 97%, and at those baselines the odds ratio is a long way from what a reader will take it to mean. This is not a subtlety — it is the ordinary case in this work.
  15. Slide 15 / 27

    The trap that catches analysts, not just readers — In Python

    adjusted = smf.logit(
        "completed ~ disability + case_category + age_band + sex"
        " + service_requested + admin1", data=d).fit(disp=0)
    print(f"crude OR    {np.exp(crude.params['disability']):.3f}")
    print(f"adjusted OR {np.exp(adjusted.params['disability']):.3f}")
    Speaker notes
    Adding covariates changes an odds ratio even when nothing is confounded, and this surprises people who have only worked with linear models.
  16. Slide 16 / 27

    The trap that catches analysts, not just readers — In R

    adjusted <- glm(completed ~ disability + case_category + age_band + sex +
                      service_requested + admin1, data = d, family = binomial())
    exp(coef(adjusted))["disability"]
  17. Slide 17 / 27

    The trap that catches analysts, not just readers

    Odds ratioRisk difference
    Crude0.430−19.05 points
    Adjusted0.388−19.41 points
  18. Slide 18 / 27

    The trap that catches analysts, not just readers

    • The odds ratio moved by 10% and the risk difference moved by a third of a point — The usual reading — "adjustment…
    • A collapsible measure equals the average of the subgroup measures — Risk differences and risk ratios are; odds ratios…
    • So an adjusted odds ratio and a crude odds ratio cannot be compared to judge confounding — That comparison is the…
    Speaker notes
    The odds ratio moved by 10% and the risk difference moved by a third of a point. The usual reading — "adjustment revealed a stronger effect" — is wrong here. Nothing was confounded away; the odds ratio simply is not collapsible. A collapsible measure equals the average of the subgroup measures. Risk differences and risk ratios are; odds ratios are not. Add a covariate that predicts the outcome and the conditional odds ratio moves away from 1 even when the covariate is unrelated to the exposure. So an adjusted odds ratio and a crude odds ratio cannot be compared to judge confounding. That comparison is the standard move in every regression tutorial and it does not work for logistic models. Compare risk differences instead, which the next lesson computes.
  19. Slide 19 / 27

    When the odds ratio is the right number

    • A case-control study — Cases and controls are sampled separately, so risks are not estimable at all and the odds ratio…
    • A rare outcome — Below about 10%, the odds ratio approximates the risk ratio closely enough that the distinction stops…
    • Comparing to published literature that reports odds ratios — Then report both, and lead with the risk difference
    Speaker notes
    Three cases, and it is worth being precise because the answer is not "never". A case-control study. Cases and controls are sampled separately, so risks are not estimable at all and the odds ratio is the only ratio the design supports. It is what the measure was invented for. A rare outcome. Below about 10%, the odds ratio approximates the risk ratio closely enough that the distinction stops mattering. Say which one you computed anyway. Comparing to published literature that reports odds ratios. Then report both, and lead with the risk difference.
  20. Slide 20 / 27

    When the odds ratio is the right number — In Python

    def report(label, p1, p0):
        return (f"{label}: {p1:.1%} vs {p0:.1%} — "
                f"risk difference {p1-p0:+.1%}, risk ratio {p1/p0:.2f}, "
                f"odds ratio {(p1/(1-p1))/(p0/(1-p0)):.2f}")
    
    print(report("Referral completion, disability reported", p1, p0))
  21. Slide 21 / 27

    When the odds ratio is the right number — In R

    # Print all three. The reader picks; you do not pick for them by omission.
  22. Slide 22 / 27

    The sentence to write, and three not to

    • Not this — "Cases reporting a disability were 57% less likely to complete a referral (OR 0.43)." Two errors in one…
    • Nor this — "Disability halved the odds of completion." Correct arithmetic, and "halved" will be read as risk by…
    • Nor this — "OR 0.43 (95% CI 0.31–0.60, p < 0.001)." Complete, checkable, and it tells a programme manager nothing they…
    • This — "Cases reporting a disability completed a referral at 26.4% against 45.5% for cases with no disability reported…
    • The last clause is the one that does the work — An odds ratio in a programme report with no translation beside it will…
    Speaker notes
    Not this: "Cases reporting a disability were 57% less likely to complete a referral (OR 0.43)." Two errors in one sentence — an odds ratio is not a likelihood, and 57% is not the reduction. Nor this: "Disability halved the odds of completion." Correct arithmetic, and "halved" will be read as risk by everyone who is not a statistician. Nor this: "OR 0.43 (95% CI 0.31–0.60, p < 0.001)." Complete, checkable, and it tells a programme manager nothing they can act on. This: "Cases reporting a disability completed a referral at 26.4% against 45.5% for cases with no disability reported — 19.1 percentage points lower (95% CI −25.7 to −12.4), or 58% of the completion rate. Adjusted for case category, age, sex, service requested and department, the gap is 19.4 points. The adjusted odds ratio is 0.39; it is reported here for comparability with published studies and should not be read as a risk." The last clause is the one that does the work. An odds ratio in a programme report with no translation beside it will be read as a risk by almost everyone who sees it, including the people who commissioned the analysis.
  23. Slide 23 / 27

    Report it whole — Example (cont.)

    Referral completion by disability status, 1,581 consenting cases
    
      Disability reported     26.4%   52 of 197
      Not reported            45.5%   629 of 1,384
    
      Risk difference   -19.1 points   95% CI -25.7 to -12.4
      Risk ratio          0.58
      Odds ratio          0.43 (crude), 0.39 (adjusted)
    
      The odds ratio is reported for comparability only. Completion is a common
      outcome (43% overall), so the odds ratio overstates the relative
      difference: 0.43 in odds is 0.58 in risk.
    
      The adjusted odds ratio differs from the crude one partly because the odds
      ratio is not collapsible, not only because of confounding. The adjusted
      risk difference is -19.4 points, essentially unchanged from crude.
  24. Slide 24 / 27

    Report it whole — Example (cont.)

    
      Fitted on the 1,581 consenting cases with complete covariates. The
      statistics course reported this gap on all 1,638 consenting cases and got
      -19.4 points; the 57-case difference is the complete-case restriction.
  25. Slide 25 / 27

    Report it whole

    • The last paragraph is two lines and prevents the single most common over-claim — that adjustment "strengthened" a…
    Speaker notes
    The last paragraph is two lines and prevents the single most common over-claim — that adjustment "strengthened" a finding when the measure simply moved for arithmetic reasons.
  26. Slide 26 / 27

    What comes next

    • A logistic model prints coefficients on a scale nobody thinks in.
    Speaker notes
    A logistic model prints coefficients on a scale nobody thinks in. The next lesson converts them back into probabilities — the number a programme manager can multiply by a caseload — and shows what the model says about the two gates the referral pathway actually has.
  27. Slide 27 / 27

    Where this goes next

    Read the full lesson, with runnable code Back to the lesson