From d5e6682c153d8433a19350d24e66c6370c3c06f7 Mon Sep 17 00:00:00 2001 From: Thomas Wutzler Date: Tue, 24 Oct 2023 16:12:20 +0200 Subject: [PATCH] format according to SciMLStyle --- .JuliaFormatter.toml | 1 + docs/make.jl | 32 ++-- ext/DistributionFitsOptimExt.jl | 8 +- src/DistributionFits.jl | 29 ++-- src/fitstats.jl | 160 +++++++++++-------- src/optimizer.jl | 10 +- src/univariate/continuous/estimateMoments.jl | 50 +++--- src/univariate/continuous/exponential.jl | 52 +++--- src/univariate/continuous/gamma.jl | 22 ++- src/univariate/continuous/laplace.jl | 25 ++- src/univariate/continuous/logitnormal.jl | 112 +++++++------ src/univariate/continuous/lognormal.jl | 44 +++-- src/univariate/continuous/normal.jl | 29 ++-- src/univariate/continuous/weibull.jl | 9 +- src/univariates.jl | 44 ++--- test/fitstats.jl | 91 +++++------ test/runtests.jl | 8 +- test/testutils.jl | 4 +- test/univariate/continuous/exponential.jl | 12 +- test/univariate/continuous/gamma.jl | 14 +- test/univariate/continuous/laplace.jl | 7 +- test/univariate/continuous/logitnormal.jl | 80 +++++----- test/univariate/continuous/lognormal.jl | 14 +- test/univariate/continuous/normal.jl | 7 +- test/univariate/continuous/weibull.jl | 7 +- test/univariate/test_univariate.jl | 65 ++++---- 26 files changed, 484 insertions(+), 452 deletions(-) create mode 100644 .JuliaFormatter.toml diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml new file mode 100644 index 0000000..580b751 --- /dev/null +++ b/.JuliaFormatter.toml @@ -0,0 +1 @@ +style = "sciml" diff --git a/docs/make.jl b/docs/make.jl index 0f5bc5c..cd6cde8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,20 +6,22 @@ using StatsPlots, Statistics # used in help # https://juliadocs.github.io/Documenter.jl/stable/man/syntax/#@example-block ENV["GKSwstype"] = "100" -DocMeta.setdocmeta!(DistributionFits, :DocTestSetup, :(using DistributionFits); recursive=true) +DocMeta.setdocmeta!(DistributionFits, + :DocTestSetup, + :(using DistributionFits); + recursive = true) makedocs(; - modules=[DistributionFits], - authors="Thomas Wutzler and contributors", + modules = [DistributionFits], + authors = "Thomas Wutzler and contributors", #repo="https://github.com/bgctw/DistributionFits.jl/blob/{commit}{path}#{line}", - repo = Remotes.GitHub("bgctw", "DistributionFits.jl"), - sitename="DistributionFits.jl", - format=Documenter.HTML(; - prettyurls=get(ENV, "CI", "false") == "true", - canonical="https://bgctw.github.io/DistributionFits.jl", - assets=String[], - ), - pages=[ + repo = Remotes.GitHub("bgctw", "DistributionFits.jl"), + sitename = "DistributionFits.jl", + format = Documenter.HTML(; + prettyurls = get(ENV, "CI", "false") == "true", + canonical = "https://bgctw.github.io/DistributionFits.jl", + assets = String[],), + pages = [ "Home" => "index.md", "Parameter type" => "partype.md", "Distributions" => [ @@ -31,10 +33,8 @@ makedocs(; "Dependencies" => "set_optimize.md", "API" => "api.md", #"Details" => "z_autodocs.md", - ], -) + ],) deploydocs(; - repo="github.com/bgctw/DistributionFits.jl", - devbranch="main", -) + repo = "github.com/bgctw/DistributionFits.jl", + devbranch = "main",) diff --git a/ext/DistributionFitsOptimExt.jl b/ext/DistributionFitsOptimExt.jl index 84c1217..78faf74 100644 --- a/ext/DistributionFitsOptimExt.jl +++ b/ext/DistributionFitsOptimExt.jl @@ -3,11 +3,11 @@ module DistributionFitsOptimExt isdefined(Base, :get_extension) ? (using Optim) : (using ..Optim) using DistributionFits: DistributionFits, optimize, AbstractDistributionFitOptimizer -struct OptimOptimizer <: AbstractDistributionFitOptimizer; end +struct OptimOptimizer <: AbstractDistributionFitOptimizer end -function DistributionFits.optimize(f, ::OptimOptimizer, lower, upper) +function DistributionFits.optimize(f, ::OptimOptimizer, lower, upper) result = Optim.optimize(f, lower, upper) - (;minimizer = result.minimizer, converged = result.converged, result) + (; minimizer = result.minimizer, converged = result.converged, result) end function __init__() @@ -16,4 +16,4 @@ function __init__() DistributionFits.set_optimizer(OptimOptimizer()) end -end \ No newline at end of file +end diff --git a/src/DistributionFits.jl b/src/DistributionFits.jl index 8d4b67f..066fd27 100644 --- a/src/DistributionFits.jl +++ b/src/DistributionFits.jl @@ -8,7 +8,7 @@ using StatsFuns: logit, logistic, normcdf #using Infiltrator if !isdefined(Base, :get_extension) - using Requires + using Requires end # for extension @@ -17,16 +17,16 @@ import Distributions: mean, var, mode import StatsAPI: fit # Moments also extends getindex, mean, kurtorsis .... -export - # fitting distributions - AbstractMoments, Moments, n_moments, moments, - QuantilePoint, - fit_mean_quantile, fit_mode_quantile, fit_median_quantile, - @qp, @qp_ll, @qp_l, @qp_m, @qp_u, @qp_uu, - @qs_cf90, @qs_cf95, - qp, qp_ll, qp_l, qp_m, qp_u, qp_uu, - qs_cf90, qs_cf95, - fit_mean_relerror +export + # fitting distributions + AbstractMoments, Moments, n_moments, moments, + QuantilePoint, + fit_mean_quantile, fit_mode_quantile, fit_median_quantile, + @qp, @qp_ll, @qp_l, @qp_m, @qp_u, @qp_uu, + @qs_cf90, @qs_cf95, + qp, qp_ll, qp_l, qp_m, qp_u, qp_uu, + qs_cf90, qs_cf95, + fit_mean_relerror # document but do not export - need to qualify by 'DistributionFits.' # export @@ -44,11 +44,10 @@ export AbstractDistributionFitOptimizer, optimize include("optimizer.jl") - function __init__() - @static if !isdefined(Base, :get_extension) - @require Optim="429524aa-4258-5aef-a3af-852621145aeb" include("../ext/DistributionFitsOptimExt.jl") - end + @static if !isdefined(Base, :get_extension) + @require Optim="429524aa-4258-5aef-a3af-852621145aeb" include("../ext/DistributionFitsOptimExt.jl") + end end # fitting distributions to stats diff --git a/src/fitstats.jl b/src/fitstats.jl index a0a12b2..5b05c60 100644 --- a/src/fitstats.jl +++ b/src/fitstats.jl @@ -30,29 +30,40 @@ kurtosis(m) # throws error because its above 2nd moment ``` """ abstract type AbstractMoments{N} end -n_moments(::Type{<:AbstractMoments{N}}) where N = N -n_moments(m::AbstractMoments{N}) where N = N -Distributions.mean(m::AbstractMoments) = n_moments(m) >= 1 ? m[1] : +n_moments(::Type{<:AbstractMoments{N}}) where {N} = N +n_moments(m::AbstractMoments{N}) where {N} = N +function Distributions.mean(m::AbstractMoments) + n_moments(m) >= 1 ? m[1] : error("mean not recorded") -Distributions.var(m::AbstractMoments) = n_moments(m) >= 2 ? m[2] : +end +function Distributions.var(m::AbstractMoments) + n_moments(m) >= 2 ? m[2] : error("variance not recorded") -Distributions.std(m::AbstractMoments) = n_moments(m) >= 2 ? sqrt(m[2]) : +end +function Distributions.std(m::AbstractMoments) + n_moments(m) >= 2 ? sqrt(m[2]) : error("std not recorded") -Distributions.skewness(m::AbstractMoments) = n_moments(m) >= 3 ? m[3] : +end +function Distributions.skewness(m::AbstractMoments) + n_moments(m) >= 3 ? m[3] : error("skewness not recorded") -Distributions.kurtosis(m::AbstractMoments) = n_moments(m) >= 4 ? m[4] : +end +function Distributions.kurtosis(m::AbstractMoments) + n_moments(m) >= 4 ? m[4] : error("kurtosis not recorded") +end - -struct Moments{N,T} <: AbstractMoments{N} - all::SVector{N,T} +struct Moments{N, T} <: AbstractMoments{N} + all::SVector{N, T} end Moments(x...) = Moments(SVector{length(x)}(promote(x...))) Moments() = Moments(SA[]) -Base.getindex(m::Moments, i) = n_moments(m) >= i ? m.all[i] : +function Base.getindex(m::Moments, i) + n_moments(m) >= i ? m.all[i] : error("$(i)th moment not recorded.") +end Base.convert(::Type{AbstractArray}, m::Moments) = m.all -Base.eltype(::Moments{N,T}) where {N,T} = T +Base.eltype(::Moments{N, T}) where {N, T} = T """ moments(D, ::Val{N} = Val(2)) @@ -67,15 +78,15 @@ moments(LogNormal(), Val(4)) # first four moments moments(Normal()) # mean and variance ``` """ -function moments(d::Distribution, ::Val{N} = Val(2)) where N +function moments(d::Distribution, ::Val{N} = Val(2)) where {N} N isa Integer || error("N must be a positive Integer") - N > 4 && error("Getting moments above 4 not yet implemented for " * - "distribution $(typeof(d)).") - N == 4 && return(Moments(mean(d), var(d), skewness(d), kurtosis(d))) - N == 3 && return(Moments(mean(d), var(d), skewness(d))) - N == 2 && return(Moments(mean(d), var(d))) - N == 1 && return(Moments(mean(d))) - N == 0 && return(Moments()) + N > 4 && error("Getting moments above 4 not yet implemented for " * + "distribution $(typeof(d)).") + N == 4 && return (Moments(mean(d), var(d), skewness(d), kurtosis(d))) + N == 3 && return (Moments(mean(d), var(d), skewness(d))) + N == 2 && return (Moments(mean(d), var(d))) + N == 1 && return (Moments(mean(d))) + N == 0 && return (Moments()) error("N must be a positive Integer.") end @@ -113,9 +124,9 @@ plot(d, label = "lognormal", ylab="probability density") plot!(Normal(3,1.2), label = "normal") ``` """ -fit(::Type{D}, m::AbstractMoments) where {D<:Distribution} = +function fit(::Type{D}, m::AbstractMoments) where {D <: Distribution} error("fitting to moments not implemented for distribution of type $D") - +end """ QuantilePoint @@ -143,53 +154,68 @@ There are macros/functions for some commonly used sets of QuantilePoints: 90% an - `@qs_cf90(q0_05,q0_95)` - `@qs_cf95(q0_025,q0_975)` -> `Set([QuantilePoint(q0_025,0.025),QuantilePoint(q0_975,0.975)]))` """ -struct QuantilePoint{TQ,TP} +struct QuantilePoint{TQ, TP} q::TQ p::TP - QuantilePoint{TQ,TP}(q,p) where {TQ,TP} = 0 < p < 1 ? new(q,p) : + function QuantilePoint{TQ, TP}(q, p) where {TQ, TP} + 0 < p < 1 ? new(q, p) : error("p must be in (0,1)") + end end -QuantilePoint(q,p) = QuantilePoint{typeof(q),typeof(p)}(q,p) +QuantilePoint(q, p) = QuantilePoint{typeof(q), typeof(p)}(q, p) -QuantilePoint(qp::QuantilePoint; q = qp.q, p = qp.p) = QuantilePoint(q,p) +QuantilePoint(qp::QuantilePoint; q = qp.q, p = qp.p) = QuantilePoint(q, p) Base.show(io::IO, qp::QuantilePoint) = print(io, "QuantilePoint($(qp.q),$(qp.p))") -function Base.isless(x::QuantilePoint,y::QuantilePoint) +function Base.isless(x::QuantilePoint, y::QuantilePoint) is_equal_q = (x.q == y.q) ((x.p == y.p) && !is_equal_q) && error("incompatible: $x,$y") isless = (x.q < y.q) # for different p, q needs to be different (isless && (x.p > y.p)) && error("incompatible: $(x),$(y)") - (!isless && !is_equal_q && (x.p < y.p)) && error("incompatible: $x,$y") - return(isless) + (!isless && !is_equal_q && (x.p < y.p)) && error("incompatible: $x,$y") + return (isless) end -macro qp(q,p) :(QuantilePoint($(esc(q)), $(esc(p)))) end -macro qp_ll(q0_025) :(QuantilePoint($(esc(q0_025)),0.025)) end -macro qp_l(q0_05) :(QuantilePoint($(esc(q0_05)),0.05)) end -macro qp_m(median) :(QuantilePoint($(esc(median)),0.5)) end -macro qp_u(q0_95) :(QuantilePoint($(esc(q0_95)),0.95)) end -macro qp_uu(q0_975) :(QuantilePoint($(esc(q0_975)),0.975)) end +macro qp(q, p) + :(QuantilePoint($(esc(q)), $(esc(p)))) +end +macro qp_ll(q0_025) + :(QuantilePoint($(esc(q0_025)), 0.025)) +end +macro qp_l(q0_05) + :(QuantilePoint($(esc(q0_05)), 0.05)) +end +macro qp_m(median) + :(QuantilePoint($(esc(median)), 0.5)) +end +macro qp_u(q0_95) + :(QuantilePoint($(esc(q0_95)), 0.95)) +end +macro qp_uu(q0_975) + :(QuantilePoint($(esc(q0_975)), 0.975)) +end -macro qs_cf90(q0_05,q0_95) - :(Set([QuantilePoint($(esc(q0_05)),0.05),QuantilePoint($(esc(q0_95)),0.95)])) end -macro qs_cf95(q0_025,q0_975) - :(Set([QuantilePoint($(esc(q0_025)),0.025),QuantilePoint($(esc(q0_975)),0.975)])) end +macro qs_cf90(q0_05, q0_95) + :(Set([QuantilePoint($(esc(q0_05)), 0.05), QuantilePoint($(esc(q0_95)), 0.95)])) +end +macro qs_cf95(q0_025, q0_975) + :(Set([QuantilePoint($(esc(q0_025)), 0.025), QuantilePoint($(esc(q0_975)), 0.975)])) +end # The non-macro versions return percentile whose type matches that of the argument -qp_ll(q0_025::T) where T = QuantilePoint(q0_025, T(0.025)) -qp_l(q0_05::T) where T = QuantilePoint(q0_05, T(0.05)) -qp_m(median::T) where T = QuantilePoint(median, T(0.5)) -qp_u(q0_95::T) where T = QuantilePoint(q0_95, T(0.95)) -qp_uu(q0_975::T) where T = QuantilePoint(q0_975, T(0.975)) - -function qs_cf90(q0_05::T,q0_95::T) where T - Set([QuantilePoint(q0_05,T(0.05)),QuantilePoint(q0_95,T(0.95))]) +qp_ll(q0_025::T) where {T} = QuantilePoint(q0_025, T(0.025)) +qp_l(q0_05::T) where {T} = QuantilePoint(q0_05, T(0.05)) +qp_m(median::T) where {T} = QuantilePoint(median, T(0.5)) +qp_u(q0_95::T) where {T} = QuantilePoint(q0_95, T(0.95)) +qp_uu(q0_975::T) where {T} = QuantilePoint(q0_975, T(0.975)) + +function qs_cf90(q0_05::T, q0_95::T) where {T} + Set([QuantilePoint(q0_05, T(0.05)), QuantilePoint(q0_95, T(0.95))]) end -function qs_cf95(q0_025::T,q0_975::T) where T - Set([QuantilePoint(q0_025,T(0.025)),QuantilePoint(q0_975,T(0.975))]) +function qs_cf95(q0_025::T, q0_975::T) where {T} + Set([QuantilePoint(q0_025, T(0.025)), QuantilePoint(q0_975, T(0.975))]) end - """ fit(D, lower::QuantilePoint, upper::QuantilePoint) @@ -208,12 +234,12 @@ quantile.(d, [0.5, 0.975]) ≈ [3,5] true ``` """ -function fit(::Type{D}, lower::QuantilePoint, upper::QuantilePoint) where D<:Distribution +function fit(::Type{D}, + lower::QuantilePoint, + upper::QuantilePoint) where {D <: Distribution} error("fitting to two quantile points not implemented for distribution of type $D") end - - """ fit(D, val, qp, ::Val{stats} = Val(:mean)) @@ -240,25 +266,31 @@ d = fit(LogNormal, 5.0, @qp_uu(14), Val(:mode)); (true, true) ``` """ -function fit(::Type{D}, val, qp::QuantilePoint, ::Val{stats} = Val(:mean)) where {D<:Distribution, stats} - stats == :mean && return(fit_mean_quantile(D, val, qp)) - stats == :mode && return(fit_mode_quantile(D, val, qp)) - stats == :median && return(fit_median_quantile(D, val, qp)) +function fit(::Type{D}, + val, + qp::QuantilePoint, + ::Val{stats} = Val(:mean)) where {D <: Distribution, stats} + stats == :mean && return (fit_mean_quantile(D, val, qp)) + stats == :mode && return (fit_mode_quantile(D, val, qp)) + stats == :median && return (fit_median_quantile(D, val, qp)) error("unknown stats: $stats") end, -function fit_median_quantile(::Type{D}, median, qp::QuantilePoint) where {D <: Distribution} - return(fit(D, @qp_m(median), qp)) +function fit_median_quantile(::Type{D}, + median, + qp::QuantilePoint) where {D <: Distribution} + return (fit(D, @qp_m(median), qp)) end, -function fit_mean_quantile(::Type{D}, mean::Real, qp::QuantilePoint) where D<:Distribution +function fit_mean_quantile(::Type{D}, + mean::Real, + qp::QuantilePoint) where {D <: Distribution} error("fit_mean_quantile not yet implemented for Distribution of type: $D") end, -function fit_mode_quantile(::Type{D}, mode::Real, qp::QuantilePoint) where D<:Distribution +function fit_mode_quantile(::Type{D}, + mode::Real, + qp::QuantilePoint) where {D <: Distribution} error("fit_mode_quantile not yet implemented for Distribution of type: $D") end # function fit_mean_quantile(d::Type{D}, mean::Real, qp::QuantilePoint) where D<:Distribution # error("fit_mean_quantile not yet implemented for Distribution of type: $D") # end - - - diff --git a/src/optimizer.jl b/src/optimizer.jl index 1a52a38..12f5127 100644 --- a/src/optimizer.jl +++ b/src/optimizer.jl @@ -31,15 +31,15 @@ Developers implementing usage of a different specific optimizer see code in ext/DistributionFitsOptimExt. """ abstract type AbstractDistributionFitOptimizer end -struct NotSetOptimizer <: AbstractDistributionFitOptimizer; end +struct NotSetOptimizer <: AbstractDistributionFitOptimizer end # see to ext/DistributionFitsOptimExt.jl -optimize(f, ::NotSetOptimizer, lower, upper) = error( - "Optimizer not set yet. Either invoke 'using Optim' or 'DistributionFits.set_optimizer(...)'.") - +function optimize(f, ::NotSetOptimizer, lower, upper) + error("Optimizer not set yet. Either invoke 'using Optim' " * + "or 'DistributionFits.set_optimizer(...)'.") +end df_optimizer = NotSetOptimizer(); optimize(f, lower, upper) = optimize(f, df_optimizer, lower, upper) set_optimizer(opt::AbstractDistributionFitOptimizer) = (global df_optimizer = opt) - diff --git a/src/univariate/continuous/estimateMoments.jl b/src/univariate/continuous/estimateMoments.jl index 62cc6b7..cd61cee 100644 --- a/src/univariate/continuous/estimateMoments.jl +++ b/src/univariate/continuous/estimateMoments.jl @@ -1,15 +1,16 @@ # estimate the mean by numerical integration over uniform percentiles -estimateMean(d::ContinuousUnivariateDistribution; kwargs...) = - meanFunOfProb(d;kwargs...,fun=(d,p)->quantile(d,p)) +function estimateMean(d::ContinuousUnivariateDistribution; kwargs...) + meanFunOfProb(d; kwargs..., fun = (d, p) -> quantile(d, p)) +end # estimate variance by numerical integration over uniform percentiles -function estimateVariance(d::ContinuousUnivariateDistribution; mean=missing, kwargs...) +function estimateVariance(d::ContinuousUnivariateDistribution; mean = missing, kwargs...) m = ismissing(mean) ? Distributions.mean(d) : mean - function squaredDiff(d,p) + function squaredDiff(d, p) t = quantile(d, p) - m - t*t + t * t end - meanFunOfProb(d;kwargs...,fun=squaredDiff) + meanFunOfProb(d; kwargs..., fun = squaredDiff) end """ @@ -17,9 +18,12 @@ compute mean over a function(uniformly distributed probabilities) used to estimate moments of logitnorm """ -function meanFunOfProb(d::ContinuousUnivariateDistribution;relPrec = 1e-4, maxCnt=2^18, fun=(d,p)->quantile.(d,p) ) - δ = 1/32 # start with 31 points (32 intervals between 0 and 1) - p = δ:δ:(1-δ) +function meanFunOfProb(d::ContinuousUnivariateDistribution; + relPrec = 1e-4, + maxCnt = 2^18, + fun = (d, p) -> quantile.(d, p)) + δ = 1 / 32 # start with 31 points (32 intervals between 0 and 1) + p = δ:δ:(1 - δ) # for K=1/δ intervals, there are (K-1) points at c_i # The first points at δ represents interval (δ/2,3/2δ) # The following picture shows points and intervals for K = 4 @@ -28,28 +32,28 @@ function meanFunOfProb(d::ContinuousUnivariateDistribution;relPrec = 1e-4, maxCn # we need to add points for δ/4 and 1-δ/4 representing the edges # but their weight is only half, because they represents half an inverval #m = sum(c_i*δ) + el*(δ/2) + er*(δ/2) = (sum(c_i) + er/2 + el/2)*δ - s = sum(fun.(d,p)) # sum at points c_i - el = fun(d,δ/4) # - er = fun(d,1-δ/4) - m = (s + el/2 + er/2)*δ + s = sum(fun.(d, p)) # sum at points c_i + el = fun(d, δ / 4) # + er = fun(d, 1 - δ / 4) + m = (s + el / 2 + er / 2) * δ relErr = 1 - while 1/δ < maxCnt + while 1 / δ < maxCnt mPrev = m - δ = δ / 2 + δ = δ / 2 # to double the number of reference points, half the interval # for each second point we already computed fun # only need to add the new points to the sum of central points - p = δ:δ*2:(1-δ) # points at the center of current intervals - s += sum(fun.(d,p)) - el = fun(d,δ/4) - er = fun(d,1-δ/4) - m = (s + el/2 + er/2)*δ - relErr = abs(m - mPrev)/m + p = δ:(δ * 2):(1 - δ) # points at the center of current intervals + s += sum(fun.(d, p)) + el = fun(d, δ / 4) + er = fun(d, 1 - δ / 4) + m = (s + el / 2 + er / 2) * δ + relErr = abs(m - mPrev) / m #println("cnt=$(1/δ), m=$m, mPrev=$mPrev, relErr=$relErr") #if the estimate did not change much, can return relErr <= relPrec && break end - relErr > relPrec && @warn "Returning meanFunOfProb results of low relative precision of $relErr" + relErr > relPrec && + @warn "Returning meanFunOfProb results of low relative precision of $relErr" m end - diff --git a/src/univariate/continuous/exponential.jl b/src/univariate/continuous/exponential.jl index a76b965..f961a71 100644 --- a/src/univariate/continuous/exponential.jl +++ b/src/univariate/continuous/exponential.jl @@ -1,7 +1,6 @@ - fit(::Type{Exponential}, m::AbstractMoments) = fit(Exponential{eltype(m)}, m) -function fit(::Type{Exponential{T}}, m::AbstractMoments) where T +function fit(::Type{Exponential{T}}, m::AbstractMoments) where {T} # https://en.wikipedia.org/wiki/Exponential_distribution n_moments(m) >= 1 || error("Need mean to estimate exponential") return Exponential(T(mean(m))) @@ -10,60 +9,69 @@ end function fit(::Type{Exponential}, lower::QuantilePoint, upper::Missing) fit(Exponential{Float64}, lower, upper) end -function fit(::Type{Exponential{T}}, lower::QuantilePoint, upper::Missing) where T - θ_lower = -lower.q/log(1-lower.p) +function fit(::Type{Exponential{T}}, lower::QuantilePoint, upper::Missing) where {T} + θ_lower = -lower.q / log(1 - lower.p) Exponential(T(θ_lower)) end function fit(::Type{Exponential}, lower::Missing, upper::QuantilePoint) fit(Exponential{Float64}, lower, upper) end -function fit(::Type{Exponential{T}}, lower::Missing, upper::QuantilePoint) where T - θ_upper = -upper.q/log(1-upper.p) +function fit(::Type{Exponential{T}}, lower::Missing, upper::QuantilePoint) where {T} + θ_upper = -upper.q / log(1 - upper.p) Exponential(T(θ_upper)) end function fit(::Type{Exponential}, lower::QuantilePoint, upper::QuantilePoint) fit(Exponential{Float64}, lower, upper) end -function fit(dt::Type{Exponential{T}}, lower::QuantilePoint, upper::QuantilePoint) where T +function fit(dt::Type{Exponential{T}}, lower::QuantilePoint, upper::QuantilePoint) where {T} # return average for the two quantiles - ismissing(lower.q) && return(fit(dt, missing, upper)) - ismissing(upper.q) && return(fit(dt, lower, missing)) - θ_lower = -lower.q/log(1-lower.p) - θ_upper = -upper.q/log(1-upper.p) - θ_lower ≈ θ_upper || @warn("Averaging scale for lower and upper quantile " * - "for fitting expoenential distribution.") - θ = (θ_lower + θ_upper)/2 + ismissing(lower.q) && return (fit(dt, missing, upper)) + ismissing(upper.q) && return (fit(dt, lower, missing)) + θ_lower = -lower.q / log(1 - lower.p) + θ_upper = -upper.q / log(1 - upper.p) + θ_lower ≈ θ_upper || @warn("Averaging scale for lower and upper quantile "* + "for fitting expoenential distribution.") + θ = (θ_lower + θ_upper) / 2 Exponential(T(θ)) end -function fit_mean_quantile(::Type{Exponential}, mean::T, qp::QuantilePoint) where T <: Real +function fit_mean_quantile(::Type{Exponential}, + mean::T, + qp::QuantilePoint) where {T <: Real} fit_mean_quantile(Exponential{T}, mean, qp) end -function fit_mean_quantile(dt::Type{Exponential{T}}, mean::Real, qp::QuantilePoint) where T +function fit_mean_quantile(dt::Type{Exponential{T}}, + mean::Real, + qp::QuantilePoint) where {T} # only fit to mean @warn "ignoring upper quantile when fitting Exponential to mean." #exception=(e,catch_backtrace(),) fit_mean_quantile(dt, mean, missing) end -function fit_mean_quantile(::Type{Exponential}, mean::T, qp::Missing) where T <: Real +function fit_mean_quantile(::Type{Exponential}, mean::T, qp::Missing) where {T <: Real} fit(Exponential{T}, Moments(mean)) end -function fit_mean_quantile(::Type{Exponential{T}}, mean::Real, qp::Missing) where T <: Real +function fit_mean_quantile(::Type{Exponential{T}}, + mean::Real, + qp::Missing) where {T <: Real} fit(Exponential{T}, Moments(mean)) end -function fit_mode_quantile(::Type{<:Exponential}, mode::T, qp::QuantilePoint) where T <: Real +function fit_mode_quantile(::Type{<:Exponential}, + mode::T, + qp::QuantilePoint) where {T <: Real} # ignore mode (its always at 0) mode != zero(mode) && @warn("ignoring mode when fitting Exponential.") fit_mode_quantile(Exponential{T}, missing, qp) end - function fit_mode_quantile(::Type{Exponential}, mode::Missing, qp::QuantilePoint) fit_mode_quantile(Exponential{Float64}, mode, qp) end -function fit_mode_quantile(::Type{Exponential{T}}, mode::Missing, qp::QuantilePoint) where T <: Real - θ = -qp.q/log(1-qp.p) +function fit_mode_quantile(::Type{Exponential{T}}, + mode::Missing, + qp::QuantilePoint) where {T <: Real} + θ = -qp.q / log(1 - qp.p) Exponential(T(θ)) end diff --git a/src/univariate/continuous/gamma.jl b/src/univariate/continuous/gamma.jl index 93e6909..1922c67 100644 --- a/src/univariate/continuous/gamma.jl +++ b/src/univariate/continuous/gamma.jl @@ -8,27 +8,25 @@ function fit(::Type{Gamma}, lower::QuantilePoint, upper::QuantilePoint) # https://www.johndcook.com/quantiles_parameters.pdf # Shape-scale paras, shape α - cook:α - w:k, scale θ - cook:β - w:θ if upper.p < lower.p - lower,upper = upper,lower + lower, upper = upper, lower end - 0 < lower.p < upper.p < 1 || error( - "Expected 0 < lower.p < upper.p < 1, but got " * - "lower.p = $(lower.p) and upper.p = $(upper.p)") - 0 < lower.q < upper.q || error( - "Expected 0 < lower.q < upper.q, but got " * - "lower.q = $(lower.q) and upper.q = $(upper.q)") + 0 < lower.p < upper.p < 1 || error("Expected 0 < lower.p < upper.p < 1, but got " * + "lower.p = $(lower.p) and upper.p = $(upper.p)") + 0 < lower.q < upper.q || error("Expected 0 < lower.q < upper.q, but got " * + "lower.q = $(lower.q) and upper.q = $(upper.q)") logp1 = log(lower.p) logp2 = log(upper.p) - lhs = upper.q/lower.q + lhs = upper.q / lower.q #α = 1 fcost = (α) -> begin - gamma1 = Gamma(α,1) - rhs = invlogcdf(gamma1, logp2)/invlogcdf(gamma1, logp1) + gamma1 = Gamma(α, 1) + rhs = invlogcdf(gamma1, logp2) / invlogcdf(gamma1, logp1) abs2(rhs - lhs) end resOpt = optimize(fcost, 1e-2, 1e8) resOpt.converged || error("Could not fit distribution to quantiles. resOpt=$resOpt") α_opt = resOpt.minimizer - fbeta(α) = lower.q / invlogcdf(Gamma(α,1), log(lower.p)) + fbeta(α) = lower.q / invlogcdf(Gamma(α, 1), log(lower.p)) Gamma(α_opt, fbeta(α_opt)) end @@ -42,5 +40,3 @@ end # θ = -qp.q/log(1-qp.p) # Gamma(θ) # end - - diff --git a/src/univariate/continuous/laplace.jl b/src/univariate/continuous/laplace.jl index a9bd3c0..3135939 100644 --- a/src/univariate/continuous/laplace.jl +++ b/src/univariate/continuous/laplace.jl @@ -1,40 +1,39 @@ function fit(::Type{Laplace}, m::AbstractMoments) fit(Laplace{eltype(m)}, m) end -function fit(::Type{Laplace{T}}, m::AbstractMoments) where T +function fit(::Type{Laplace{T}}, m::AbstractMoments) where {T} # https://en.wikipedia.org/wiki/Laplace_distribution # θ corresponds to b n_moments(m) >= 2 || error("Need mean and variance to estimate Laplace distribution.") μ = mean(m) - θ = sqrt(var(m)/2) - Laplace(T(μ),T(θ)) + θ = sqrt(var(m) / 2) + Laplace(T(μ), T(θ)) end function fit(::Type{Laplace}, lower::QuantilePoint, upper::QuantilePoint) fit(Laplace{Float64}, lower, upper) end -function fit(::Type{Laplace{T}}, lower::QuantilePoint, upper::QuantilePoint) where T +function fit(::Type{Laplace{T}}, lower::QuantilePoint, upper::QuantilePoint) where {T} # q = F^-1(p) = μ - θ a(p) - a = (p) -> sign(p-0.5) * log(1-2*abs(p-0.5)) + a = (p) -> sign(p - 0.5) * log(1 - 2 * abs(p - 0.5)) a_lower, a_upper = a(lower.p), a(upper.p) - θ = (upper.q - lower.q)/(a_lower - a_upper) + θ = (upper.q - lower.q) / (a_lower - a_upper) μ = upper.q + θ * a_upper - Laplace(T(μ),T(θ)) + Laplace(T(μ), T(θ)) end -function fit_mode_quantile(::Type{Laplace}, mode::T, qp::QuantilePoint) where T<:Real +function fit_mode_quantile(::Type{Laplace}, mode::T, qp::QuantilePoint) where {T <: Real} fit_mode_quantile(Laplace{T}, mode, qp) end -function fit_mode_quantile(::Type{Laplace{T}}, mode::Real, qp::QuantilePoint) where T - a = (p) -> sign(p-0.5) * log(1-2*abs(p-0.5)) +function fit_mode_quantile(::Type{Laplace{T}}, mode::Real, qp::QuantilePoint) where {T} + a = (p) -> sign(p - 0.5) * log(1 - 2 * abs(p - 0.5)) a_qp = a(qp.p) #θ = (qp.q - mode)/(0 - a_qp) - θ = (mode - qp.q)/a_qp - Laplace(T(mode),T(θ)) + θ = (mode - qp.q) / a_qp + Laplace(T(mode), T(θ)) end function fit_mean_quantile(D::Type{<:Laplace}, mean::Real, qp::QuantilePoint) # mode equals mean fit_mode_quantile(D, mean, qp) end - diff --git a/src/univariate/continuous/logitnormal.jl b/src/univariate/continuous/logitnormal.jl index e89947a..b97f146 100644 --- a/src/univariate/continuous/logitnormal.jl +++ b/src/univariate/continuous/logitnormal.jl @@ -1,48 +1,47 @@ -mean(d::LogitNormal{T}; kwargs...) where T = T(estimateMean(d,kwargs...)) +mean(d::LogitNormal{T}; kwargs...) where {T} = T(estimateMean(d, kwargs...)) -function mode(d::LogitNormal{T}) where T +function mode(d::LogitNormal{T}) where {T} (μ, σ) = params(d) # if mu<0 then maximum is left of median, if mu>0 right of median # if mu=0 the maximum is either at mu or there are two maxima of the same height # here, report the left mode - interval = μ <= zero(μ) ? (zero(μ),logistic(μ)) : (logistic(μ),one(μ)) + interval = μ <= zero(μ) ? (zero(μ), logistic(μ)) : (logistic(μ), one(μ)) resOpt = optimize(x -> -pdf(d, x), interval[1], interval[2]) T(resOpt.minimizer) end -function var(d::LogitNormal{T}; mean=missing, kwargs...) where T +function var(d::LogitNormal{T}; mean = missing, kwargs...) where {T} # estimateVariance does not pass kwargs to mean, need to do it here - m = ismissing(mean) ? estimateMean(d,kwargs...) : mean - T(estimateVariance(d; kwargs..., mean=m)) + m = ismissing(mean) ? estimateMean(d, kwargs...) : mean + T(estimateVariance(d; kwargs..., mean = m)) end # function fit(::Type{LogitNormal}, m::AbstractMoments) # error("Fitting LogitNormal to moments not implemented.") # end -function fit(::Type{LogitNormal}, lower::QuantilePoint, upper::QuantilePoint) +function fit(::Type{LogitNormal}, lower::QuantilePoint, upper::QuantilePoint) fit(LogitNormal{Float64}, lower, upper) end -function fit(::Type{LogitNormal{T}}, lower::QuantilePoint, upper::QuantilePoint) where T +function fit(::Type{LogitNormal{T}}, lower::QuantilePoint, upper::QuantilePoint) where {T} lower_logit = QuantilePoint(lower, q = logit(lower.q)) upper_logit = QuantilePoint(upper, q = logit(upper.q)) DN = fit(Normal, lower_logit, upper_logit) LogitNormal((T(p) for p in params(DN))...) end -function fit_mode_quantile(::Type{LogitNormal}, mode::T, qp::QuantilePoint) where T <: Real +function fit_mode_quantile(::Type{LogitNormal}, + mode::T, + qp::QuantilePoint) where {T <: Real} fit_mode_quantile(LogitNormal{T}, mode, qp) end -function fit_mode_quantile(::Type{LogitNormal{T}}, mode::Real, qp::QuantilePoint) where T +function fit_mode_quantile(::Type{LogitNormal{T}}, mode::Real, qp::QuantilePoint) where {T} matchModeUpper(T(mode), qp.q, Val(40); perc = qp.p) end -function matchModeUpper( - mode::T, upper # partype only defined by mode - TODO define eltype by upper - , ::Val{nTry} - ; perc::Real = 0.99 -) where {nTry, T<:Real} - mode == 0.5 && return matchMedianUpper(LogitNormal, 0.5, upper; perc=perc) +function matchModeUpper(mode::T, upper, ::Val{nTry} + ; perc::Real = 0.99) where {nTry, T <: Real} + mode == 0.5 && return matchMedianUpper(LogitNormal, 0.5, upper; perc = perc) # for given mu we can compute sigma by mode and upper quantile # hence univariate search for mu # we now that mu is in (\code{logit(mode)},0) for \code{mode < 0.5} and in @@ -53,22 +52,22 @@ function matchModeUpper( # logitMode = logit(mode) logitUpper = logit(upper) - upperMu = abs(logitMode) - eps() - muTry = SVector{nTry}( - sign(logitMode) .* log.(range(1,stop=exp(upperMu),length=nTry))) + upperMu = abs(logitMode) - eps() + muTry = SVector{nTry}(sign(logitMode) .* + log.(range(1, stop = exp(upperMu), length = nTry))) oF(mu) = ofLogitNormalModeUpper(mu, mode, logitMode, logitUpper, perc) ofMuTry = oF.(muTry) iMin = argmin(ofMuTry) # on postive side muTry are increasing, on negative side muTry decreasing # neet to have the lower value at the beginning of the interval - interval = (logitMode >= 0) ? - (muTry[max(1,iMin-1)], muTry[min(nTry,iMin+1)]) : - (muTry[max(1,iMin+1)], muTry[min(nTry,max(1,iMin-1))]) + interval = (logitMode >= 0) ? + (muTry[max(1, iMin - 1)], muTry[min(nTry, iMin + 1)]) : + (muTry[max(1, iMin + 1)], muTry[min(nTry, max(1, iMin - 1))]) resOpt = optimize(oF, interval...) resOpt.converged || error("could not find minimum") μ = resOpt.minimizer - σ = sqrt((logitMode - μ)/(2*mode - 1)) - LogitNormal{T}(T(μ),T(σ)) + σ = sqrt((logitMode - μ) / (2 * mode - 1)) + LogitNormal{T}(T(μ), T(σ)) end # function matchModeUpper( @@ -78,17 +77,16 @@ end # matchModeUpper(mode_p, upper_p, Val(nTry); kwargs...) # end - "objective function used by `matchModeUpper(LogitNormal,...)`" function ofLogitNormalModeUpper(mu, mode, logitMode, logitUpper, perc) - # given mu and mode, we can calculate sigma, - # predict the percentile of logitUpper - # and return the squared difference as cost to be minimized - sigma2 = (logitMode - mu)/(2.0*mode - 1.0) - sigma2 > 0.0 || return prevfloat(Inf) - predp = normcdf(mu, sqrt(sigma2), logitUpper) - diff = predp - perc - diff*diff + # given mu and mode, we can calculate sigma, + # predict the percentile of logitUpper + # and return the squared difference as cost to be minimized + sigma2 = (logitMode - mu) / (2.0 * mode - 1.0) + sigma2 > 0.0 || return prevfloat(Inf) + predp = normcdf(mu, sqrt(sigma2), logitUpper) + diff = predp - perc + diff * diff end """ @@ -109,31 +107,37 @@ mode(d) ≈ m true ``` """ -function fit_mode_flat(::Type{LogitNormal}, mode::T, ::Val{nTry} = Val(40); peakedness = 1) where {T<:Real,nTry} +function fit_mode_flat(::Type{LogitNormal}, + mode::T, + ::Val{nTry} = Val(40); + peakedness = 1) where {T <: Real, nTry} fit_mode_flat(LogitNormal{T}, mode, Val(nTry); peakedness) end -function fit_mode_flat(::Type{LogitNormal{T}}, mode, ::Val{nTry} = Val(40); peakedness = 1) where {T<:Real,nTry} - mode == 0.5 && return(LogitNormal{T}(zero(T), sqrt(2)/peakedness)) +function fit_mode_flat(::Type{LogitNormal{T}}, + mode, + ::Val{nTry} = Val(40); + peakedness = 1) where {T <: Real, nTry} + mode == 0.5 && return (LogitNormal{T}(zero(T), sqrt(2) / peakedness)) is_right = mode > 0.5 mode_r = is_right ? mode : 1 - mode res_opt = optimize(x -> of_mode_flat(x, mode_r, logit(mode_r)), 0.0, 0.5) res_opt.converged || error("could not find minimum") xt = res_opt.minimizer - σ2 = (1/xt + 1/(1-xt))/2/peakedness^2 - μr = logit(mode_r) - σ2*(2*mode_r - 1.0) + σ2 = (1 / xt + 1 / (1 - xt)) / 2 / peakedness^2 + μr = logit(mode_r) - σ2 * (2 * mode_r - 1.0) μ = is_right ? μr : -μr - LogitNormal{T}(T(μ),T(sqrt(σ2))) + LogitNormal{T}(T(μ), T(sqrt(σ2))) end function of_mode_flat(x, m, logitm = logit(m)) - # lhs = 1/x + 1/(1-x) - # rhs = (logitm - logit(x))/(m-x) - # lhs = (m-x)/x + (m-x)/(1-x)P - # rhs = (logitm - logit(x)) - lhs = m/x + (m-1)/(1-x) - rhs = logitm - logit(x) - d = lhs - rhs - d*d + # lhs = 1/x + 1/(1-x) + # rhs = (logitm - logit(x))/(m-x) + # lhs = (m-x)/x + (m-x)/(1-x)P + # rhs = (logitm - logit(x)) + lhs = m / x + (m - 1) / (1 - x) + rhs = logitm - logit(x) + d = lhs - rhs + d * d end """ @@ -143,18 +147,10 @@ Get a Shifted Flat LogitNormal distribution that is most spread with an extent between lower and upper. This is a more smooth alternative to the bounded uniform distribution. """ -function shifloNormal(lower,upper) - lower,upper = promote(lower,upper/1) +function shifloNormal(lower, upper) + lower, upper = promote(lower, upper / 1) T = typeof(lower) dln = LogitNormal(zero(T), sqrt(T(2))) #dln * (upper-lower) + lower - Distributions.AffineDistribution{T}(lower, (upper -lower), dln) + Distributions.AffineDistribution{T}(lower, (upper - lower), dln) end - - - - - - - - diff --git a/src/univariate/continuous/lognormal.jl b/src/univariate/continuous/lognormal.jl index 8aaecfe..ad8fcd3 100644 --- a/src/univariate/continuous/lognormal.jl +++ b/src/univariate/continuous/lognormal.jl @@ -1,17 +1,17 @@ fit(::Type{LogNormal}, m::AbstractMoments) = fit(LogNormal{eltype(m)}, m) -function fit(::Type{LogNormal{T}}, m::AbstractMoments) where T +function fit(::Type{LogNormal{T}}, m::AbstractMoments) where {T} # https://en.wikipedia.org/wiki/Log-normal_distribution n_moments(m) >= 2 || error("Need mean and variance to estimate lognormal") - γ = 1+var(m)/mean(m)^2 - μ = log(mean(m)/sqrt(γ)) + γ = 1 + var(m) / mean(m)^2 + μ = log(mean(m) / sqrt(γ)) σ = sqrt(log(γ)) - return LogNormal(T(μ),T(σ)) + return LogNormal(T(μ), T(σ)) end function fit(::Type{LogNormal}, lower::QuantilePoint, upper::QuantilePoint) fit(LogNormal{Float64}, lower, upper) end -function fit(::Type{LogNormal{T}}, lower::QuantilePoint, upper::QuantilePoint) where T +function fit(::Type{LogNormal{T}}, lower::QuantilePoint, upper::QuantilePoint) where {T} #length(qset) == 2 || error("only implemented yet for exactly two quantiles.") #qset_log = [QuantilePoint(qp, q = log(qp.q)) for qp in qset] lower_log = QuantilePoint(lower, q = log(lower.q)) @@ -20,37 +20,37 @@ function fit(::Type{LogNormal{T}}, lower::QuantilePoint, upper::QuantilePoint) w LogNormal(params(DN)...) end -function fit_mean_quantile(::Type{LogNormal}, mean::T, qp::QuantilePoint) where T <: Real +function fit_mean_quantile(::Type{LogNormal}, mean::T, qp::QuantilePoint) where {T <: Real} fit_mean_quantile(LogNormal{T}, mean, qp) end -function fit_mean_quantile(::Type{LogNormal{T}}, mean::Real, qp::QuantilePoint) where T +function fit_mean_quantile(::Type{LogNormal{T}}, mean::Real, qp::QuantilePoint) where {T} # solution of # (1) mean = exp(mu + sigma^2/2) # (2) upper = mu + sigmaFac sigma # see R packaage lognorm inst/doc/coefLognorm.Rmd for derivation sigmaFac = quantile(Normal(), qp.p) m = log(mean) - discr = sigmaFac^2 - 2*(log(qp.q) - m) + discr = sigmaFac^2 - 2 * (log(qp.q) - m) (discr > 0) || error("Cannot fit LogNormal with quantile $(qp) and mean $(mean).") sigma = sigmaFac > sqrt(discr) ? (sigmaFac - sqrt(discr)) : sigmaFac + sqrt(discr) - mu = m - sigma^2/2 + mu = m - sigma^2 / 2 LogNormal(T(mu), T(sigma)) end -function fit_mode_quantile(::Type{LogNormal}, mode::T, qp::QuantilePoint) where T <: Real +function fit_mode_quantile(::Type{LogNormal}, mode::T, qp::QuantilePoint) where {T <: Real} fit_mode_quantile(LogNormal{T}, mode, qp) end -function fit_mode_quantile(::Type{LogNormal{T}}, mode::Real, qp::QuantilePoint) where T +function fit_mode_quantile(::Type{LogNormal{T}}, mode::Real, qp::QuantilePoint) where {T} # solution of # (1) mle = exp(mu - sigma^2) # (2) upper = mu + sigmaFac sigma # see R packaage lognorm inst/doc/coefLognorm.Rmd for derivation - sigmaFac = quantile(Normal(),qp.p) + sigmaFac = quantile(Normal(), qp.p) m = log(mode) - discr = sigmaFac^2/4 + (log(qp.q) - m) + discr = sigmaFac^2 / 4 + (log(qp.q) - m) (discr > 0) || error("Cannot fit LogNormal with quantile $(qp) and mode $(mode).") root_discr = sqrt(discr) - mfh = -sigmaFac/2 + mfh = -sigmaFac / 2 sigma = mfh > root_discr ? (mfh - root_discr) : (mfh + root_discr) #sigma = mfh + root_discr mu = m + sigma^2 @@ -76,11 +76,11 @@ true abstract type AbstractΣstar end struct Σstar{T} <: AbstractΣstar - σstar::T + σstar::T end (a::Σstar)() = a.σstar -Base.eltype(::Type{Σstar{T}}) where T = T -Base.eltype(::Σstar{T}) where T = T +Base.eltype(::Type{Σstar{T}}) where {T} = T +Base.eltype(::Σstar{T}) where {T} = T """ σstar(d) @@ -121,14 +121,14 @@ d = fit(LogNormal, 2, Σstar(1.1)); true ``` """ -function fit(::Type{LogNormal}, mean::T, σstar::AbstractΣstar) where T <: Real +function fit(::Type{LogNormal}, mean::T, σstar::AbstractΣstar) where {T <: Real} _T = promote_type(T, eltype(σstar)) fit(LogNormal{_T}, mean, σstar) end -function fit(::Type{LogNormal{T}}, mean::Real, σstar::AbstractΣstar) where T +function fit(::Type{LogNormal{T}}, mean::Real, σstar::AbstractΣstar) where {T} σ = log(σstar()) - μ = log(mean) - σ*σ/2 + μ = log(mean) - σ * σ / 2 LogNormal(T(μ), T(σ)) end @@ -153,9 +153,7 @@ d = fit_mean_relerror(LogNormal, 10.0, 0.03); function fit_mean_relerror(::Type{LogNormal}, mean, relerror) # e.g. Limpert 2001, Wutzler 2020 w = 1 + abs2(relerror) - μ = log(mean/sqrt(w)) + μ = log(mean / sqrt(w)) σ = sqrt(log(w)) LogNormal(μ, σ) end - - diff --git a/src/univariate/continuous/normal.jl b/src/univariate/continuous/normal.jl index 6c9d563..71efb3d 100644 --- a/src/univariate/continuous/normal.jl +++ b/src/univariate/continuous/normal.jl @@ -1,34 +1,37 @@ fit(::Type{Normal}, m::AbstractMoments) = fit(Normal{eltype(m)}, m) -function fit(::Type{Normal{T}}, m::AbstractMoments) where T +function fit(::Type{Normal{T}}, m::AbstractMoments) where {T} n_moments(m) >= 2 || error("Need mean and variance to estimate normal") - return Normal(T(mean(m)),T(std(m))) + return Normal(T(mean(m)), T(std(m))) end function fit(::Type{Normal}, lower::QuantilePoint, upper::QuantilePoint) fit(Normal{Float64}, lower, upper) end -function fit(::Type{Normal{T}}, lower::QuantilePoint, upper::QuantilePoint) where T +function fit(::Type{Normal{T}}, lower::QuantilePoint, upper::QuantilePoint) where {T} # https://www.johndcook.com/quantiles_parameters.pdf - if (upper < lower) - lower,upper = (upper,lower) + if (upper < lower) + lower, upper = (upper, lower) end q_lower, q_upper = promote(lower.q, upper.q) TQ = typeof(q_lower) qz1 = convert(TQ, quantile(Normal(), lower.p))::TQ qz2 = convert(TQ, quantile(Normal(), upper.p))::TQ dqz = (qz2 - qz1) - σ = (q_upper - q_lower)/dqz - μ = (q_lower*qz2 - q_upper*qz1)/dqz - Normal(T(μ),T(σ)) + σ = (q_upper - q_lower) / dqz + μ = (q_lower * qz2 - q_upper * qz1) / dqz + Normal(T(μ), T(σ)) end -fit_mean_quantile(::Type{Normal}, mean::T, qp::QuantilePoint) where T <: Real = +function fit_mean_quantile(::Type{Normal}, mean::T, qp::QuantilePoint) where {T <: Real} fit(Normal{T}, mean, qp) -fit_mean_quantile(D::Type{Normal{T}}, mean::Real, qp::QuantilePoint) where T = +end +function fit_mean_quantile(D::Type{Normal{T}}, mean::Real, qp::QuantilePoint) where {T} fit(D, QuantilePoint(mean, 0.5), qp) +end -fit_mode_quantile(::Type{Normal}, mode::T, qp::QuantilePoint) where T <: Real = +function fit_mode_quantile(::Type{Normal}, mode::T, qp::QuantilePoint) where {T <: Real} fit(Normal{T}, mode, qp) -fit_mode_quantile(D::Type{Normal{T}}, mode::Real, qp::QuantilePoint) where T = +end +function fit_mode_quantile(D::Type{Normal{T}}, mode::Real, qp::QuantilePoint) where {T} fit(D, QuantilePoint(mode, 0.5), qp) - \ No newline at end of file +end diff --git a/src/univariate/continuous/weibull.jl b/src/univariate/continuous/weibull.jl index 358b6fb..f304bc8 100644 --- a/src/univariate/continuous/weibull.jl +++ b/src/univariate/continuous/weibull.jl @@ -7,10 +7,11 @@ function fit(::Type{Weibull}, lower::QuantilePoint, upper::QuantilePoint) fit(Weibull{Float64}, lower, upper) end -function fit(::Type{Weibull{T}}, lower::QuantilePoint, upper::QuantilePoint) where T +function fit(::Type{Weibull{T}}, lower::QuantilePoint, upper::QuantilePoint) where {T} # https://www.johndcook.com/quantiles_parameters.pdf - gamma = (log(-log(1-upper.p)) - log(-log(1-lower.p)))/(log(upper.q) -log(lower.q)) - beta = lower.q / (-log(1-lower.p))^(1/gamma) + gamma = (log(-log(1 - upper.p)) - log(-log(1 - lower.p))) / + (log(upper.q) - log(lower.q)) + beta = lower.q / (-log(1 - lower.p))^(1 / gamma) Weibull(T(gamma), T(beta)) end @@ -24,5 +25,3 @@ end # θ = -qp.q/log(1-qp.p) # Exponential(θ) # end - - diff --git a/src/univariates.jl b/src/univariates.jl index 2ee9ca0..cfc4c2f 100644 --- a/src/univariates.jl +++ b/src/univariates.jl @@ -1,21 +1,21 @@ ##### specific distributions ##### const discrete_distributions = [ - # "bernoulli", - # "betabinomial", - # "binomial", - # "dirac", - # "discreteuniform", - # "discretenonparametric", - # "categorical", - # "geometric", - # "hypergeometric", - # "negativebinomial", - # "noncentralhypergeometric", - # "poisson", - # "skellam", - # "soliton", - # "poissonbinomial" +# "bernoulli", +# "betabinomial", +# "binomial", +# "dirac", +# "discreteuniform", +# "discretenonparametric", +# "categorical", +# "geometric", +# "hypergeometric", +# "negativebinomial", +# "noncentralhypergeometric", +# "poisson", +# "skellam", +# "soliton", +# "poissonbinomial" ] const continuous_distributions = [ @@ -29,10 +29,10 @@ const continuous_distributions = [ # "chi", # "cosine", # "epanechnikov", - "exponential", + "exponential", # "fdist", # "frechet", - "gamma", + "gamma", # "erlang", # "pgeneralizedgaussian", # GeneralizedGaussian depends on Gamma # "generalizedpareto", @@ -43,7 +43,7 @@ const continuous_distributions = [ # "kolmogorov", # "ksdist", # "ksonesided", - "laplace", + "laplace", # "levy", # "locationscale", # "logistic", @@ -51,11 +51,11 @@ const continuous_distributions = [ # "noncentralchisq", # "noncentralf", # "noncentralt", - "normal", + "normal", # "normalcanon", # "normalinversegaussian", - "lognormal", # LogNormal depends on Normal - "logitnormal", # LogitNormal depends on Normal + "lognormal", # LogNormal depends on Normal + "logitnormal", # LogitNormal depends on Normal # "pareto", # "rayleigh", # "semicircle", @@ -67,7 +67,7 @@ const continuous_distributions = [ # "triweight", # "uniform", # "vonmises", - "weibull" + "weibull", ] for dname in discrete_distributions diff --git a/test/fitstats.jl b/test/fitstats.jl index d358145..f060945 100644 --- a/test/fitstats.jl +++ b/test/fitstats.jl @@ -2,7 +2,7 @@ using StatsBase, Distributions, DistributionFits, Test @testset "moments" begin @testset "two moments" begin - m = @inferred Moments(1,0.5) + m = @inferred Moments(1, 0.5) @test n_moments(typeof(m)) == 2 @test n_moments(m) == 2 #@test n_moments(typeof(M)) == 2 @@ -12,13 +12,13 @@ using StatsBase, Distributions, DistributionFits, Test @test_throws Exception skewness(m) @test_throws Exception kurtosis(m) @test (@inferred convert(AbstractArray, m)) == [1.0, 0.5] - end; + end @testset "zero moments" begin - m = @inferred Moments() + m = @inferred Moments() @test (@inferred n_moments(m)) == 0 @test_throws Exception mean(m) @test_throws Exception m[1] - end; + end end; @testset "moments of Distribution" begin @@ -37,65 +37,63 @@ end; @test_throws Exception moments(d, Val(:bla)) end @testset "Normal" begin - d = @inferred Normal(2,5) + d = @inferred Normal(2, 5) testMoments(d) - end; - @testset "Lognormal" begin - testMoments((@inferred LogNormal(2,5))) - end; + end + @testset "Lognormal" begin + testMoments((@inferred LogNormal(2, 5))) + end @testset "Logitnormal" begin # mean not defined fro LogitNormal - @test_throws MethodError testMoments((@inferred LogitNormal(2,5))) - end; + @test_throws MethodError testMoments((@inferred LogitNormal(2, 5))) + end end; @testset "QuantilePoint" begin - qp1 = @inferred QuantilePoint(2,0.25); - qp2 = @inferred QuantilePoint(7.5,0.95); + qp1 = @inferred QuantilePoint(2, 0.25) + qp2 = @inferred QuantilePoint(7.5, 0.95) @testset "isless" begin #println("isless") # p and q both increasing/decreasing - @test (@inferred(QuantilePoint(2,0.4) < QuantilePoint(3,0.5))) == true - @test (@inferred(QuantilePoint(1,0.6) < QuantilePoint(1,0.5))) == false + @test (@inferred(QuantilePoint(2, 0.4) x*x, -1, 1) + @test_throws Exception DistributionFits.optimize(x -> x * x, -1, 1) end # Optim package for interactive testing i_loadlibs = () -> begin @@ -13,7 +13,9 @@ i_loadlibs = () -> begin end using Optim: Optim, optimize -DistributionFitsOptimExt = isdefined(Base, :get_extension) ? Base.get_extension(DistributionFits, :DistributionFitsOptimExt) : DistributionFits.DistributionFitsOptimExt +DistributionFitsOptimExt = isdefined(Base, :get_extension) ? + Base.get_extension(DistributionFits, :DistributionFitsOptimExt) : + DistributionFits.DistributionFitsOptimExt @testset "optimize set in __init__ after using Optim" begin # set in __init__ @@ -29,11 +31,9 @@ include("fitstats.jl") #include("test/univariate/test_univariate.jl") include("univariate/test_univariate.jl") - # test coverage of set_optimize (already called in init) # print method ambiguities println("Potentially stale exports: ") display(Test.detect_ambiguities(DistributionFits)) println() - diff --git a/test/testutils.jl b/test/testutils.jl index 48628c0..d1ef548 100644 --- a/test/testutils.jl +++ b/test/testutils.jl @@ -1,6 +1,6 @@ function test_mode_larger_than_neighbors(d) @testset "mode is larger than neighbors" begin mo = mode(d) - @test pdf(d, mo-1e-6) < pdf(d,mo) && pdf(d, mo+1e-6) < pdf(d,mo) - end; + @test pdf(d, mo - 1e-6) < pdf(d, mo) && pdf(d, mo + 1e-6) < pdf(d, mo) + end end diff --git a/test/univariate/continuous/exponential.jl b/test/univariate/continuous/exponential.jl index 8124ca9..fdd4dce 100644 --- a/test/univariate/continuous/exponential.jl +++ b/test/univariate/continuous/exponential.jl @@ -2,7 +2,7 @@ @testset "Default partype" begin d = Exponential(2.1) # note: did not specify type parameter - test_univariate_fits(d,Exponential) + test_univariate_fits(d, Exponential) end; @testset "Float32" begin @@ -12,18 +12,18 @@ end; @testset "fit two quantiles same" begin qpl = @qp_m(3) - d = fit(Exponential, qpl, qpl); + d = fit(Exponential, qpl, qpl) @test quantile.(d, [qpl.p]) ≈ [qpl.q] end; @testset "fit quantiles missing" begin qpl = @qp_m(3) - d = fit(Exponential, qpl, missing); + d = fit(Exponential, qpl, missing) @test quantile.(d, [qpl.p]) ≈ [qpl.q] - d = fit(Exponential, missing, qpl); + d = fit(Exponential, missing, qpl) @test quantile.(d, [qpl.p]) ≈ [qpl.q] - d = fit_mode_quantile(Exponential, missing, qpl); + d = fit_mode_quantile(Exponential, missing, qpl) @test quantile.(d, [qpl.p]) ≈ [qpl.q] - d = fit_mean_quantile(Exponential, 1.2, missing); + d = fit_mean_quantile(Exponential, 1.2, missing) @test mean(d) ≈ 1.2 end; diff --git a/test/univariate/continuous/gamma.jl b/test/univariate/continuous/gamma.jl index 8c0209a..42c0009 100644 --- a/test/univariate/continuous/gamma.jl +++ b/test/univariate/continuous/gamma.jl @@ -1,7 +1,7 @@ @testset "Default partype" begin - d = Gamma(0.5,1.9) + d = Gamma(0.5, 1.9) # note: did not specify type parameter - test_univariate_fits(d,Gamma) + test_univariate_fits(d, Gamma) end; @testset "Float32" begin @@ -12,19 +12,19 @@ end; @testset "fit two quantiles" begin qpl = @qp_m(0.5) qpu = @qp_uu(5) - d = fit(Gamma, qpl, qpu); + d = fit(Gamma, qpl, qpu) @test median(d) ≈ 0.5 @test quantile(d, 0.975) ≈ 5 #Plots.plot(d); end; i_tmp = () -> begin - dw = fit(Weibull, @qp_m(0.5), @qp_uu(5)); - dg = fit(Gamma, @qp_m(0.5), @qp_uu(5)); + dw = fit(Weibull, @qp_m(0.5), @qp_uu(5)) + dg = fit(Gamma, @qp_m(0.5), @qp_uu(5)) Plots.plot(dw) - Plots.plot!(dg, xlim=(0,0.2)) + Plots.plot!(dg, xlim = (0, 0.2)) pdf(dw, 0) pdf(dg, 0) pdf(dw, 0.01) pdf(dg, 0.01) -end \ No newline at end of file +end diff --git a/test/univariate/continuous/laplace.jl b/test/univariate/continuous/laplace.jl index b583588..b6ef15b 100644 --- a/test/univariate/continuous/laplace.jl +++ b/test/univariate/continuous/laplace.jl @@ -1,12 +1,11 @@ @testset "Default partype" begin - d = Laplace(1.1,2) + d = Laplace(1.1, 2) # note: did not specify type parameter - test_univariate_fits(d,Laplace) + test_univariate_fits(d, Laplace) end; @testset "Float32" begin - d = Laplace(1.1f0,2f0) + d = Laplace(1.1f0, 2.0f0) test_univariate_fits(d) end; - diff --git a/test/univariate/continuous/logitnormal.jl b/test/univariate/continuous/logitnormal.jl index 2cc9efd..eadeab0 100644 --- a/test/univariate/continuous/logitnormal.jl +++ b/test/univariate/continuous/logitnormal.jl @@ -1,7 +1,7 @@ @testset "Default partype" begin d = LogitNormal(1.3) # note: did not specify type parameter - test_univariate_fits(d,LogitNormal) + test_univariate_fits(d, LogitNormal) end; @testset "Float32" begin @@ -11,78 +11,82 @@ end; @testset "numerical moments" begin DN = LogitNormal(1.3) - x = rand(DN, 1_000_000); + x = rand(DN, 1_000_000) m = mean(DN) - @test abs(m - mean(x))/mean(x) <= 1e-3 + @test abs(m - mean(x)) / mean(x) <= 1e-3 s2 = var(DN) - @test abs(s2 - var(x))/var(x) <= 1e-2 #1e-4 too strong for random numbers - s2m = var(DN; mean=m) # specify so that not need to recompute - @test abs(s2m - var(x))/var(x) <= 1e-2 #1e-4 too strong for random numbers + @test abs(s2 - var(x)) / var(x) <= 1e-2 #1e-4 too strong for random numbers + s2m = var(DN; mean = m) # specify so that not need to recompute + @test abs(s2m - var(x)) / var(x) <= 1e-2 #1e-4 too strong for random numbers sd = std(DN) # specify so that not need to recompute - @test abs(sd - std(x))/std(x) <= 1e-2 #1e-4 too strong for random numbers + @test abs(sd - std(x)) / std(x) <= 1e-2 #1e-4 too strong for random numbers # sd2 = std(DN, mean=m) # specify so that not need to recompute # @test abs(sd2 - std(x))/std(x) <= 1e-2 #1e-4 too strong for random numbers # DN32 = LogitNormal(1.3f0) @test partype(DN32) == Float32 - x = rand(DN32, 1_000_000); + x = rand(DN32, 1_000_000) # TODO: specify Type of sample with distribution creation? m = mean(DN32) @test m isa Float32 s2 = var(DN32) @test s2 isa Float32 - @test abs(s2 - var(x))/var(x) <= 1e-2 #1e-4 too strong for random numbers - s2m = var(DN32; mean=m) # specify so that not need to recompute + @test abs(s2 - var(x)) / var(x) <= 1e-2 #1e-4 too strong for random numbers + s2m = var(DN32; mean = m) # specify so that not need to recompute @test s2m isa Float32 - @test abs(s2m - var(x))/var(x) <= 1e-2 #1e-4 too strong for random numbers + @test abs(s2m - var(x)) / var(x) <= 1e-2 #1e-4 too strong for random numbers sd = std(DN32) # specify so that not need to recompute @test sd isa Float32 - @test abs(sd - std(x))/std(x) <= 1e-2 #1e-4 too strong for random numbers + @test abs(sd - std(x)) / std(x) <= 1e-2 #1e-4 too strong for random numbers end; - ###### numerical estimation of moments @testset "Logitnormal seek mode" begin #plot(g); vline!([mode(g)]) # median - g = LogitNormal(); test_mode_larger_than_neighbors(g) - g = LogitNormal(0,1.4); test_mode_larger_than_neighbors(g) + g = LogitNormal() + test_mode_larger_than_neighbors(g) + g = LogitNormal(0, 1.4) + test_mode_larger_than_neighbors(g) # larger one of two modes, infer the lower one - g = LogitNormal(0,1.6); test_mode_larger_than_neighbors(g) + g = LogitNormal(0, 1.6) + test_mode_larger_than_neighbors(g) # two modes, larger is on the right - g = LogitNormal(0.1,1.6); test_mode_larger_than_neighbors(g) + g = LogitNormal(0.1, 1.6) + test_mode_larger_than_neighbors(g) # two modes, larger is on the left - g = LogitNormal(-0.1,1.6); test_mode_larger_than_neighbors(g) + g = LogitNormal(-0.1, 1.6) + test_mode_larger_than_neighbors(g) # # test with Float32 - g = LogitNormal(0f0,1.4f0); + g = LogitNormal(0.0f0, 1.4f0) @test partype(g) == Float32 - test_mode_larger_than_neighbors(g) + test_mode_larger_than_neighbors(g) end -function is_logit_slope_monotone(d, upper=0.5, lower=0.0, decreasing = false) - x = range(lower,upper;length=41)[2:40] # plotting grid - dx = pdf.(d, x) #density function - if decreasing - all(diff(dx) .<= 0) - else - all(diff(dx) .>= 0) - end +function is_logit_slope_monotone(d, upper = 0.5, lower = 0.0, decreasing = false) + x = range(lower, upper; length = 41)[2:40]# plotting grid + dx = pdf.(d, x)#density function + if decreasing + all(diff(dx) .<= 0) + else + all(diff(dx) .>= 0) + end end @testset "fit by single mode and flat" begin d9 = d = fit_mode_flat(LogitNormal, 0.9) - @test isapprox( mode(d), 0.9, atol=1e-4) + @test isapprox(mode(d), 0.9, atol = 1e-4) @test is_logit_slope_monotone(d, 0.9) @test is_logit_slope_monotone(d, 0.0, 0.9, true) d1 = d = fit_mode_flat(LogitNormal, 0.1) - @test isapprox( mode(d), 0.1, atol=1e-4) + @test isapprox(mode(d), 0.1, atol = 1e-4) @test is_logit_slope_monotone(d, 0.1) @test is_logit_slope_monotone(d, 0.0, 0.1, true) @test d1.σ == d9.σ @test d1.μ == -d9.μ d5 = d = fit_mode_flat(LogitNormal, 0.5) - @test isapprox( mode(d), 0.5, atol=1e-4) + @test isapprox(mode(d), 0.5, atol = 1e-4) @test is_logit_slope_monotone(d, 0.5) @test is_logit_slope_monotone(d, 0.0, 0.5, true) #plot(d1) @@ -94,11 +98,11 @@ end end @testset "shifloNormal" begin - d = shifloNormal(1,3) - @test isapprox(mode(d), 2.0, atol=0.001) + d = shifloNormal(1, 3) + @test isapprox(mode(d), 2.0, atol = 0.001) @test minimum(d) == 1.0 @test maximum(d) == 3.0 - @test scale(d) == 3-1 + @test scale(d) == 3 - 1 @test location(d) == 1.0 #plot(d) end; @@ -111,24 +115,22 @@ end; # #plot(d) # end; - @testset "shifloNormal Float32" begin - d32 = shifloNormal(1f0,3f0) + d32 = shifloNormal(1.0f0, 3.0f0) @test partype(d32) == Float32 @test mean(d32) isa Float32 # wait until fix in AffineDistribution is merged to Distributions.jl # @test rand(d32) isa eltype(d32) m = mode(d32) @test m isa Float32 - @test isapprox(m, 2.0, atol=0.1) + @test isapprox(m, 2.0, atol = 0.1) dmin = minimum(d32) @test dmin == 1.0 @test maximum(d32) == 3.0 - @test scale(d32) == 3-1 + @test scale(d32) == 3 - 1 @test scale(d32) isa Float32 @test location(d32) == 1.0 @test location(d32) isa Float32 #plot(d) #dln = LogitNormal(0f0,sqrt(2f0)); plot!(dln) end; - diff --git a/test/univariate/continuous/lognormal.jl b/test/univariate/continuous/lognormal.jl index 518d4ee..972d7e3 100644 --- a/test/univariate/continuous/lognormal.jl +++ b/test/univariate/continuous/lognormal.jl @@ -1,15 +1,14 @@ @testset "Default partype" begin - d = LogNormal(1,0.6) + d = LogNormal(1, 0.6) # note: did not specify type parameter - test_univariate_fits(d,LogNormal) + test_univariate_fits(d, LogNormal) end; @testset "Float32" begin - d = LogNormal(1f0,0.6f0) + d = LogNormal(1.0f0, 0.6f0) test_univariate_fits(d) end; - @testset "Σstar" begin ss = Σstar(4.5) @test ss() == 4.5 @@ -28,14 +27,13 @@ end; end; @testset "fit_mean_relerror" begin - d = fit_mean_relerror(LogNormal, 10.0, 0.03); - @test all((mean(d), std(d)/mean(d)) .≈ (10.0, 0.03)) + d = fit_mean_relerror(LogNormal, 10.0, 0.03) + @test all((mean(d), std(d) / mean(d)) .≈ (10.0, 0.03)) # dfit32 = fit_mean_relerror(LogNormal, 10.0f0, 0.03f0) @test mean(dfit32) == 10.0 - @test std(dfit32)/mean(dfit32) ≈ 0.03 + @test std(dfit32) / mean(dfit32) ≈ 0.03 @test partype(dfit32) === Float32 # # plot(d); plot!(dfit32) end; - diff --git a/test/univariate/continuous/normal.jl b/test/univariate/continuous/normal.jl index f8e1b5f..eefbc9c 100644 --- a/test/univariate/continuous/normal.jl +++ b/test/univariate/continuous/normal.jl @@ -1,11 +1,10 @@ @testset "Default partype" begin - d = Normal(3,2) + d = Normal(3, 2) # note: did not specify type parameter - test_univariate_fits(d,Normal) + test_univariate_fits(d, Normal) end; @testset "Float32" begin - d = Normal(3f0,2f0) + d = Normal(3.0f0, 2.0f0) test_univariate_fits(d) end; - diff --git a/test/univariate/continuous/weibull.jl b/test/univariate/continuous/weibull.jl index ffe5fc9..fcdc8f0 100644 --- a/test/univariate/continuous/weibull.jl +++ b/test/univariate/continuous/weibull.jl @@ -1,11 +1,10 @@ @testset "Default partype" begin - d = Weibull(1,1) + d = Weibull(1, 1) # note: did not specify type parameter - test_univariate_fits(d,Weibull) + test_univariate_fits(d, Weibull) end; @testset "Float32" begin - d = Weibull(1f0,1f0) + d = Weibull(1.0f0, 1.0f0) test_univariate_fits(d) end; - diff --git a/test/univariate/test_univariate.jl b/test/univariate/test_univariate.jl index 6af4a55..9fedf4b 100644 --- a/test/univariate/test_univariate.jl +++ b/test/univariate/test_univariate.jl @@ -1,41 +1,42 @@ -function test_univariate_fits(d, D=typeof(d)) +function test_univariate_fits(d, D = typeof(d)) @testset "fit moments" begin - if !occursin("fit(::Type{D}", string(first(methods(fit, (Type{typeof(d)}, AbstractMoments))))) + if !occursin("fit(::Type{D}", + string(first(methods(fit, (Type{typeof(d)}, AbstractMoments))))) m = Moments(mean(d), var(d)) d_fit = fit(D, m) @test d ≈ d_fit @test partype(d_fit) == partype(d) end - end; + end @testset "fit two quantiles" begin qpl = @qp_l(quantile(d, 0.05)) qpu = @qp_u(quantile(d, 0.95)) - d_fit = fit(D, qpl, qpu); + d_fit = fit(D, qpl, qpu) @test quantile.(d, [qpl.p, qpu.p]) ≈ [qpl.q, qpu.q] - d_fit = fit(D, qpl, qpu); + d_fit = fit(D, qpl, qpu) @test quantile.(d, [qpl.p, qpu.p]) ≈ [qpl.q, qpu.q] d_fit = fit(D, qpu, qpl) # sort @test quantile.(d, [qpl.p, qpu.p]) ≈ [qpl.q, qpu.q] @test partype(d_fit) == partype(d) - end; + end @testset "fit two quantiles, function version" begin P = partype(d) qpl = qp_l(P(quantile(d, 0.05))) qpu = qp_u(P(quantile(d, 0.95))) - d_fit = fit(D, qpl, qpu); + d_fit = fit(D, qpl, qpu) @test quantile.(d, [qpl.p, qpu.p]) ≈ [qpl.q, qpu.q] - d_fit = fit(D, qpl, qpu); + d_fit = fit(D, qpl, qpu) @test quantile.(d, [qpl.p, qpu.p]) ≈ [qpl.q, qpu.q] d_fit = fit(D, qpu, qpl) # sort @test quantile.(d, [qpl.p, qpu.p]) ≈ [qpl.q, qpu.q] @test partype(d_fit) == partype(d) - end; + end @testset "typeof mean, mode equals partype" begin if !(d isa Gamma && first(params(d)) < 1) @test mean(d) isa partype(d) @test mode(d) isa partype(d) end - end; + end @testset "quantile is of eltype" begin # quantile still Float64 for Normal of eltype Float32 if d isa Normal && eltype(d) != Float64 @@ -46,19 +47,21 @@ function test_univariate_fits(d, D=typeof(d)) # quantile is sample-like: stick to eltype - special of normal # broken, because quantile Normal{Float32} returns Float32 # but eltype(D{Float32}) is Float64 - if d isa Union{LogNormal, LogitNormal, Exponential, Laplace, Weibull} && partype(d) != eltype(d) + if d isa Union{LogNormal, LogitNormal, Exponential, Laplace, Weibull} && + partype(d) != eltype(d) @test_broken quantile(d, 0.1f0) isa eltype(d) else @test quantile(d, 0.1f0) isa eltype(d) end - - end; + end @testset "fit to quantilepoint and mean" begin - if !occursin("fit_mean_quantile(::Type{D}", string(first( - methods(fit_mean_quantile, (Type{typeof(d)}, partype(d), QuantilePoint))))) + if !occursin("fit_mean_quantile(::Type{D}", + string(first(methods(fit_mean_quantile, + (Type{typeof(d)}, partype(d), QuantilePoint))))) m = log(mean(d)) - qp = @qp_u(quantile(d,0.95)) - logger = d isa Exponential ? MinLevelLogger(current_logger(), Logging.Error) : current_logger() + qp = @qp_u(quantile(d, 0.95)) + logger = d isa Exponential ? MinLevelLogger(current_logger(), Logging.Error) : + current_logger() with_logger(logger) do d_fit = fit_mean_quantile(D, mean(d), qp) @test d_fit ≈ d @@ -67,7 +70,7 @@ function test_univariate_fits(d, D=typeof(d)) @test d_fit ≈ d @test partype(d_fit) == partype(d) # with lower quantile - qp = @qp_l(quantile(d,0.05)) + qp = @qp_l(quantile(d, 0.05)) d_fit = fit_mean_quantile(D, mean(d), qp) @test d_fit ≈ d @test partype(d_fit) == partype(d) @@ -77,33 +80,34 @@ function test_univariate_fits(d, D=typeof(d)) # d_fit = fit_mean_quantile(D, mean(d), qp) # @test mean(d_fit) ≈ mean(d) && quantile(d_fit, qp.p) ≈ qp.q end - end; + end @testset "fit to quantilepoint and mode" begin if !(d isa Gamma && first(params(d)) < 1) && - !(d isa Weibull) - qp = qp_u(quantile(d,0.95)) + !(d isa Weibull) + qp = qp_u(quantile(d, 0.95)) d_fit = fit_mode_quantile(D, mode(d), qp) - @test d_fit ≈ d atol=0.1 + @test d_fit≈d atol=0.1 d_fit = fit(D, mode(d), qp, Val(:mode)) - @test d_fit ≈ d atol=0.1 + @test d_fit≈d atol=0.1 @test partype(d_fit) == partype(d) # with lower quantile - qp = qp_ll(quantile(d,0.025)) + qp = qp_ll(quantile(d, 0.025)) d_fit = fit(D, mode(d), qp, Val(:mode)) - @test mode(d_fit) ≈ mode(d) - @test quantile(d_fit, qp.p) ≈ qp.q atol=0.01 + @test mode(d_fit) ≈ mode(d) + @test quantile(d_fit, qp.p)≈qp.q atol=0.01 @test partype(d_fit) == partype(d) end - end; + end @testset "fit to quantilepoint and median" begin - qp = @qp_u(quantile(d,0.95)) - logger = d isa Exponential ? MinLevelLogger(current_logger(), Logging.Error) : current_logger() + qp = @qp_u(quantile(d, 0.95)) + logger = d isa Exponential ? MinLevelLogger(current_logger(), Logging.Error) : + current_logger() with_logger(logger) do d_fit = fit(D, median(d), qp, Val(:median)) @test d_fit ≈ d @test partype(d_fit) == partype(d) end - end; + end end const tests = [ @@ -123,4 +127,3 @@ for t in tests include("continuous/$t.jl") end end -