Field note · May 27, 2023
The overall average hides 4 different numbers
Real group means for one column across 4 segments, and what the pooled average conceals.
channel splits retail-orders into 4 groups. Here is what discount_pct looks like inside each.
marketplace— mean 0.25, median 0.25 (1,151 rows)web— mean 0.07, median 0.00 (2,734 rows)android— mean 0.07, median 0.00 (1,386 rows)ios— mean 0.07, median 0.00 (1,729 rows)
Top to bottom that is 0.25 against 0.07, a spread of 279.9%. The pooled average is 0.10.
select channel,
count(*) as rows,
round(avg(discount_pct)::numeric, 2) as mean,
percentile_cont(0.5) within group (order by discount_pct) as median
from retail_orders
group by 1
order by mean desc;A spread that wide means the pooled number is not a summary, it is an artefact of the mix. Change the proportion of marketplace rows and the overall average moves without any individual group changing at all — which is how a metric goes up while every segment goes down.
Notice the mean and median columns disagree only slightly here. 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.