statistics.kde_random: Sample from a KDE

Published on: October 13, 2026
Reading time: 5 minutes
Data visualization and probability distribution for Python statistics.kde_random

statistics.kde_random generates random values from a distribution estimated with kernel density estimation (KDE). Instead of assuming that data follows a normal, uniform, or exponential distribution, you can start from a real sample and build a smooth approximation of its observed density. This is useful for simulation, testing, synthetic data generation, exploratory analysis, and statistical prototyping.

The function complements statistics.kde. You can estimate a density from the same sample and create a callable that produces new values with a similar shape. The result, however, depends on the quality of the data, the number of observations, and especially the bandwidth.

How kernel density estimation works

A KDE places a small curve, called a kernel, around each observation. Those curves are added together to form a continuous density. The bandwidth controls how far each observation influences nearby values. A bandwidth that is too small creates a noisy density with many isolated peaks. A bandwidth that is too large can erase asymmetry, gaps, or multiple groups.

KDE does not create new information. It smooths the information already present in the sample. Outliers, sampling bias, and missing regions can therefore influence the generated values.

Basic example

from statistics import kde, kde_random

data = [1.2, 1.4, 1.5, 1.8, 2.0, 2.1, 2.2, 2.7, 3.0]
h = 0.25

density = kde(data, h=h)
generate = kde_random(data, h=h, seed=42)

samples = [generate() for _ in range(10)]
print(samples)
print(density(2.0))

The callable returned by kde_random can be invoked repeatedly. A fixed seed makes the sequence reproducible, which is helpful in documentation, automated tests, and controlled comparisons.

Choosing the bandwidth

The bandwidth, usually named h, is the most important modeling decision. There is no universal value. A practical strategy is to compare several bandwidths and inspect how they change the density and the statistics of generated samples.

Imagine a sample with two well-separated clusters. A very large bandwidth may merge them into one broad mound. A very small bandwidth may turn every observation into an individual spike. When variables use different scales, normalize or standardize them before estimating the density.

from statistics import kde_random, mean, median

base = [10, 11, 11, 12, 13, 20, 21, 22]

for h in (0.3, 1.0, 3.0):
    generate = kde_random(base, h=h, seed=7)
    simulated = [generate() for _ in range(5000)]
    print(h, mean(simulated), median(simulated), min(simulated), max(simulated))

This comparison helps reveal excessive smoothing and unexpected dispersion. Histograms, empirical cumulative distributions, and density plots are useful complements when visualization is available.

Reproducibility with seed

In data science, testing, and benchmarking, exact repeatability may be essential. The seed argument avoids accidental dependence on unrelated random operations. Use fixed seeds in tests and examples, but rotate or omit them in simulations that need independent scenarios.

Reproducibility is not security. This generator is not suitable for passwords, session tokens, reset links, cryptographic keys, or any security-sensitive workflow. Use secrets for those cases.

Validating input data

Before creating the generator, check that the sample is not empty, that all values are finite, and that the observations use compatible units. A model built from invalid or mixed data can produce numbers that look plausible but are statistically meaningless.

from math import isfinite
from statistics import kde_random


def build_generator(values, h, seed=None):
    data = [float(value) for value in values]
    if not data:
        raise ValueError("the sample cannot be empty")
    if h <= 0:
        raise ValueError("h must be positive")
    if not all(isfinite(value) for value in data):
        raise ValueError("the sample contains non-finite values")
    return kde_random(data, h=h, seed=seed)

Also consider sample size. A function may operate with only a few observations, but the resulting density will rarely represent a complex population reliably.

Generating synthetic data

One practical use is producing test data that approximately preserves the shape of an original sample without copying every record directly. This can be useful in prototypes and load tests, but it does not guarantee privacy. With a narrow bandwidth, generated values may be very close to original observations.

generate_latency = build_generator(
    [120, 135, 128, 160, 142, 150, 131],
    h=8,
    seed=123,
)
synthetic_latencies = [round(generate_latency(), 2) for _ in range(1000)]

After generation, compare more than the mean. Check median, standard deviation, quantiles, tails, and the proportion of values outside acceptable business limits.

Respecting the natural domain

Ordinary KDEs may generate values outside the natural domain. Durations can become negative, probabilities can exceed one, and ages can fall below zero. You can reject invalid values, transform data before fitting, or choose a model designed for a bounded domain.

