Anscombe's Quartet and the Datasaurus Dozen: Why You Plot Your Data First

โฑ๏ธ 9 min read ๐Ÿ“Š Visualization

Anscombe's quartet is four small datasets, published by the statistician F. J. Anscombe in The American Statistician in 1973, that share the same means, variances, correlation, and least-squares line but look completely different when plotted: a noisy line, a curve, a line bent by one outlier, and a vertical stack plus one distant point. The Datasaurus Dozen (Matejka and Fitzmaurice, CHI 2017) makes the same point with 13 datasets, one of them a dinosaur. Both teach one thing: summary statistics throw away the shape of the data, so plot it before you trust a number or fit a model.

Quick answer: Anscombe's quartet (1973) is four datasets of 11 points each that all have mean x = 9, variance x = 11, mean y ≈ 7.50, variance y ≈ 4.12 to 4.13, Pearson correlation r ≈ 0.816, and the regression line y = 3 + 0.5x, yet their scatter plots show a linear trend, a smooth curve, a line pulled by one outlier, and a slope created by a single point. The Datasaurus Dozen (2017) repeats the trick with 13 datasets whose means, standard deviations, and correlation match to two decimal places. The lesson: identical statistics do not mean identical data, so always plot. In Python, seaborn.load_dataset("anscombe") loads the quartet.

What is Anscombe's quartet?

It is a teaching example Francis Anscombe built for his 1973 paper "Graphs in Statistical Analysis." Each of the four datasets, labeled I to IV, has 11 (x, y) pairs. Datasets I, II, and III use the same x values (4 through 14); dataset IV puts ten points at x = 8 and one at x = 19. His argument was that analysts should "make both calculations and graphs. Both sorts of output should be studied; each will contribute to understanding." How he constructed the numbers is not known.

What summary statistics do the four datasets share?

All four agree on the count, both means, both variances, the correlation, and the fitted regression line. The x statistics are identical exactly; the y statistics differ only in the third decimal place. Variances below are sample variances (divided by n − 1), which is what pandas and most statistics software report by default.

StatisticValue in all four datasets
Number of points (n)11
Mean of x9 (exact)
Sample variance of x11 (exact)
Mean of y7.50
Sample variance of y4.12 to 4.13
Standard deviation of y2.03
Pearson correlation r0.816 (0.817 for dataset IV at three decimals)
Least-squares liney = 3.00 + 0.500x
R²0.67
Standard error of the slope0.118

Shown only this table, most analysts would conclude that all four datasets describe the same moderately strong linear relationship. That is true of dataset I and misleading for the other three.

How are the four datasets in Anscombe's quartet different?

Plotted, they tell four different stories, and only one of them fits the straight-line model the shared statistics imply. Robust and rank-based statistics start to separate them: the medians and Spearman rank correlations below differ even though the means and Pearson r do not.

DatasetWhat the scatter plot showsMedian ySpearman ρWhat the straight-line fit gets wrong
IA noisy linear cloud7.580.82Nothing serious; a linear model is reasonable here
IIA smooth arch that a quadratic fits almost exactly (R² above 0.9999)8.140.69Misses the curvature; residuals are negative at both ends and positive in the middle
IIITen points on a near-perfect line plus one outlier at (13, 12.74)7.110.99The outlier tilts the line; without it the fit is y ≈ 4.01 + 0.345x with r above 0.9999
IVTen points stacked at x = 8 and one point at (19, 12.50)7.040.50One point sets the entire slope; remove it and x has no variance, so no slope can be estimated

Dataset IV is the one that catches careful analysts. In simple regression, a point's leverage is h = 1/n + (x − x̄)² / Σ(x − x̄)², and for the point at x = 19 that is 1/11 + 100/110 = 1. With leverage 1, the fitted line is forced through the point, so its residual is exactly 0 and a residual plot shows nothing unusual there. Cook's distance divides by (1 − h)², so for that point it is undefined rather than large. Dataset III's outlier is the easy case by comparison: its residual is 3.24 and its Cook's distance is about 1.39, far above any other point in that set.

How do I reproduce Anscombe's quartet in Python with seaborn?

Seaborn ships the quartet as a built-in example dataset in long format: a dataset column with the values I to IV, plus x and y. load_dataset downloads it from the seaborn-data repository on GitHub the first time you call it, so it needs internet access once, then reads from a local cache. This computes the shared statistics for each dataset:

import numpy as np
import pandas as pd
import seaborn as sns

df = sns.load_dataset("anscombe")   # columns: dataset (I-IV), x, y

rows = []
for name, g in df.groupby("dataset"):
    slope, intercept = np.polyfit(g["x"], g["y"], 1)
    rows.append({
        "dataset": name,
        "mean_x": g["x"].mean(), "var_x": g["x"].var(),
        "mean_y": g["y"].mean(), "var_y": g["y"].var(),
        "r": g["x"].corr(g["y"]),
        "intercept": intercept, "slope": slope,
    })
print(pd.DataFrame(rows).set_index("dataset").round(3))

Output:

         mean_x  var_x  mean_y  var_y      r  intercept  slope
dataset
I           9.0   11.0   7.501  4.127  0.816      3.000    0.5
II          9.0   11.0   7.501  4.128  0.816      3.001    0.5
III         9.0   11.0   7.500  4.123  0.816      3.002    0.5
IV          9.0   11.0   7.501  4.123  0.817      3.002    0.5

