Numerosity Reduction in Data Mining
A dataset with millions of rows is slower to store, slower to scan, and slower to mine than a smaller one — but simply deleting most of the data would throw away the very information a mining algorithm needs. Data reduction is the general goal of shrinking a dataset's size while preserving enough of its meaning and structure that analysis run on the reduced version still gives trustworthy results. It splits into two distinct strategies depending on what gets reduced: dimensionality reduction shrinks the number of attributes (covered with verified examples in the lesson on feature transformation), while numerosity reduction — this lesson's subject — shrinks the number of records, replacing the raw data with a smaller model or summary that stands in for it.
The Core Idea: Replace Data With a Representation of Data
Numerosity reduction does not simply throw away rows at random. Instead, it replaces the raw dataset with something smaller that still captures what later analysis needs — a mathematical model, a set of summary statistics, or a carefully chosen smaller sample. This distinction — model or summary, not just "less data" — is what separates numerosity reduction from careless data loss, and it splits into two families depending on whether that replacement assumes a specific mathematical form for the data.
Parametric Methods: Storing a Model Instead of the Data
Parametric methods assume the data follows a particular mathematical form, and once that assumption is made, the entire dataset can be replaced by just the small number of parameters that define the model. Regression is the clearest example: fitting a line y = wx + b to a scatter of points means the two numbers w and b can stand in for every point in the dataset, because any predicted value can be reconstructed directly from the model.
import numpy as np
n = 200
x = np.random.uniform(0, 100, n)
y = 3.2*x + 15 + np.random.normal(0, 8, n)
slope, intercept = np.polyfit(x, y, 1)
print(f"raw data: {n} points x 2 values = {n*2} numbers stored")
print(f"regression model: slope={slope:.3f}, intercept={intercept:.3f} -> just 2 numbers")
Output:
raw data: 200 points x 2 values = 400 numbers stored
regression model: slope=3.193, intercept=15.335 -> just 2 numbers
400 numbers compress down to 2. That reduction is not free, though: the model only approximates the original data, and the gap between the model's predictions and the real values (a residual standard deviation of 7.91 in this data) is exactly the detail that got discarded in exchange for the massive size reduction. This is the fundamental trade-off behind every numerosity reduction technique: a smaller representation always costs some fidelity, and the right amount to give up depends on how much precision the downstream analysis actually needs.
Log-linear models extend the same idea to several variables at once, estimating the probability of different combinations of discrete values using a compact set of parameters rather than storing every observed combination directly. This is especially useful for high-dimensional categorical data, where the raw number of possible attribute combinations can be enormous even if the actual patterns in the data are simple.
Non-Parametric Methods: Summarizing Without Assuming a Model
Non-parametric methods make no assumption about the data's underlying mathematical shape. Instead, they reduce the data by grouping or summarizing it directly, which tends to be more flexible and broadly applicable than a parametric model, at the cost of not compressing quite as aggressively.
Histograms
A histogram divides a variable's range into bins and stores only the count of values falling into each bin, discarding the exact value of every individual observation. On 1,000 values drawn from a right-skewed distribution:
data = np.random.exponential(scale=20, size=1000)
counts, bin_edges = np.histogram(data, bins=10)
bin_mids = (bin_edges[:-1] + bin_edges[1:]) / 2
approx_mean = np.sum(counts * bin_mids) / counts.sum()
print("true mean from raw data:", round(data.mean(), 3))
print("approx mean recovered from histogram bins:", round(approx_mean, 3))
Output:
true mean from raw data: 19.756
approx mean recovered from histogram bins: 20.632
The histogram — just 10 bin counts plus 11 bin edges, 21 numbers total in place of 1,000 raw values — recovers a mean within about 4% of the true value. That small gap is the direct, measurable cost of binning: every value inside a bin is treated as if it sat exactly at the bin's midpoint, which is only ever an approximation.
Clustering
Clustering reduces data by grouping similar records together and, when used for numerosity reduction specifically, representing each cluster by a small number of summary values (such as its centroid) rather than every individual member. Cluster quality for this purpose is judged by measures like diameter (the distance between the two farthest points in a cluster — smaller means the cluster's members are genuinely similar) and centroid distance (how far a typical point sits from the cluster's center). This is the same underlying technique covered in depth, with fully worked distance calculations, in the dedicated lessons on clustering elsewhere in this series — applied here specifically as a way to shrink a dataset rather than only to discover natural groupings in it.
Sampling
Sampling reduces data by analyzing a smaller subset instead of the full dataset, trading completeness for size. The trade-off is not that a sample is simply "less accurate" — it's that accuracy improves predictably with sample size, in a way worth seeing directly. Drawing progressively larger samples from a population of 100,000 simulated transaction amounts (true mean: 499.98):
for sample_size in [10, 100, 1000, 10000]:
sample = rng.choice(population, size=sample_size, replace=False)
print(sample_size, sample.mean())
Output:
sample size 10: sample mean = 491.39 (error = 8.59)
sample size 100: sample mean = 486.56 (error = 13.42)
sample size 1000: sample mean = 499.69 (error = 0.30)
sample size 10000: sample mean = 499.89 (error = 0.09)
Notice that the error does not shrink perfectly smoothly — the size-100 sample happened to land slightly farther from the true mean than the size-10 sample did, purely by chance. What genuinely shrinks in a predictable, mathematical way is the standard error — the expected size of that gap — computed as the population's standard deviation divided by the square root of the sample size:
n= 10 theoretical standard error = 38.02
n= 100 theoretical standard error = 12.02
n= 1000 theoretical standard error = 3.80
n= 10000 theoretical standard error = 1.20
This is the correct way to reason about sampling: any single sample can land closer or farther from the truth by luck, but the typical distance shrinks reliably as the sample size grows, following a square-root relationship — quadrupling the sample size roughly halves the expected error. The standard variants — simple random sampling (with or without replacement), cluster sampling (sampling whole pre-existing groups rather than individual records), and stratified sampling (sampling proportionally from each subgroup to guarantee every subgroup is represented) — all share this underlying trade-off, differing only in how the subset is chosen.
Data Cube Aggregation
Data cube aggregation reduces a large, detailed transaction table into a much smaller summary table by grouping and summing along chosen dimensions — exactly the operation that underlies a data warehouse's reporting layer. Aggregating 5,000 individual retail transactions by region and month collapses them into a table with one row per combination:
cube = transactions.groupby(["region", "month"])["sales"].sum().reset_index()
print(len(transactions), "->", len(cube), "rows")
Output:
5000 -> 12 rows
Five thousand individual transactions reduce to just 12 rows (4 regions × 3 months) — a massive size reduction for any analysis that only needs totals by region and month, though it permanently discards the ability to inspect any individual transaction once the aggregation is done. This is exactly the trade every numerosity technique makes: less data to process, in exchange for detail that has to be judged genuinely unnecessary for the analysis at hand before it is thrown away.
Data Compression
Data compression reduces storage size by encoding data more efficiently, and comes in two forms with a critical difference. Lossless compression guarantees the original data can be perfectly reconstructed; lossy compression permanently discards some information in exchange for a smaller size. Applying standard lossless compression to a numeric array with a strongly repeating pattern demonstrates both the potential and the guarantee:
import zlib
raw_bytes = repetitive_data.tobytes()
compressed = zlib.compress(raw_bytes)
decompressed = zlib.decompress(compressed)
print("raw size:", len(raw_bytes), "bytes")
print("compressed size:", len(compressed), "bytes")
print("lossless check:", decompressed == raw_bytes)
Output:
raw size: 72000 bytes
compressed size: 172 bytes
lossless check: True
A 418-fold size reduction, with the decompressed data matching the original byte-for-byte. This particular result is a bit of a best case — it works this well specifically because the underlying data was constructed with a short, exactly repeating pattern; real-world data compresses less dramatically, but lossless compression's core guarantee (perfect reconstruction) holds regardless of how repetitive the data is. This is the one numerosity technique on this list that does not trade away any information at all — it only removes redundancy, which is a fundamentally different kind of reduction than the approximations every other technique on this list makes.
Numerosity Reduction vs. Dimensionality Reduction
It's worth being precise about the distinction these two names point to, since they are easy to conflate. Numerosity reduction reduces the number of records — fewer rows, achieved through models, summaries, or samples, as demonstrated throughout this lesson. Dimensionality reduction reduces the number of attributes — fewer columns, achieved through feature selection or mathematical transformations like the ones covered in the lesson on feature transformation. A real preprocessing pipeline often applies both: dimensionality reduction to drop columns that add no useful signal, and numerosity reduction to shrink the remaining table down to a size that can be processed efficiently — two separate levers on the same underlying goal of making a large dataset tractable without discarding what actually matters.