diff --git a/docs/src/lognormal.md b/docs/src/lognormal.md index fde54c9..62833b8 100644 --- a/docs/src/lognormal.md +++ b/docs/src/lognormal.md @@ -45,6 +45,33 @@ d = fit_mean_relerror(LogNormal, 2, 0.2) (true, true) ``` +## ScaledLogNormal: LogNormal(-x) +To support the use case of a distribution of strictly negative values, the +fitting of a mirrored LogNormal on `-x` is supported. + +There is a type-alias +`ScaledLogNormal = LocationScale{T, Continuous, LogNormal{T}} where T`, +denoting a scaled and shifted LogNormal distribution. + +There are fitting function dispatched by this type that fit +such a mirrored distribution. + +```jldoctest; output = false, setup = :(using DistributionFits) +d = fit_mean_Σ(ScaledLogNormal, -1, log(1.1)) +(mean(d), σstar(d)) .≈ (-1.0, 1.1) +# output +(true, true) +``` + +```jldoctest; output = false, setup = :(using DistributionFits) +d = fit(ScaledLogNormal, -1.0, @qp_ll(-1.32), Val(:mode)) +(mode(d), quantile(d, 0.025)) .≈ (-1.0, -1.32) +# output +(true, true) +``` +Note the usage of lower quantile for the mirrored distribution, here. + + ## Detailed API ```@docs diff --git a/ext/DistributionFitsOptimExt.jl b/ext/DistributionFitsOptimExt.jl index 78faf74..1f1e20a 100644 --- a/ext/DistributionFitsOptimExt.jl +++ b/ext/DistributionFitsOptimExt.jl @@ -11,7 +11,7 @@ function DistributionFits.optimize(f, ::OptimOptimizer, lower, upper) end function __init__() - @info "DistributionFits: setting OptimOptimizer" + #@info "DistributionFits: setting OptimOptimizer" #DistributionFits.set_optimizer(DistributionFitsOptimExt.OptimOptimizer()) DistributionFits.set_optimizer(OptimOptimizer()) end diff --git a/inst/scratch.jl b/inst/scratch.jl new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/inst/scratch.jl @@ -0,0 +1 @@ + diff --git a/src/DistributionFits.jl b/src/DistributionFits.jl index 91b20f0..8c65943 100644 --- a/src/DistributionFits.jl +++ b/src/DistributionFits.jl @@ -36,7 +36,7 @@ export # optimize, set_optimizer # LogNormal -export AbstractΣstar, Σstar, σstar +export AbstractΣstar, Σstar, σstar, ScaledLogNormal # LogitNormal export fit_mode_flat, shifloNormal diff --git a/src/univariate/continuous/lognormal.jl b/src/univariate/continuous/lognormal.jl index b960f63..b4f18da 100644 --- a/src/univariate/continuous/lognormal.jl +++ b/src/univariate/continuous/lognormal.jl @@ -166,3 +166,32 @@ function fit_mean_relerror(::Type{LogNormal}, mean, relerror) σ = sqrt(log(w)) LogNormal(μ, σ) end + + +#---- support LogNormal(-x) of negative values ------------ +const ScaledLogNormal{T} = LocationScale{T, Continuous, LogNormal{T}} where T + +σstar(d::ScaledLogNormal) = exp(params(d.ρ)[2]) + + +function fit_mean_Σ(::Type{ScaledLogNormal}, mean::T1, σ::T2) where {T1 <: Real,T2 <: Real} + _T = promote_type(T1, T2) + fit_mean_Σ(ScaledLogNormal{_T}, mean, σ) +end +function fit_mean_Σ(d::Type{ScaledLogNormal{T}}, mean::Real, σ::Real) where T + mean < 0 && return(-1 * fit_mean_Σ(LogNormal{T}, -mean, σ)) + 1 * fit_mean_Σ(LogNormal{T}, mean, σ) +end + +function fit_mode_quantile(::Type{ScaledLogNormal}, mode::T, qp::QuantilePoint) where T<:Real + fit_mode_quantile(ScaledLogNormal{T}, mode, qp) +end +function fit_mode_quantile(::Type{ScaledLogNormal{T}}, mode::Real, qp::QuantilePoint) where T + if mode < 0 + return(-1 * fit_mode_quantile(LogNormal{T}, -mode, QuantilePoint(-qp.q,1-qp.p))) + #return(-1 * fit_mode_quantile(LogNormal{T}, -mode, qp)) + end + 1 * fit_mode_quantile(LogNormal{T}, mode, qp) +end + + diff --git a/test/univariate/continuous/lognormal.jl b/test/univariate/continuous/lognormal.jl index e0347db..daa447a 100644 --- a/test/univariate/continuous/lognormal.jl +++ b/test/univariate/continuous/lognormal.jl @@ -54,3 +54,23 @@ end; @test mode(d) ≈ 0.5 @test quantile(d, 0.95) ≈ 0.9 end; + +@testset "fitting ScaledLogNormal" begin + d2 = fit_mean_Σ(ScaledLogNormal, -1, log(1.1)) + @test d2 isa ScaledLogNormal + @test σstar(d2) == 1.1 + + d2 = fit_mean_Σ(ScaledLogNormal, 1.0, log(1.1)) + @test d2 isa ScaledLogNormal + @test mean(d2) == 1.0 + @test σstar(d2) == 1.1 + + # take care to specify qp_ll here, its lower than mode + d3 = fit(ScaledLogNormal, -1.0, @qp_ll(-1.32), Val(:mode)) + @test d3 isa ScaledLogNormal + @test mode(d3) == -1.0 + @test quantile(d3, 0.025) ≈ -1.32 + + d3 = fit(ScaledLogNormal, 1.0, @qp_uu(1.32), Val(:mode)) + @test d3 isa ScaledLogNormal +end;