Field note · June 11, 2023

The overall average hides 8 different numbers

Real group means for one column across 8 segments, and what the pooled average conceals.

1 min read ·Analytics practice ·statistics practice

country splits retail-orders into 8 groups. Here is what revenue_usd looks like inside each.

  • JP — mean 119, median 58 (587 rows)
  • CA — mean 113, median 57 (715 rows)
  • FR — mean 113, median 60 (522 rows)
  • AU — mean 111, median 60 (501 rows)
  • DE — mean 110, median 63 (738 rows)
  • US — mean 109, median 61 (2,655 rows)

Top to bottom that is 119 against 107, a spread of 11.8%. The pooled average is 111.

sql
select country,
  count(*)                                                 as rows,
  round(avg(revenue_usd)::numeric, 2)                      as mean,
  percentile_cont(0.5) within group (order by revenue_usd) as median
from retail_orders
group by 1
order by mean desc;

The groups are close enough that the pooled average is a fair summary. That is worth confirming rather than assuming: the check costs one query and the failure mode is invisible.

Notice the mean and median columns disagree most in JP, where the mean sits well above the median. Always compute both in the group-by. The comparison between them per segment is free and tells you whether you are looking at a level difference or a tail difference.

This is the setup for Simpson's paradox — the case where every segment moves one way and the total moves the other.