Discretize Distributions.jl
A Julia package for converting continuous and discrete probability distributions into discrete representations with interval-based support using IntervalArithmetic.jl
.
The package provides functions to discretize univariate distributions into DiscreteNonParametric
distributions where the support consists of IntervalArithmetic.Interval
objects. Each interval [a, b]
represents a probability mass over that range, computed using the cumulative distribution function (CDF) for continuous distributions or aggregated probability mass function (PMF) for discrete distributions.
Limitations
- Finite support: Infinite distributions are truncated using quantile bounds (default 0.1% and 99.9%)
- Discrete distribution quirks: Discretizing already-discrete distributions has some limitations and edge cases
- Non-integer discrete values: Discrete distributions with non-integer support may behave unexpectedly
Future Work
- Develop better warnings for incompatible distributions
- Support for multivariate distributions
API Overview
The package provides three main discretize
methods:
- Fixed intervals:
discretize(dist, interval_width)
- Creates uniform intervals of specified width - Custom boundaries:
discretize(dist, boundaries)
- Uses custom interval boundaries - Pre-constructed intervals:
discretize(dist, intervals)
- Uses pre-builtInterval
objects
All methods return a DiscreteNonParametric
distribution with IntervalArithmetic.Interval
support.
Working with Results
using Distributions, DiscretizeDistributions, IntervalArithmetic
# Discretize a normal distribution
normal_dist = Normal(0, 1)
interval_dist = discretize(normal_dist, 0.5)
# The result has interval support
support(interval_dist) # Vector of Interval{Float64} objects
probs(interval_dist) # Corresponding probabilities
# Convert to point-based distributions
left_aligned = left_align_distribution(interval_dist) # Use left endpoints
centered = centred_distribution(interval_dist) # Use midpoints
right_aligned = right_align_distribution(interval_dist) # Use right endpoints
Mathematical Details
Continuous Distributions
For continuous distributions, discretisation computes probability masses using the cumulative distribution function (CDF):
\[P(X' ∈ [a_i, a_{i+1})) = F(a_{i+1}) - F(a_i)\]
where F(x)
is the CDF of the continuous distribution X
.
Discrete Distributions
For discrete distributions, probability masses are aggregated over intervals using the probability mass function (PMF):
\[P(X' ∈ [a_i, a_{i+1})) = ∑_{k=⌈a_i⌉}^{⌊a_{i+1}⌋-1} P(X = k) + (P(X = ⌊a_i⌋) × (⌈a_i⌉ - a_i)) + (P(X = ⌊a_{i+1}⌋) × (a_{i+1} - ⌊a_{i+1}⌋))\]
All resulting discrete distributions are normalized to ensure probabilities sum to 1.
Advanced Usage
Handling Unbounded Distributions
For distributions with infinite support, control truncation with quantile bounds:
# Normal distribution - unbounded in both directions
normal_dist = Normal(0, 1)
discrete_normal = discretize(normal_dist, 0.2; min_quantile=0.005, max_quantile=0.995)
# Exponential distribution - unbounded above
exp_dist = Exponential(1.0)
discrete_exp = discretize(exp_dist, 0.1; max_quantile=0.99)
# Result includes infinite tail intervals
support(discrete_exp) # [..., interval(4.5, 5.0), interval(5.0, ∞)]
Custom Interval Structures
Create non-uniform discretisations with custom boundaries:
# Fine resolution near zero, coarser elsewhere
custom_boundaries = [-5.0, -2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, 5.0]
discrete_custom = discretize(Normal(0, 1), custom_boundaries)
# Results in intervals: [(-∞,-5], [-5,-2], [-2,-1], ..., [5,∞)]
length(support(discrete_custom)) # 10 intervals (8 from boundaries + 2 infinite tails)
Working with Pre-constructed Intervals
For advanced use cases, you can provide pre-constructed IntervalArithmetic.Interval
objects:
using IntervalArithmetic
# Create custom intervals with specific properties
intervals = [
interval(-2.0, -1.0), # Standard interval
interval(-1.0, 0.0), # Adjacent interval
interval(0.0, 2.0), # Wider interval
interval(2.0, Inf) # Semi-infinite interval
]
# Discretize using these intervals
normal_dist = Normal(0, 1)
discrete_custom = discretize(normal_dist, intervals)
DiscretizeDistributions.discretize
— Functiondiscretize(dist::Distributions.UnivariateDistribution, interval::Real;
min_quantile=0.001, max_quantile=0.999)
Discretize a univariate distribution into an interval-based discrete distribution using fixed intervals.
This function converts a univariate distribution into a discrete one by dividing the distribution's support into intervals of fixed width and computing the probability mass in each interval. The resulting distribution has IntervalArithmetic.Interval
objects as support points.
Arguments
dist::Distributions.UnivariateDistribution
: The distribution to discretize (continuous or discrete)interval::Real
: The width of each discretisation intervalmin_quantile=0.001
: Lower quantile bound for unbounded distributionsmax_quantile=0.999
: Upper quantile bound for unbounded distributions
Returns
DiscreteNonParametric{Interval{T}, ...}
: Discrete distribution with interval support
Details
For bounded distributions, the natural bounds are used. For unbounded distributions, the bounds are determined using the specified quantiles. The probability mass in each interval is computed using the CDF for continuous distributions or a pseudo-CDF for discrete distributions.
Examples
using Distributions, DiscretizeDistributions, IntervalArithmetic
# Discretize a normal distribution with interval width 0.5
normal_dist = Normal(0, 1)
discrete_intervals = discretize(normal_dist, 0.5)
# Returns distribution with support like [interval(-∞, -3.5), interval(-3.5, -3.0), ...]
# Convert to point-based distributions if needed
left_aligned = left_align_distribution(discrete_intervals) # [-3.5, -3.0, -2.5, ...]
centered = centred_distribution(discrete_intervals) # [-3.25, -2.75, -2.25, ...]
# Discretize a discrete distribution
poisson_dist = Poisson(3.0)
discrete_poisson = discretize(poisson_dist, 2)
discretize(dist::Distributions.UnivariateDistribution, interval::AbstractVector)
Discretize a univariate distribution using custom interval boundaries.
This function converts a univariate distribution into a discrete one using user-specified interval boundaries. The resulting distribution has IntervalArithmetic.Interval
objects as support points representing the probability mass in each interval.
Arguments
dist::Distributions.UnivariateDistribution
: The distribution to discretizeinterval::AbstractVector
: Vector of interval boundaries (will be sorted automatically)
Returns
DiscreteNonParametric{Interval{T}, ...}
: Discrete distribution with interval support
Details
The input interval vector is automatically sorted and combined with distribution bounds. Probability masses are computed using the CDF for continuous distributions or pseudo-CDF for discrete distributions. The resulting distribution represents probability masses over intervals [a_i, a_{i+1})
.
Examples
using Distributions, DiscretizeDistributions, IntervalArithmetic
# Discretize using custom intervals
normal_dist = Normal(5, 2)
custom_intervals = [0.0, 2.0, 4.0, 6.0, 8.0, 10.0]
discrete_intervals = discretize(normal_dist, custom_intervals)
# Support: [interval(-∞, 0.0), interval(0.0, 2.0), ..., interval(10.0, ∞)]
# Convert to different alignments
left_points = left_align_distribution(discrete_intervals)
centered_points = centred_distribution(discrete_intervals)
right_points = right_align_distribution(discrete_intervals)
# Discrete distribution with custom intervals
poisson_dist = Poisson(3.0)
discrete_poisson = discretize(poisson_dist, [0.5, 2, 4, 6, 8, 10])
discretize(dist::Distributions.UnivariateDistribution, interval::AbstractVector{IntervalArithmetic.Interval{X}}) where X <: Real
Discretize a univariate distribution using pre-constructed interval objects.
This function converts a univariate distribution into a discrete one using user-specified IntervalArithmetic.Interval
objects. This is the core discretization method that all other discretize
methods ultimately call. The resulting distribution has the same interval objects as support points with computed probability masses.
Arguments
dist::Distributions.UnivariateDistribution
: The distribution to discretizeinterval::AbstractVector{IntervalArithmetic.Interval{X}}
: Vector of pre-constructed intervals
Returns
DiscreteNonParametric{Interval{X}, ...}
: Discrete distribution with interval support
Details
This method computes probability masses directly using the interval boundaries. For each interval [a, b]
, the probability is computed as cdf(dist, b) - cdf(dist, a)
. The resulting probabilities are normalized to sum to 1.
Examples
using Distributions, DiscretizeDistributions, IntervalArithmetic
# Create intervals manually
intervals = [interval(-1.0, 0.0), interval(0.0, 1.0), interval(1.0, 2.0)]
# Discretize using these intervals
normal_dist = Normal(0, 1)
discrete_intervals = discretize(normal_dist, intervals)
# Each interval gets probability mass according to the normal distribution
DiscretizeDistributions.left_align_distribution
— Functionleft_align_distribution(dist::Distributions.DiscreteNonParametric{IntervalArithmetic.Interval{T}, ...})
Convert an interval-based discrete distribution to a left-aligned point-based distribution.
This function takes a discrete distribution with interval support and creates a new distribution where each support point is positioned at the left endpoint (infimum) of the corresponding interval. Infinite intervals are automatically removed before conversion.
Arguments
dist::DiscreteNonParametric{Interval{T}, ...}
: Input discrete distribution with interval support
Returns
DiscreteNonParametric{T, ...}
: New distribution with left-aligned point support
Examples
using Distributions, DiscretizeDistributions, IntervalArithmetic
# Create an interval-based distribution
intervals = [interval(0.0, 1.0), interval(1.0, 2.0), interval(2.0, 3.0)]
probs = [0.3, 0.4, 0.3]
interval_dist = DiscreteNonParametric(intervals, probs, check_args=false)
# Convert to left-aligned points
left_aligned = left_align_distribution(interval_dist)
# Support becomes [0.0, 1.0, 2.0] (left endpoints of intervals)
DiscretizeDistributions.centred_distribution
— Functioncentred_distribution(dist::Distributions.DiscreteNonParametric{IntervalArithmetic.Interval{T}, ...})
Convert an interval-based discrete distribution to a centered point-based distribution.
This function takes a discrete distribution with interval support and creates a new distribution where each support point is positioned at the center (midpoint) of the corresponding interval. Infinite intervals are automatically removed before conversion.
Arguments
dist::DiscreteNonParametric{Interval{T}, ...}
: Input discrete distribution with interval support
Returns
DiscreteNonParametric{T, ...}
: New distribution with centered point support
Examples
using Distributions, DiscretizeDistributions, IntervalArithmetic
# Create an interval-based distribution
intervals = [interval(0.0, 1.0), interval(1.0, 2.0), interval(2.0, 3.0)]
probs = [0.3, 0.4, 0.3]
interval_dist = DiscreteNonParametric(intervals, probs, check_args=false)
# Convert to centered points
centered = centred_distribution(interval_dist)
# Support becomes [0.5, 1.5, 2.5] (midpoints of intervals)
DiscretizeDistributions.right_align_distribution
— Functionright_align_distribution(dist::Distributions.DiscreteNonParametric{IntervalArithmetic.Interval{T}, ...})
Convert an interval-based discrete distribution to a right-aligned point-based distribution.
This function takes a discrete distribution with interval support and creates a new distribution where each support point is positioned at the right endpoint (supremum) of the corresponding interval. Infinite intervals are automatically removed before conversion.
Arguments
dist::DiscreteNonParametric{Interval{T}, ...}
: Input discrete distribution with interval support
Returns
DiscreteNonParametric{T, ...}
: New distribution with right-aligned point support
Examples
using Distributions, DiscretizeDistributions, IntervalArithmetic
# Create an interval-based distribution
intervals = [interval(0.0, 1.0), interval(1.0, 2.0), interval(2.0, 3.0)]
probs = [0.3, 0.4, 0.3]
interval_dist = DiscreteNonParametric(intervals, probs, check_args=false)
# Convert to right-aligned points
right_aligned = right_align_distribution(interval_dist)
# Support becomes [1.0, 2.0, 3.0] (right endpoints of intervals)