Large data and plotting performance

seaborn · Expert · 12 min

Default seaborn settings that are fine on a thousand rows can take minutes or produce unreadable blobs on a million.

Most seaborn slowness on big data comes from three sources.

Bootstrapped error bars. Each aggregated point resamples its group n_boot times. With many groups and millions of rows this dominates. Use errorbar=None, errorbar="se", or pre-aggregate with pandas and plot the summary with errorbar=None.

Per-point artists. scatterplot, stripplot and especially swarmplot draw every observation. Swarm placement is quadratic and becomes unusable beyond a few thousand points per category. Alternatives:

  • 2D histograms: sns.histplot(data=df, x=..., y=..., bins=50) or Matplotlib's hexbin;
  • distribution summaries: boxenplot (designed for large n) or violinplot;
  • a random sample: df.sample(5_000, random_state=0) for the scatter layer, while statistics use all rows.

KDE on huge arrays. kdeplot evaluates a kernel per point on a grid; histograms with many bins are much faster and show the same shape at that scale.

Also, high-cardinality hue (hundreds of levels) builds a huge legend and many artists; aggregate or filter first. Converting string columns to pandas category dtype speeds up grouping and fixes their order.

Profile with a timer before and after: often one argument, such as errorbar=None, turns a minute-long plot into a second.

Example

import matplotlib
matplotlib.use("Agg")
import time
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

rng = np.random.default_rng(10)
n = 200_000
df = pd.DataFrame({"x": rng.integers(0, 20, n), "g": rng.choice(list("abcd"), n)})
df["y"] = df.x * 0.5 + rng.normal(0, 3, n)

def timed(**kw):
    fig, ax = plt.subplots()
    t = time.perf_counter()
    sns.lineplot(data=df, x="x", y="y", hue="g", ax=ax, **kw)
    plt.close(fig)
    return time.perf_counter() - t

slow, fast = timed(n_boot=200), timed(errorbar=None)
print(fast < slow)                    # True: skipping the bootstrap is much faster

ax = sns.histplot(data=df, x="x", y="y", bins=(20, 40), cmap="mako")  # not a 200k scatter
ax.figure.savefig("big.png")

Remember

  • Bootstraps dominate cost: use errorbar=None or 'se' on big data
  • Replace large scatters and swarms with 2D histograms, boxen plots or samples
  • Profile one change at a time; often one argument fixes the speed

Watch out for

  • Using swarmplot on tens of thousands of points, which is extremely slow and overflows the category width.

Check yourself

  1. A lineplot over 5 million rows takes two minutes. Which change most likely helps most?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: seaborn.pydata.org/tutorial/error_bars.html