Index Ai Academy
Phase 3 · Machine learning · Data analysis

Data in pieces: partitions and mergeable summaries

Split, summarize and combine; which statistics merge exactly, merging variances, sketches for the rest, and partitioning well.

About 30 minutes · A free account opens the whole lesson

Create a free account Try part of it here

Already have an account? Sign in to open this lesson

Why it matters

Log files, click streams and training data quickly outgrow one machine's memory. Tools like Spark, BigQuery, Snowflake and DuckDB handle it with one idea: split the data into pieces, summarize each piece where it lives, and combine the small summaries. Whether that gives the right answer depends on the statistic. Average three averages and you're usually wrong; take the median of medians and you're wrong in a way nobody notices. This lesson shows which numbers combine exactly, how to combine them, and what to do about the ones that don't.

What you'll be able to do

  • Explain how big-data systems split data into partitions and combine small summaries
  • Tell mergeable statistics (count, sum, mean, variance) from ones that aren't (median, distinct count)
  • Merge means and variances from partitions exactly, without seeing the raw data again
  • Choose partitions sensibly, and know when data isn't really big at all

What it covers

  1. Split, summarize, combine
  2. Mergeable summaries
  3. Merge two basic summaries
  4. Merging variances exactly
  5. Sketches for what won't merge
  6. Find the bug: the average of averages
  7. Partitioning well, and checking whether it's big at all
Quick check
A guess first. Three servers each report their average response time for the day. How do you get the overall average?
  1. Average the three averages
  2. It can't be done without the raw times
  3. Weight each average by its server's request count
  4. Take the middle one of the three
Show the answer

Weight each average by its server's request count

A server that handled ten times as many requests should count ten times as much. With each average and its count, the overall mean is exact; the plain average of averages is only right when the counts are equal.

Split, summarize, combine

When data won't fit on one machine, or would take too long to read on one, systems split it into partitions: files by day, by customer, or by a hash of some key. Each worker reads its partitions and produces a small summary, such as a count and a total. The summaries travel to one place and are merged into the answer. Moving a few numbers is cheap; moving terabytes isn't, so the whole design rests on this shape, often called map and reduce.

import statistics as st

partitions = [[12, 15, 22, 30], [41, 38, 25], [14, 9, 61, 33, 20]]   # response times, three servers
everything = [x for p in partitions for x in p]
means = [st.mean(p) for p in partitions]
print("mean of the means:", round(st.mean(means), 2))
print("true mean:", round(st.mean(everything), 2))
print("weighted by counts:", round(sum(m * len(p) for m, p in zip(means, partitions)) / len(everything), 2))

The plain average of averages is off because the partitions have different sizes. Carry each mean's count along, and the merge is exact.

Quick check
Each partition reports its median. What does this print?
import statistics as st
partitions = [[12, 15, 22, 30], [41, 38, 25], [14, 9, 61, 33, 20]]
everything = [x for p in partitions for x in p]
print(st.median([st.median(p) for p in partitions]), st.median(everything))
  1. 20 20
  2. 20 23.5
  3. 18.5 23.5
  4. 23.5 23.5
Show the answer

20 23.5

The medians are 18.5, 38 and 20, whose median is 20, but the true median of all twelve values is 23.5. No small summary of each partition lets you merge medians exactly.

Mergeable summaries

A summary is mergeable when the summary of two partitions together can be computed from their two summaries alone. Some are easy:

  • count: add them; sum: add them;
  • min and max: the smaller of the minimums, the larger of the maximums;
  • mean: keep the sum and the count, and divide only at the end.

Some aren't. The median needs to know where every value sits relative to every other, and the number of distinct values can't be added either: two partitions with 1,000 distinct customers each might share all of them or none. These need either all the raw data in one place, which is expensive, or an approximate sketch, which comes later in this lesson.

The rule of thumb: if you can compute it with a running total that you update one value at a time, there's usually a way to merge it.

Watch for rates and percentages, which look like single numbers but merge like means. A refund rate of 5% in one store and 20% in another doesn't average to 12.5% unless the stores sold the same amount. Carry the numerator and the denominator, refunds and orders, and divide at the very end, only after every partition has been merged in.

Drill

Merge two basic summaries

A summary is a tuple (count, total, low, high). Write merge(a, b) that returns the summary of both partitions together, and mean(s) that returns the mean from a summary.

def merge(a, b):
    return a


def mean(s):
    return 0


s = merge((4, 79, 12, 30), (3, 104, 25, 41))
print(s, round(mean(s), 2))
In the lesson, your code is checked as you write it.
Try it now

Response times with the server that handled each request. Group by server and compare the mean with the count.

In your own words
What makes a statistic mergeable, and which common ones aren't?

Write a sentence or two, then compare it with a good answer in the lesson.

Merging variances exactly

Variance looks unmergeable, since it's built from distances to the mean and each partition has a different mean. It merges exactly if each partition keeps three numbers: its count nn, its mean xˉ\bar x, and M2M_2, the sum of squared distances from its own mean. For partitions AA and BB, with δ=xˉB−xˉA\delta = \bar x_B - \bar x_A:

n=nA+nBxˉ=xˉA+δ nBnM2=M2,A+M2,B+δ2 nAnBnn = n_A + n_B \qquad \bar x = \bar x_A + \delta\,\frac{n_B}{n} \qquad M_2 = M_{2,A} + M_{2,B} + \delta^2\,\frac{n_A n_B}{n}

In plain words: add the two partitions' own spreads, then add the spread between their two means, weighted by how many values sit on each side.

The last term adds back the spread between the two partitions' means, which each partition alone can't see. The sample variance is then M2/(n−1)M_2 / (n - 1).

