SKELLAM

Overview

The SKELLAM function computes values for the Skellam distribution, a discrete probability distribution that describes the difference between two independent Poisson-distributed random variables. Named after the British statistician John Gordon Skellam, who first described it in 1946, this distribution is useful for modeling the difference in counts from two independent Poisson processes.

The Skellam distribution arises naturally when comparing two count-based phenomena. Given two independent Poisson random variables N_1 and N_2 with expected values \mu_1 and \mu_2 respectively, the difference K = N_1 - N_2 follows a Skellam distribution. This makes it applicable to scenarios such as analyzing the point spread in sports like soccer, hockey, or baseball where both teams score discrete points, as well as comparing photon counts in imaging applications.

The probability mass function (PMF) is given by:

p(k; \mu_1, \mu_2) = e^{-(\mu_1 + \mu_2)} \left(\frac{\mu_1}{\mu_2}\right)^{k/2} I_{|k|}(2\sqrt{\mu_1 \mu_2})

where I_k(z) is the modified Bessel function of the first kind. Unlike many discrete distributions that only take non-negative values, the Skellam distribution has support on all integers (positive, negative, and zero), reflecting the possibility that either Poisson variable could be larger.

The distribution has mean \mu_1 - \mu_2 and variance \mu_1 + \mu_2. When \mu_1 = \mu_2, the distribution is symmetric around zero. Both parameters \mu_1 and \mu_2 must be strictly positive.

This implementation uses SciPy’s scipy.stats.skellam module, which provides methods for the PMF, CDF, survival function, inverse CDF (percent point function), and statistical measures including mean, variance, and standard deviation. The optional loc parameter shifts the distribution along the integer axis.

This example function is provided as-is without any representation of accuracy.

Excel Usage

=SKELLAM(k, mu_one, mu_two, skellam_mode, loc)
  • k (float, required): Value at which to evaluate the distribution (or probability for ICDF/ISF modes). Can be a scalar or 2D list.
  • mu_one (float, required): Mean of the first Poisson variable (must be greater than 0).
  • mu_two (float, required): Mean of the second Poisson variable (must be greater than 0).
  • skellam_mode (str, optional, default: “pmf”): Output type for the distribution calculation.
  • loc (float, optional, default: 0): Location parameter that shifts the distribution.

Returns (float): Distribution result (float), or error message string.

Examples

Example 1: PMF at k=2

Inputs:

k mu_one mu_two skellam_mode loc
2 5 3 pmf 0

Excel formula:

=SKELLAM(2, 5, 3, "pmf", 0)

Expected output:

0.1431

Example 2: CDF at k=2

Inputs:

k mu_one mu_two skellam_mode loc
2 5 3 cdf 0

Excel formula:

=SKELLAM(2, 5, 3, "cdf", 0)

Expected output:

0.5776

Example 3: SF at k=2

Inputs:

k mu_one mu_two skellam_mode loc
2 5 3 sf 0

Excel formula:

=SKELLAM(2, 5, 3, "sf", 0)

Expected output:

0.4224

Example 4: ICDF at probability 0.5

Inputs:

k mu_one mu_two skellam_mode loc
0.5 5 3 icdf 0

Excel formula:

=SKELLAM(0.5, 5, 3, "icdf", 0)

Expected output:

2

Example 5: Mean of distribution

Inputs:

k mu_one mu_two skellam_mode loc
0 5 3 mean 0

Excel formula:

=SKELLAM(0, 5, 3, "mean", 0)

Expected output:

2

Python Code

from scipy.stats import skellam as scipy_skellam

def skellam(k, mu_one, mu_two, skellam_mode='pmf', loc=0):
    """
    Compute Skellam distribution values using scipy.stats.skellam.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.skellam.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        k (float): Value at which to evaluate the distribution (or probability for ICDF/ISF modes). Can be a scalar or 2D list.
        mu_one (float): Mean of the first Poisson variable (must be greater than 0).
        mu_two (float): Mean of the second Poisson variable (must be greater than 0).
        skellam_mode (str, optional): Output type for the distribution calculation. Valid options: PMF, CDF, SF, ICDF, ISF, Mean, Variance, Std Dev, Median. Default is 'pmf'.
        loc (float, optional): Location parameter that shifts the distribution. Default is 0.

    Returns:
        float: Distribution result (float), or error message string.
    """
    # Validate mu_one, mu_two
    try:
        mu_one_val = float(mu_one)
        mu_two_val = float(mu_two)
    except (TypeError, ValueError):
        return "Invalid input: mu_one and mu_two must be numbers."

    if not (mu_one_val > 0 and mu_two_val > 0):
        return "Invalid input: mu_one and mu_two must be > 0."

    # Validate loc
    try:
        loc_val = float(loc)
    except (TypeError, ValueError):
        return "Invalid input: loc must be a number."
    # Validate skellam_mode
    valid_modes = {"pmf", "cdf", "sf", "icdf", "isf", "mean", "var", "std", "median"}
    if not isinstance(skellam_mode, str) or skellam_mode not in valid_modes:
        return f"Invalid input: skellam_mode must be one of {sorted(valid_modes)}."
    # Helper to process k (scalar or 2D list)
    def process_k(val):
        try:
            return float(val)
        except (TypeError, ValueError):
            return None

    # Helper to validate probability range for ICDF/ISF modes
    def validate_probability(val, mode):
        if mode in ["icdf", "isf"]:
            if val < 0 or val > 1:
                return f"Invalid input: k must be between 0 and 1 for {mode} mode."
        return None

    # Handle statistics
    if skellam_mode == "mean":
        return float(scipy_skellam.mean(mu_one_val, mu_two_val, loc=loc_val))
    if skellam_mode == "var":
        return float(scipy_skellam.var(mu_one_val, mu_two_val, loc=loc_val))
    if skellam_mode == "std":
        return float(scipy_skellam.std(mu_one_val, mu_two_val, loc=loc_val))
    if skellam_mode == "median":
        return float(scipy_skellam.median(mu_one_val, mu_two_val, loc=loc_val))

    # PMF, CDF, SF, ICDF, ISF
    def compute(val):
        kval = process_k(val)
        if kval is None:
            return "Invalid input: k must be a number."

        # Validate probability range for ICDF/ISF modes
        prob_error = validate_probability(kval, skellam_mode)
        if prob_error:
            return prob_error

        if skellam_mode == "pmf":
            return float(scipy_skellam.pmf(kval, mu_one_val, mu_two_val, loc=loc_val))
        elif skellam_mode == "cdf":
            return float(scipy_skellam.cdf(kval, mu_one_val, mu_two_val, loc=loc_val))
        elif skellam_mode == "sf":
            return float(scipy_skellam.sf(kval, mu_one_val, mu_two_val, loc=loc_val))
        elif skellam_mode == "icdf":
            return float(scipy_skellam.ppf(kval, mu_one_val, mu_two_val, loc=loc_val))
        elif skellam_mode == "isf":
            return float(scipy_skellam.isf(kval, mu_one_val, mu_two_val, loc=loc_val))
    # 2D list or scalar
    if isinstance(k, list):
        if not all(isinstance(row, list) for row in k):
            return "Invalid input: k must be a scalar or 2D list."
        result = []
        for row in k:
            result_row = []
            for val in row:
                out = compute(val)
                if isinstance(out, str):
                    return out
                result_row.append(out)
            result.append(result_row)
        return result
    else:
        return compute(k)

Online Calculator