How to calculate probabilities with SciPy distributions

Probability distribution objects in scipy.stats turn a named statistical model into density values, cumulative probabilities, quantiles, and simulated observations. Python analysis code can use the same object to check how unusual a value is, find a cutoff, or generate values from one parameterized model.

Freezing a distribution stores parameters once. For scipy.stats.norm, loc is the mean and scale is the standard deviation; other distributions can add shape parameters while still exposing the same common method names.

Continuous distributions need careful wording because pdf() is density at a point, not probability mass at exactly that value. Use cdf() for probability at or below a threshold, sf() for upper-tail probability, ppf() for inverse cumulative cutoffs, and rvs() with a NumPy generator when repeatable sample values matter.

Steps to calculate probabilities with SciPy distributions:

  1. Create a Python script named probability_distribution_demo.py.
    probability_distribution_demo.py
    import numpy as np
    from scipy.stats import norm
     
    np.set_printoptions(precision=2, suppress=True)
     
    scores = norm(loc=70, scale=8)
    lo, hi = scores.interval(0.90)
    rng = np.random.default_rng(20260625)
    sample = scores.rvs(size=5, random_state=rng)
    q = np.array([0.1, 0.5, 0.9])
     
    print(f"mean/std: {scores.mean():.1f} {scores.std():.1f}")
    print(f"pdf(75): {scores.pdf(75):.4f}")
    print(f"cdf(75): {scores.cdf(75):.4f}")
    print(f"sf(85): {scores.sf(85):.4f}")
    print(f"central 90%: {lo:.2f} to {hi:.2f}")
    print(f"ppf(0.90): {scores.ppf(0.90):.2f}")
    print("sample:", sample)
    print("cdf(ppf(q)):", np.round(scores.cdf(scores.ppf(q)), 1))

    norm(loc=70, scale=8) freezes a normal distribution, so each later method call uses the same mean and standard deviation.

  2. Run the script.
    $ python3 probability_distribution_demo.py
    mean/std: 70.0 8.0
    pdf(75): 0.0410
    cdf(75): 0.7340
    sf(85): 0.0304
    central 90%: 56.84 to 83.16
    ppf(0.90): 80.25
    sample: [78.71 79.82 84.9  63.33 77.79]
    cdf(ppf(q)): [0.1 0.5 0.9]
  3. Use pdf(75) as density at the selected score.

    A density can be greater than 1 for some continuous distributions. It is not the probability of exactly one value.

  4. Use cdf(75) and sf(85) for lower-tail and upper-tail probabilities.

    sf() is the survival function. It represents the probability above the threshold and can be more accurate than subtracting cdf() from 1 for extreme upper tails.

  5. Use ppf() or interval() to convert probability levels back to score cutoffs.

    ppf(0.90) returns the cutoff where 90 percent of the distribution is at or below that value. interval(0.90) returns equal-tail bounds around the median.

  6. Pass a NumPy generator to rvs() when sample output must repeat across runs.

    Use a fixed generator seed only for repeatable checks. Use an unseeded generator or a documented project seed policy for simulation work.

  7. Verify the inverse cumulative calculation before changing the distribution.
    q = np.array([0.1, 0.5, 0.9])
    scores.cdf(scores.ppf(q))

    Confirm cdf(ppf(q)) returns the original probability levels apart from normal floating-point rounding.

  8. Remove the demo script when it was only created for the check.
    $ rm probability_distribution_demo.py