import statistics as st

def summary(xs):
    m = sum(xs) / len(xs)
    return len(xs), m, sum((x - m) ** 2 for x in xs)

def merge(a, b):
    (na, ma, m2a), (nb, mb, m2b) = a, b
    n, d = na + nb, mb - ma
    return n, ma + d * nb / n, m2a + m2b + d * d * na * nb / n

partitions = [[12, 15, 22, 30], [41, 38, 25], [14, 9, 61, 33, 20]]
s = summary(partitions[0])
for p in partitions[1:]:
    s = merge(s, summary(p))
print("merged:", round(s[2] / (s[0] - 1), 3), "direct:", round(st.variance([x for p in partitions for x in p]), 3))

The merged variance matches the direct calculation exactly, without any partition sharing its raw values.

Quick check
Two partitions: A has n = 2, mean 10 and M2 = 2; B has n = 2, mean 20 and M2 = 2. What merged M2 does this print?
na, ma, m2a = 2, 10, 2
nb, mb, m2b = 2, 20, 2
n, delta = na + nb, mb - ma
print(m2a + m2b + delta ** 2 * na * nb / n)
  1. 4.0
  2. 104.0
  3. 100.0
  4. 54.0
Show the answer

104.0

Each partition is tight (M2 = 2), but their means are 10 apart: δ² · nA · nB / n = 100 · 2 · 2 / 4 = 100 more. Most of the spread is between the partitions, which neither could see alone.

Quick check
A reminder from the grouping lesson. In pandas, what does df.groupby("server")["ms"].agg(["sum", "count"]) give you?
  1. A new column on every row
  2. A total and a count for each server
  3. One total and one count for the whole table
  4. The rows sorted by server
Show the answer

A total and a count for each server

Grouping computes a summary per group, which is exactly a per-partition summary. Carrying sum and count, rather than the mean, keeps the results mergeable.

In your own words
Why does merging two tight partitions give a large variance?

Write a sentence or two, then compare it with a good answer in the lesson.

Sketches for what won't merge

For the statistics that won't merge, there are sketches: small, mergeable structures that give approximate answers with known error.

  • Distinct counts: HyperLogLog keeps a few kilobytes per partition and estimates the number of distinct values within about 2%, merging by taking element-wise maximums. Databases use it for "unique visitors".
  • Quantiles such as the median and the 95th percentile: t-digest and similar sketches keep a compressed outline of the distribution that merges and answers any quantile closely, especially near the tails.
  • Most frequent items: Count-Min and similar sketches track the heaviest hitters in fixed memory.

When an answer must be exact, the system has to bring related data together, called a shuffle: every row moves to the worker responsible for its key. Shuffles are the most expensive step in most big-data jobs, so good pipelines filter and pre-aggregate before them.

Quick check
You need daily unique visitors across 200 web servers, and within 2% is fine. What should each server keep?
  1. The average number of visits per visitor
  2. Its own count of unique visitors
  3. A HyperLogLog sketch of visitor IDs
  4. The full list of every visitor ID
Show the answer

A HyperLogLog sketch of visitor IDs

Distinct counts don't add up across servers, since one visitor can hit many. HyperLogLog sketches merge, in a few kilobytes each, into a close estimate.

Drill

Find the bug: the average of averages

overall_mean(parts) takes each partition's (count, mean) and should return the mean of all the values together. It's off whenever partitions have different sizes. Fix it.

def overall_mean(parts):
    return sum(mean for count, mean in parts) / len(parts)


print(round(overall_mean([(4, 19.75), (3, 34.67), (5, 27.4)]), 2))
In the lesson, your code is checked as you write it.

Partitioning well, and checking whether it's big at all

Choose partitions by how the data is used. Partition logs by day, and a question about last week reads only seven partitions. Partition orders by a hash of the customer ID, and everything about one customer lands in one place, so per-customer summaries need no shuffle. Watch for skew: if one customer has 40% of the rows, the worker that gets them finishes last and the whole job waits for it. Store data in a columnar format such as Parquet, so a query that needs two columns reads two columns.

And check before you build. A few gigabytes fit comfortably on a laptop: DuckDB queries Parquet and CSV files larger than memory, and pandas can read a file in chunks and merge chunk summaries exactly as above. A cluster is worth its cost and complexity only when one machine really can't keep up.

Mergeable summaries also make data that arrives late cheap to handle. When yesterday's partition gets a few extra events this morning, summarize only the new events and merge them into yesterday's summary, instead of reprocessing the whole day. Streaming systems are built on this: each window keeps a mergeable summary and folds new events in as they come.

Quick check
A colleague wants a Spark cluster to analyze a 3 GB CSV of last month's orders. What do you suggest first?
  1. Split it into 3,000 files by hand
  2. Try one machine: DuckDB, or pandas in chunks
  3. Sample 1% and never look at the rest
  4. Rent the largest cluster available
Show the answer

Try one machine: DuckDB, or pandas in chunks

3 GB is small for one modern machine. DuckDB or chunked pandas will answer most questions in seconds, with far less to set up and pay for.

In your own words
How do you count distinct visitors across 200 servers, and when is data not big?

Write a sentence or two, then compare it with a good answer in the lesson.

Practice what you just read

  • 2 code examples you can edit and run
  • a practice exercise that checks your code
  • a 5-question quiz
  • 6 review cards for spaced repetition

Create a free account

Try a quiz question

Which statistic can be merged exactly from per-partition summaries?

  1. The variance, from each count, mean and M2
  2. The median, from each median
  3. The distinct count, from each distinct count
  4. The mode, from each mode

Answer it at the end of the lesson to see if you're right, and why.

Before this lesson