3.16 Statistics over a cross-section — correlate and regress
MATCH (u:UkDistricts {set:'england-and-wales'})-[:LISTS_CRIME]->(c:DistrictCrime)WHERE c.districtCode =~ '^[EW]0.*'MATCH (u)-[:HAS_PAY]->(i:DistrictIncome)WHERE i.districtCode = c.districtCodeRETURN correlate(toFloat(i.annualPay), toFloat(c.totalRate)) AS payVsCrime, regress([toFloat(i.annualPay), toFloat(c.population)], toFloat(c.totalRate), c.district) AS modelMeans: “Does income track crime across every district — and which districts have more crime than their income and population explain?”
Unlike the prose reductions, these two are arithmetic: no model call, and the same rows always
yield the same result. correlate answers “do X and Y move together” with
{r, n, dropped, ci, detectable} — and detectable is the field to honour: r = 0.23 on 8 rows
and r = 0.42 on 138 print alike, but only one is a finding, and when detectable is false the
honest sentence is “no detectable relationship on this sample”, never the bare r.
regress fits the outcome against a list of predictors and returns the whole statistical
answer in one cell: fit quality (r2, adjustedR2), one standardised beta per predictor —
named readably (imdbRating, not toFloat(m.imdbRating)), ordered strongest first, so “which
matters most” reads straight off the list — and two residual lists, abovePrediction and
belowPrediction: the rows furthest above and furthest below the fit, labelled by the third
argument. The residual lists are usually the story: a fit with r² = 0.09 says income barely
explains crime and the lists name the places that break the pattern — and when one side is empty,
the divergence itself runs one way, which is a finding of its own.
Three habits keep it honest:
- Feed it rates, not raw counts, wherever population varies across the group — a raw count regression rediscovers “big places have more of everything”.
- Group for free:
RETURN d.region, regress(…)fits one model per region, exactly ascount()would group. Small groups refuse with null rather than overfit — a fit needs max(k+2, 5·k) usable rows for k predictors. - Say “associates with”, never “predicts causally” — the fit is a description of the
cross-section, and rows with missing operands are dropped and counted in
dropped, so a thin join cannot masquerade as a strong signal.