Now plot it. lmplot draws a scatter plot with a fitted regression line in each panel, and the panels share axes by default, which is what makes the comparison fair:

sns.lmplot(data=df, x="x", y="y", col="dataset", col_wrap=2,
           ci=None, height=3, scatter_kws={"s": 40})

The four panels show exactly what the comparison table describes: the same fitted line drawn through a linear cloud, an arch, a line with one outlier, and a vertical stack joined to one point. To see the statistics that do separate the sets, compare medians and Spearman correlations. Pearson on ranks is Spearman's ρ, and pandas' default average ranking handles the tied x values in dataset IV:

check = pd.DataFrame({
    "median_y": df.groupby("dataset")["y"].median(),
    "spearman": [grp["x"].rank().corr(grp["y"].rank())
                 for _, grp in df.groupby("dataset")],
})
print(check.round(2))
         median_y  spearman
dataset
I            7.58      0.82
II           8.14      0.69
III          7.11      0.99
IV           7.04      0.50

In R, the quartet is built in as anscombe in the base datasets package, in wide format (x1 to x4, y1 to y4). For more on faceted layouts like the one above, see small multiples charts.

What is the Datasaurus Dozen?

The Datasaurus Dozen is a set of 13 datasets of 142 points each that share the same x mean, y mean, x and y standard deviations, and Pearson correlation to two decimal places: mean x = 54.26, mean y = 47.83, SD x = 16.76, SD y = 26.93, r = −0.06. It accompanies Justin Matejka and George Fitzmaurice's CHI 2017 paper "Same Stats, Different Graphs: Generating Datasets with Varied Appearance and Identical Statistics through Simulated Annealing." The starting point was the Datasaurus, a dinosaur-shaped scatter plot Alberto Cairo created in 2016, which the authors morphed toward 12 target shapes, including a star, a circle, a bullseye, an X, horizontal and vertical lines, and diagonal slants.

The method is what makes it more than a curiosity. Starting from an existing dataset, they repeatedly move a random point by a small amount, keep the move only if the statistics still match to two decimal places, and favor moves that bring points closer to the target shape. Simulated annealing accepts some moves away from the target early on so the process does not get stuck. The technique does not depend on which statistics are held fixed, and the same paper uses it to produce six different distributions that draw an identical boxplot.

Anscombe's quartetDatasaurus Dozen
Published1973, The American Statistician2017, ACM CHI conference
Created byF. J. AnscombeJustin Matejka and George Fitzmaurice, starting from Alberto Cairo's Datasaurus
Datasets413 (the dinosaur plus 12 shapes)
Points per dataset11142
Statistics held equalMeans, variances, correlation, regression lineMeans, standard deviations, Pearson correlation (to two decimals)
How it was madeNot knownSimulated annealing from the dinosaur toward each target shape
Where to get itseaborn load_dataset("anscombe"); R anscombeR datasauRus package; not included in seaborn

How do I plot the Datasaurus Dozen in Python or R?

Seaborn does not include it, so load it from the R datasauRus package, which provides a long-format datasaurus_dozen data frame with dataset, x, and y columns. In Python, read the same data from the tab-separated file in that package's GitHub repository:

import numpy as np
import pandas as pd
import seaborn as sns

url = ("https://raw.githubusercontent.com/jumpingrivers/datasauRus/"
       "main/inst/extdata/DatasaurusDozen-Long.tsv")
dd = pd.read_csv(url, sep="\t")   # columns: dataset, x, y

stats = dd.groupby("dataset").agg(
    mean_x=("x", "mean"), mean_y=("y", "mean"),
    sd_x=("x", "std"), sd_y=("y", "std"),
)
stats["r"] = [grp["x"].corr(grp["y"]) for _, grp in dd.groupby("dataset")]
print(np.trunc(stats * 100) / 100)   # all 13 rows: 54.26, 47.83, 16.76, 26.93, -0.06

sns.relplot(data=dd, x="x", y="y", col="dataset", col_wrap=4,
            height=2.5, s=10)

Note the truncation. The 13 sets agree to within 0.01 on every statistic, but they do not all round to the same value: with .round(2), some rows show 54.27 or −0.07. Truncating to two decimals, as above, reproduces the published figures for all 13. In R:

install.packages("datasauRus")
library(datasauRus)
library(ggplot2)

ggplot(datasaurus_dozen, aes(x = x, y = y)) +
  geom_point(size = 0.8) +
  facet_wrap(~dataset, ncol = 4)

What do Anscombe's quartet and the Datasaurus teach about exploratory data analysis?

Summary statistics are a lossy compression of the data. Many very different datasets map to the same handful of numbers, so a statistic can confirm what a plot shows but cannot replace it. In practice, that becomes a short checklist to run before you report any correlation or fit any regression:

What are the common mistakes when applying Anscombe's lesson?

The quartet is easy to cite and easy to misapply. These are the errors that show up most often in real analyses:

Pro Tip: Make a leverage check routine before you report a regression, not only a residual check. With statsmodels (import statsmodels.formula.api as smf) and iv = df[df["dataset"] == "IV"], smf.ols("y ~ x", data=iv).fit().get_influence().hat_matrix_diag returns 0.1 for ten points and 1.0 for the point at x = 19. Any leverage near 1 means one observation is deciding the slope on its own, and no residual plot will warn you.

โ† Back to Visualization Tips