def sample_nonnegative(generate, attempts=1000):
    for _ in range(attempts):
        value = generate()
        if value >= 0:
            return value
    raise RuntimeError("unable to obtain a valid value")

Rejection sampling is easy to understand, but it can become inefficient when a large portion of the estimated density lies outside the allowed range. For probabilities, a logit transformation can be more appropriate.

Automated testing

Statistical tests should not rely only on an exact sequence unless the seed and implementation are intentionally fixed. Prefer testing properties: output count, finite values, reasonable limits, approximate mean, and stable behavior.

def test_kde_samples():
    generate = build_generator([1, 2, 3, 4, 5], h=0.5, seed=10)
    values = [generate() for _ in range(2000)]
    assert len(values) == 2000
    assert all(isfinite(value) for value in values)
    assert 2.5 < sum(values) / len(values) < 3.5

Avoid extremely narrow tolerances. Internal implementation changes can alter a sequence without making the distribution incorrect.

Performance and production use

For hundreds or a few thousand observations, the standard-library approach is often convenient. For very large datasets or massive generation, benchmark the workflow. Both model construction and sampling can cost more than drawing from a simple parametric distribution.

In production, record the Python version, bandwidth, sample size, seed policy, preprocessing steps, and source dataset version when traceability matters. Construct the generator once and reuse it instead of rebuilding it for every generated value.

Common mistakes

A common mistake is selecting a bandwidth only because the first graph looks smooth. Another is using KDE to hide poor data quality. A third is treating synthetic values as anonymous by default. KDE can also be inappropriate when several variables have strong dependencies, because this API models a univariate distribution.

Document why the chosen bandwidth is acceptable, validate generated values against domain rules, and compare multiple diagnostics. If the data is multimodal, inspect whether those modes remain visible after smoothing.

When to use it

Use statistics.kde_random when you have one-dimensional numeric data, want a nonparametric shape, and prefer a standard-library solution. Avoid it when the domain has hard boundaries, the sample is tiny, multiple variables must preserve correlation, or the project requires formal privacy guarantees.

For related Python foundations, see Academify guides on priority queues with heapq, graphlib, safe ZIP archives, and thread communication with queue. External references include the official statistics documentation and the official random documentation.

Conclusion

statistics.kde_random provides a direct way to sample from a density learned from observations. The API is compact, but responsible use requires explicit decisions about bandwidth, data quality, domain constraints, reproducibility, validation, and privacy. With those controls in place, it becomes a valuable tool for simulation, testing, and exploratory statistical work without an external dependency.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Linux workspace representing namespaces and Python os.setns automation
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    os.setns: Enter Linux Namespaces Safely

    Learn Python os.setns for entering Linux namespaces with safe permissions, validation, testing, and cleanup.

    Ler mais

    Tempo de leitura: 5 minutos
    13/10/2026
    Computer with code representing file copying with Python Path.copy
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Path.copy: Copy Files and Directories Safely

    Learn Python Path.copy to copy files and directories with explicit safety, consistency, performance, and compatibility.

    Ler mais

    Tempo de leitura: 5 minutos
    12/10/2026
    Code on a monitor representing native memory access with Python ctypes.memoryview_at
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    ctypes.memoryview_at: Zero-Copy Native Memory

    Learn Python ctypes.memoryview_at for zero-copy native memory access, pointer validation, ownership, and safer C integration.

    Ler mais

    Tempo de leitura: 6 minutos
    12/10/2026
    Python code on screen representing EnumDict and advanced enumerations
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    EnumDict: Build Custom Enums with Validation

    Learn Python EnumDict for custom EnumType metaclasses, member validation, and safer advanced enumerations.

    Ler mais

    Tempo de leitura: 4 minutos
    11/10/2026
    Server rack representing connection load balancing with Python SO_REUSEPORT_LB
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: Balance Connections Across Workers

    Learn Python SO_REUSEPORT_LB to distribute connections across multiple workers with safe testing, portability, and graceful shutdown.

    Ler mais

    Tempo de leitura: 5 minutos
    11/10/2026
    Python asynchronous code on a laptop for inspect.markcoroutinefunction
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: Detect Async Wrappers

    Learn inspect.markcoroutinefunction in Python to identify async wrappers, integrate frameworks, and avoid incorrect coroutine detection.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026