Skip to content

Commit

Permalink
Generalize LocationScale to discrete distributions (#1286)
Browse files Browse the repository at this point in the history
* Move `locationscale.jl`

* Fix `logcdf` and `logccdf` for DiscreteNonParametric

* Generalize `LocationScale` to discrete distributions
  • Loading branch information
devmotion authored May 2, 2021
1 parent a36c613 commit 5604316
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 132 deletions.
96 changes: 0 additions & 96 deletions src/univariate/continuous/locationscale.jl

This file was deleted.

10 changes: 10 additions & 0 deletions src/univariate/discrete/discretenonparametric.jl
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ end
ccdf(d::DiscreteNonParametric{T}, x::Integer) where T = _ccdf(d, convert(T, x))
ccdf(d::DiscreteNonParametric{T}, x::Real) where T = _ccdf(d, convert(T, x))

# fix incorrect defaults
for f in (:cdf, :ccdf)
_f = Symbol(:_, f)
logf = Symbol(:log, f)
@eval begin
$logf(d::DiscreteNonParametric{T}, x::Integer) where T = log($_f(d, convert(T, x)))
$logf(d::DiscreteNonParametric{T}, x::Real) where T = log($_f(d, convert(T, x)))
end
end

function quantile(d::DiscreteNonParametric, q::Real)
0 <= q <= 1 || throw(DomainError())
x = support(d)
Expand Down
132 changes: 132 additions & 0 deletions src/univariate/locationscale.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""
LocationScale(μ,σ,ρ)
A location-scale transformed distribution with location parameter `μ`,
scale parameter `σ`, and given univariate distribution `ρ`.
If ``Z`` is a random variable with distribution `ρ`, then the distribution of the random
variable
```math
X = μ + σ Z
```
is the location-scale transformed distribution with location parameter `μ` and scale
parameter `σ`.
If `ρ` is a discrete distribution, the probability mass function of
the transformed distribution is given by
```math
P(X = x) = P\\left(Z = \\frac{x-μ}{σ} \\right).
```
If `ρ` is a continuous distribution, the probability density function of
the transformed distribution is given by
```math
f(x) = \\frac{1}{σ} ρ \\! \\left( \\frac{x-μ}{σ} \\right).
```
```julia
LocationScale(μ,σ,ρ) # location-scale transformed distribution
params(d) # Get the parameters, i.e. (μ, σ, and the base distribution)
location(d) # Get the location parameter
scale(d) # Get the scale parameter
```
External links
[Location-Scale family on Wikipedia](https://en.wikipedia.org/wiki/Location%E2%80%93scale_family)
"""
struct LocationScale{T<:Real, S<:ValueSupport, D<:UnivariateDistribution{S}} <: UnivariateDistribution{S}
μ::T
σ::T
ρ::D
function LocationScale{T,S,D}::T, σ::T, ρ::D; check_args=true) where {T<:Real, S<:ValueSupport, D<:UnivariateDistribution{S}}
check_args && @check_args(LocationScale, σ > zero(σ))
new{T, S, D}(μ, σ, ρ)
end
end

function LocationScale::T, σ::T, ρ::UnivariateDistribution; check_args=true) where {T<:Real}
_T = promote_type(eltype(ρ), T)
D = typeof(ρ)
S = value_support(D)
return LocationScale{_T,S,D}(_T(μ), _T(σ), ρ; check_args=check_args)
end

LocationScale::Real, σ::Real, ρ::UnivariateDistribution) = LocationScale(promote(μ, σ)..., ρ)

# aliases
const ContinuousLocationScale{T<:Real,D<:ContinuousUnivariateDistribution} = LocationScale{T,Continuous,D}
const DiscreteLocationScale{T<:Real,D<:DiscreteUnivariateDistribution} = LocationScale{T,Discrete,D}

Base.eltype(::Type{<:LocationScale{T}}) where T = T

minimum(d::LocationScale) = d.μ + d.σ * minimum(d.ρ)
maximum(d::LocationScale) = d.μ + d.σ * maximum(d.ρ)

LocationScale::Real, σ::Real, d::LocationScale) = LocationScale+ d.μ * σ, σ * d.σ, d.ρ)

#### Conversions

convert(::Type{LocationScale{T}}, μ::Real, σ::Real, ρ::D) where {T<:Real, D<:UnivariateDistribution} = LocationScale(T(μ),T(σ),ρ)
convert(::Type{LocationScale{T}}, d::LocationScale{S}) where {T<:Real, S<:Real} = LocationScale(T(d.μ),T(d.σ),d.ρ, check_args=false)

#### Parameters

location(d::LocationScale) = d.μ
scale(d::LocationScale) = d.σ
params(d::LocationScale) = (d.μ,d.σ,d.ρ)
partype(::LocationScale{T}) where {T} = T

#### Statistics

mean(d::LocationScale) = d.μ + d.σ * mean(d.ρ)
median(d::LocationScale) = d.μ + d.σ * median(d.ρ)
mode(d::LocationScale) = d.μ + d.σ * mode(d.ρ)
modes(d::LocationScale) = d.μ .+ d.σ .* modes(d.ρ)

var(d::LocationScale) = d.σ^2 * var(d.ρ)
std(d::LocationScale) = d.σ * std(d.ρ)
skewness(d::LocationScale) = skewness(d.ρ)
kurtosis(d::LocationScale) = kurtosis(d.ρ)

isplatykurtic(d::LocationScale) = isplatykurtic(d.ρ)
isleptokurtic(d::LocationScale) = isleptokurtic(d.ρ)
ismesokurtic(d::LocationScale) = ismesokurtic(d.ρ)

entropy(d::ContinuousLocationScale) = entropy(d.ρ) + log(d.σ)
entropy(d::DiscreteLocationScale) = entropy(d.ρ)

mgf(d::LocationScale,t::Real) = exp(d.μ*t) * mgf(d.ρ,d.σ*t)

#### Evaluation & Sampling

pdf(d::ContinuousLocationScale,x::Real) = pdf(d.ρ,(x-d.μ)/d.σ) / d.σ
pdf(d::DiscreteLocationScale, x::Real) = pdf(d.ρ,(x-d.μ)/d.σ)

logpdf(d::ContinuousLocationScale,x::Real) = logpdf(d.ρ,(x-d.μ)/d.σ) - log(d.σ)
logpdf(d::DiscreteLocationScale, x::Real) = logpdf(d.ρ,(x-d.μ)/d.σ)

# additional definitions are required to fix ambiguity errors and incorrect defaults
for f in (:cdf, :ccdf, :logcdf, :logccdf)
_f = Symbol(:_, f)
@eval begin
$f(d::LocationScale, x::Real) = $_f(d, x)
$f(d::DiscreteLocationScale, x::Real) = $_f(d, x)
$f(d::DiscreteLocationScale, x::Integer) = $_f(d, x)
$_f(d::LocationScale, x::Real) = $f(d.ρ, (x - d.μ) / d.σ)
end
end

quantile(d::LocationScale,q::Real) = d.μ + d.σ * quantile(d.ρ,q)

rand(rng::AbstractRNG, d::LocationScale) = d.μ + d.σ * rand(rng, d.ρ)
cf(d::LocationScale, t::Real) = cf(d.ρ,t*d.σ) * exp(1im*t*d.μ)
gradlogpdf(d::ContinuousLocationScale, x::Real) = gradlogpdf(d.ρ,(x-d.μ)/d.σ) / d.σ

#### Syntactic sugar for simple transforms of distributions, e.g., d + x, d - x, and so on

Base.:+(d::UnivariateDistribution, x::Real) = LocationScale(x, one(x), d)
Base.:+(x::Real, d::UnivariateDistribution) = d + x
Base.:*(x::Real, d::UnivariateDistribution) = LocationScale(zero(x), x, d)
Base.:*(d::UnivariateDistribution, x::Real) = x * d
Base.:-(d::UnivariateDistribution, x::Real) = d + -x
Base.:/(d::UnivariateDistribution, x::Real) = inv(x) * d

3 changes: 2 additions & 1 deletion src/univariates.jl
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,6 @@ const continuous_distributions = [
"ksonesided",
"laplace",
"levy",
"locationscale",
"logistic",
"noncentralbeta",
"noncentralchisq",
Expand All @@ -631,6 +630,8 @@ const continuous_distributions = [
"weibull"
]

include(joinpath("univariate", "locationscale.jl"))

for dname in discrete_distributions
include(joinpath("univariate", "discrete", "$(dname).jl"))
end
Expand Down
Loading

0 comments on commit 5604316

Please sign in to comment.