diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65ffe1b..8a4460b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - uses: actions/cache@v1 + - uses: actions/cache@v4 env: cache-name: cache-artifacts with: diff --git a/.gitignore b/.gitignore index 74ca53d..5dd2d63 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ docs/site/ Manifest.toml # script for testing -script.jl \ No newline at end of file +script.jl + +.vscode/ +.DS_Store \ No newline at end of file diff --git a/Project.toml b/Project.toml index ada5e96..2aa786a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "QuantileMatching" uuid = "c8fc808f-91d0-4910-90d7-c7fb3b64f671" authors = ["Gabriel Gobeil ", "Jonathan Jalbert "] -version = "0.1.0" +version = "0.2.0" [deps] CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" diff --git a/ReleaseNotes.md b/ReleaseNotes.md new file mode 100644 index 0000000..931ef21 --- /dev/null +++ b/ReleaseNotes.md @@ -0,0 +1,9 @@ +# Release Notes + +## 0.2.0 + +- Refactored `match()` for scalar matching. + The function can now be broadcast to match a vector. + +- Added an exception to parametric matching when the actual value to match is outside the support of the actual distribution. + This situation can occur when the actual value lies outside the support of the actual distribution while the target distribution is unbounded. In such cases, the returned matched value is `Inf`. diff --git a/docs/.DS_Store b/docs/.DS_Store index aaa5d36..10d1d86 100644 Binary files a/docs/.DS_Store and b/docs/.DS_Store differ diff --git a/docs/src/StationaryQuantileMatching.md b/docs/src/StationaryQuantileMatching.md index c0a1819..78353d1 100644 --- a/docs/src/StationaryQuantileMatching.md +++ b/docs/src/StationaryQuantileMatching.md @@ -134,7 +134,7 @@ qmm = EmpiricalQuantileMatchingModel(y⁺, x⁺) Quantile matching of the non-zero simulated precipitations: ```@example stationary -x̃⁺ = match(qmm, x⁺) +x̃⁺ = match.(qmm, x⁺) println("") # hide ``` @@ -212,7 +212,7 @@ qmm = ParametricQuantileMatchingModel(fd_Y, fd_X) Quantile matching of the non-zero simulated precipitations: ```@example stationary -x̃⁺ = match(qmm, x⁺) +x̃⁺ = match.(qmm, x⁺) println("") # hide ``` @@ -304,7 +304,7 @@ qmm = ParametricQuantileMatchingModel(fd_Y, fd_X) Quantile matching of the non-zero simulated precipitations: ```@example stationary -x̃⁺ = match(qmm, x⁺) +x̃⁺ = match.(qmm, x⁺) println("") # hide ``` @@ -369,7 +369,7 @@ qmm = ParametricQuantileMatchingModel(fd_Y, fd_X) Quantile matching of the non-zero simulated precipitations: ```@example stationary -x̃⁺ = match(qmm, x⁺) +x̃⁺ = match.(qmm, x⁺) println("") # hide ``` diff --git a/src/AbstractQuantileMatchingModel.jl b/src/AbstractQuantileMatchingModel.jl index 073a96d..0629589 100644 --- a/src/AbstractQuantileMatchingModel.jl +++ b/src/AbstractQuantileMatchingModel.jl @@ -9,6 +9,8 @@ abstract type AbstractQuantileMatchingModel end abstract type Stationary end abstract type NonStationary end +Base.Broadcast.broadcastable(obj::AbstractQuantileMatchingModel) = Ref(obj) + """ match(qmm::AbstractQuantileMappingModel, x::Vector{<:Real}) diff --git a/src/AbstractQuantileMatchingModel/EmpiricalQuantileMatchingModel.jl b/src/AbstractQuantileMatchingModel/EmpiricalQuantileMatchingModel.jl index 0ee2e49..6619245 100644 --- a/src/AbstractQuantileMatchingModel/EmpiricalQuantileMatchingModel.jl +++ b/src/AbstractQuantileMatchingModel/EmpiricalQuantileMatchingModel.jl @@ -105,7 +105,7 @@ function showEmpiricalQuantileMatchingModel(io::IO, obj::EmpiricalQuantileMatchi println(io, prefix, " ", "extrapolation: ",get_extrapolation(obj)) end -function match(eqmm::EmpiricalQuantileMatchingModel{Stationary}, x::Vector{<:Real}) +function match(eqmm::EmpiricalQuantileMatchingModel{Stationary}, x::Real) nbins = get_nbins(eqmm) @@ -123,7 +123,7 @@ function match(eqmm::EmpiricalQuantileMatchingModel{Stationary}, x::Vector{<:Rea x̃ = itp(x) end -function match(eqmm::EmpiricalQuantileMatchingModel{NonStationary}, x::Vector{<:Real}) +function match(eqmm::EmpiricalQuantileMatchingModel{NonStationary}, x::Real) nbins = get_nbins(eqmm) diff --git a/src/AbstractQuantileMatchingModel/ParametricQuantileMatchingModel.jl b/src/AbstractQuantileMatchingModel/ParametricQuantileMatchingModel.jl index 3097aac..3a20736 100644 --- a/src/AbstractQuantileMatchingModel/ParametricQuantileMatchingModel.jl +++ b/src/AbstractQuantileMatchingModel/ParametricQuantileMatchingModel.jl @@ -58,20 +58,43 @@ function showParametricQuantileMatchingModel(io::IO, obj::ParametricQuantileMatc end -function match(pqm::ParametricQuantileMatchingModel{Stationary}, x::Vector{<:Real}) +""" + match(pqm::ParametricQuantileMatchingModel{Stationary}, x::Real) + +Parametric quantile matching of the actual value `x` according to the parametric quantile matching model `pqm`. + +### Note + +If `x` is outside the support of the actual distribution and the target distribution is unbounded, the function returns an infinite value. +""" +function match(pqm::ParametricQuantileMatchingModel{Stationary}, x::Real) targetdist = get_targetdist(pqm) actualdist = get_actualdist(pqm) - p = cdf.(actualdist, x) - - x̃ = quantile.(targetdist, p) + p = cdf(actualdist, x) + if p ≈ 0. + if isfinite(minimum(targetdist)) + x̃ = minimum(targetdist) + else + x̃ = -Inf + end + elseif p ≈ 1. + if isfinite(maximum(targetdist)) + x̃ = maximum(targetdist) + else + x̃ = Inf + end + else + x̃ = quantile.(targetdist, p) + end + return x̃ end -function match(nspqm::ParametricQuantileMatchingModel{NonStationary}, x::Vector{<:Real}) +function match(nspqm::ParametricQuantileMatchingModel{NonStationary}, x::Real) targetdist = get_targetdist(nspqm) actualdist = get_actualdist(nspqm) diff --git a/src/functions.jl b/src/functions.jl index ac91d74..ee9f648 100644 --- a/src/functions.jl +++ b/src/functions.jl @@ -51,7 +51,7 @@ function eqm(y::Vector{<:Real}, x::Vector{<:Real}) qmm = EmpiricalQuantileMatchingModel(y⁺, x⁺) # Quantile matching of non-zero values - x̃⁺ = match(qmm, x⁺) + x̃⁺ = match.(qmm, x⁺) # Replace the non-zero values in the frequency adjusted series. x̃[x̃ .> 0] = x̃⁺ @@ -89,7 +89,7 @@ function pqm(pd::Type{<:ContinuousUnivariateDistribution}, y::AbstractVector{<:R qmm = ParametricQuantileMatchingModel(fd_Y, fd_X) # Quantile matching of non-zero values - x̃⁺ = match(qmm, x⁺) + x̃⁺ = match.(qmm, x⁺) # Replace the non-zero values in the frequency adjusted series. x̃[x̃ .> 0] = x̃⁺ diff --git a/test/AbstractQuantileMatchingModel/EmpiricalQuantileMatchingModel_test.jl b/test/AbstractQuantileMatchingModel/EmpiricalQuantileMatchingModel_test.jl index b1a5f81..cd4a71c 100644 --- a/test/AbstractQuantileMatchingModel/EmpiricalQuantileMatchingModel_test.jl +++ b/test/AbstractQuantileMatchingModel/EmpiricalQuantileMatchingModel_test.jl @@ -65,7 +65,7 @@ end qmm = EmpiricalQuantileMatchingModel(targetsample, actualsample) - @test match(qmm, [2.])[] ≈ 0.48401845735265503 + @test match(qmm, 2.) ≈ 0.48401845735265503 end @@ -73,7 +73,7 @@ end qmm = EmpiricalQuantileMatchingModel(targetsample, actualsample, projsample) - @test match(qmm, [2.])[] ≈ 0.8349460253709341 + @test match(qmm, 2.) ≈ 0.8349460253709341 end diff --git a/test/AbstractQuantileMatchingModel/ParametricQuantileMatchingModel_test.jl b/test/AbstractQuantileMatchingModel/ParametricQuantileMatchingModel_test.jl index 04758c3..9514834 100644 --- a/test/AbstractQuantileMatchingModel/ParametricQuantileMatchingModel_test.jl +++ b/test/AbstractQuantileMatchingModel/ParametricQuantileMatchingModel_test.jl @@ -52,33 +52,56 @@ end @testset "match(::ParametricQuantileMatchingModel)" begin - targetdist = Exponential(.5) - actualdist = Exponential(2) - projdist = Exponential(3) - @testset "stationary" begin + + targetdist = Exponential(.5) + actualdist = Exponential(2) qmm = ParametricQuantileMatchingModel(targetdist, actualdist) - x = [4] - x̃ = rate(actualdist)/rate(targetdist)*[4] + x = 4 + x̃ = rate(actualdist)/rate(targetdist)*4 @test match(qmm, x) ≈ x̃ end @testset "non-stationary" begin + + targetdist = Exponential(.5) + actualdist = Exponential(2) + projdist = Exponential(3) qmm = ParametricQuantileMatchingModel(targetdist, actualdist, projdist) λ = rate(targetdist)*rate(projdist)/rate(actualdist) - x = [4.] + x = 4. x̃ = quantile(Exponential(1/λ), cdf(projdist, x)) @test match(qmm, x) ≈ x̃ end + + @testset "outside the support" begin + + targetdist = Exponential(1) + actualdist = Beta(1,1) + + qmm = ParametricQuantileMatchingModel(targetdist, actualdist) + + @test isfinite(match(qmm, -1.)) + @test !isfinite(match(qmm, 1.2)) + + targetdist = Beta(2,2) + qmm = ParametricQuantileMatchingModel(targetdist, actualdist) + @test isfinite(match(qmm, 1.2)) + + targetdist = Normal(0,1) + qmm = ParametricQuantileMatchingModel(targetdist, actualdist) + @test !isfinite(match(qmm, -1.)) + + end end diff --git a/test/functions_test.jl b/test/functions_test.jl index ce3671a..dda3c99 100644 --- a/test/functions_test.jl +++ b/test/functions_test.jl @@ -24,7 +24,7 @@ actualsample = [0.21713363327549823, 1.502901744984021, 2.046092118287037, 2.2249912757196464, 2.6629189261859207] qmm = EmpiricalQuantileMatchingModel(targetsample, actualsample) - x̃ = match(qmm, actualsample) + x̃ = match.(qmm, actualsample) @test all(QuantileMatching.eqm(targetsample, actualsample) .≈ x̃) @@ -40,7 +40,7 @@ qmm = ParametricQuantileMatchingModel(fd_Y, fd_X) - x̃ = match(qmm, actualsample) + x̃ = match.(qmm, actualsample) @test all(pqm(Gamma, targetsample, actualsample) .≈ x̃)