-
Notifications
You must be signed in to change notification settings - Fork 432
Add Censored distribution #1470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
2d2b82c
eba2355
12786be
ad80bc0
b9a9d23
066d15e
c111081
ec24f3c
97db2f5
744133b
1ffed7a
e9b37cc
805211c
21194d6
74d5228
1123bd1
d6f293c
3d5ae7d
c555075
d9a1ff3
ae5fa7c
3dfbd72
e9aa316
da70bc9
a6204b4
ea7e77a
2609bd0
40afb67
d8830e1
5c21d83
36c3e7b
2b8c621
7ac66f6
ba1cdd3
595c227
9760a93
abfca6d
8be8573
f8c5171
5ef0974
a40b2b8
791795c
06db648
f140cb6
f21afba
90dba2d
a972a55
5a383ea
14e4f79
3f48e7f
66f4735
29be974
ad6d76a
7391d13
5a9b598
4ec91a0
625d8d7
a241067
247d52b
e29bdb9
cc19906
7219dab
f55d7e0
6e11add
f926b62
cc4c3eb
5630be0
b296aa9
67c0259
e444b80
87b68c2
a85bdca
f8bb1f7
804eba9
bed8267
73e5615
ff6d487
3f79d2a
9688143
d7b8eef
d778052
bb33140
d1468a0
98c4b0b
70c2cd3
4547804
d2e20f4
ac3b799
f024f45
768cfca
16633c6
72a6efb
f487cc6
13b016f
f9457db
e5e3246
5ac7dc7
6c0c789
f135580
19a331f
c712050
b4a1dda
0fb63a9
286e8e6
eb20f91
386f779
a11f0e8
18085ac
125dee4
72e6135
ddde5cd
ccef571
7af5e83
f0342a0
f155cda
d70e518
6ed1e96
399d9bf
6062f7f
e9d93a4
d2759ad
c584c85
e41b221
0273192
63b03f0
f323ae1
f4e3aa0
461c19a
cc4051b
fcf9fbc
c89458d
b33f0f4
36f2430
ba60409
68e5de2
8308cda
a1e8fe3
f632f10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,234 @@ | ||
| """ | ||
| censored(d::UnivariateDistribution, l::Real, u::Real) | ||
| Censor a univariate distribution `d` to the interval `[l, u]`. | ||
sethaxen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
sethaxen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| The lower bound `l` can be finite or `-Inf` and the upper bound `u` can be finite or | ||
| `Inf`. The function throws an error if `l > u`. | ||
sethaxen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| The function falls back to constructing a [`Censored`](@ref) wrapper. | ||
| # Implementation | ||
| To implement a specialized censored form for distributions of type `D`, the method | ||
| `censored(d::D, l::T, u::T) where {T <: Real}` should be implemented. | ||
| """ | ||
| function censored(d::UnivariateDistribution, l::Real, u::Real) | ||
| return censored(d, promote(l, u)...) | ||
| end | ||
| censored(d::UnivariateDistribution, l::T, u::T) where {T <: Real} = Censored(d, l, u) | ||
|
|
||
| """ | ||
| Censored | ||
| Generic wrapper for a censored distribution. | ||
| """ | ||
| struct Censored{D<:UnivariateDistribution, S<:ValueSupport, T <: Real} <: UnivariateDistribution{S} | ||
mschauer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| uncensored::D # the original distribution (uncensored) | ||
| lower::T # lower bound | ||
| upper::T # upper bound | ||
mschauer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| function Censored(d::UnivariateDistribution, l::T, u::T) where {T <: Real} | ||
| l ≤ u || error("the lower bound must be less than or equal to the upper bound") | ||
| new{typeof(d), value_support(typeof(d)), T}(d, l, u) | ||
| end | ||
| end | ||
|
|
||
| params(d::Censored) = tuple(params(d.uncensored)..., d.lower, d.upper) | ||
sethaxen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| partype(d::Censored) = partype(d.uncensored) | ||
sethaxen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Base.eltype(::Type{<:Censored{D}}) where {D} = eltype(D) | ||
|
|
||
| #### Range and Support | ||
|
|
||
| islowerbounded(d::Censored) = islowerbounded(d.uncensored) || isfinite(d.lower) | ||
| isupperbounded(d::Censored) = isupperbounded(d.uncensored) || isfinite(d.upper) | ||
|
|
||
| minimum(d::Censored) = max(minimum(d.uncensored), d.lower) | ||
| maximum(d::Censored) = min(maximum(d.uncensored), d.upper) | ||
|
|
||
| function insupport(d::Censored{<:UnivariateDistribution}, x::Real) | ||
| return d.lower ≤ x ≤ d.upper && insupport(d.uncensored, x) | ||
|
||
| end | ||
|
|
||
| #### Show | ||
|
|
||
| function show(io::IO, d::Censored) | ||
| print(io, "Censored(") | ||
| d0 = d.uncensored | ||
| uml, namevals = _use_multline_show(d0) | ||
| uml ? show_multline(io, d0, namevals; newline=false) : show_oneline(io, d0, namevals) | ||
| print(io, ", range=(", d.lower, ", ", d.upper, "))") | ||
| end | ||
|
|
||
| _use_multline_show(d::Censored) = _use_multline_show(d.uncensored) | ||
|
|
||
|
|
||
| #### Statistics | ||
|
|
||
| quantile(d::Censored, p::Real) = clamp(quantile(d.uncensored, p), d.lower, d.upper) | ||
|
|
||
| median(d::Censored) = clamp(median(d.uncensored), d.lower, d.upper) | ||
|
|
||
| function mean(d::Censored) | ||
sethaxen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| d0 = d.uncensored | ||
| lower = d.lower | ||
| upper = d.upper | ||
| dtrunc = truncated(d.uncensored, lower, upper) | ||
| prob_lower = if value_support(typeof(d)) === Discrete && isfinite(lower) | ||
| cdf(d0, lower) - pdf(d0, lower) | ||
| else | ||
| cdf(d0, lower) | ||
| end | ||
| prob_upper = ccdf(d0, upper) | ||
| prob_interval = 1 - (prob_lower + prob_upper) | ||
|
|
||
| μ = prob_interval * mean(dtrunc) | ||
| if isfinite(lower) | ||
| μ += prob_lower * lower | ||
| end | ||
| if isfinite(upper) | ||
| μ += prob_upper * upper | ||
| end | ||
| return μ | ||
| end | ||
|
|
||
| function var(d::Censored) | ||
| d0 = d.uncensored | ||
| lower = d.lower | ||
| upper = d.upper | ||
| dtrunc = truncated(d.uncensored, lower, upper) | ||
sethaxen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| prob_lower = if value_support(typeof(d)) === Discrete && isfinite(lower) | ||
mschauer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
sethaxen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| cdf(d0, lower) - pdf(d0, lower) | ||
| else | ||
| cdf(d0, lower) | ||
| end | ||
| prob_upper = ccdf(d0, upper) | ||
| prob_interval = 1 - (prob_lower + prob_upper) | ||
| μinterval = mean(dtrunc) | ||
|
|
||
| μ = prob_interval * μinterval | ||
| if isfinite(lower) | ||
| μ += prob_lower * lower | ||
| end | ||
| if isfinite(upper) | ||
| μ += prob_upper * upper | ||
| end | ||
|
|
||
| v = prob_interval * (var(dtrunc) + abs2(μinterval - μ)) | ||
| if isfinite(lower) | ||
| v += prob_lower * abs2(lower - μ) | ||
| end | ||
| if isfinite(upper) | ||
| v += prob_upper * abs2(upper - μ) | ||
| end | ||
| return v | ||
| end | ||
|
|
||
| function entropy(d::Censored) | ||
| d0 = d.uncensored | ||
| lower = d.lower | ||
| upper = d.upper | ||
| dtrunc = truncated(d.uncensored, lower, upper) | ||
| lcdf = cdf(d0, lower) | ||
| prob_lower = if value_support(typeof(d)) === Discrete && isfinite(lower) | ||
| lcdf - pdf(d0, lower) | ||
| else | ||
| lcdf | ||
| end | ||
| prob_upper = ccdf(d0, upper) | ||
| prob_interval = 1 - (prob_lower + prob_upper) | ||
| result = prob_interval * (entropy(dtrunc) - log(prob_interval)) - | ||
| prob_lower * log(lcdf) - xlogx(prob_upper) | ||
| return result | ||
| end | ||
|
|
||
|
|
||
| #### Evaluation | ||
|
|
||
| function pdf(d::Censored, x::Real) | ||
| d0 = d.uncensored | ||
| lower = d.lower | ||
| upper = d.upper | ||
| return if x == lower | ||
| cdf(d0, x) | ||
| elseif x == upper | ||
| uccdf = ccdf(d0, x) | ||
| if value_support(typeof(d)) === Discrete && isfinite(upper) | ||
| uccdf - pdf(d0, x) | ||
| else | ||
| uccdf | ||
| end | ||
| else | ||
| result = pdf(d0, x) | ||
| lower < x < upper ? result : zero(result) | ||
| end | ||
| end | ||
|
|
||
| function logpdf(d::Censored, x::Real) | ||
| d0 = d.uncensored | ||
| lower = d.lower | ||
| upper = d.upper | ||
| return if x == lower | ||
| logcdf(d0, x) | ||
| elseif x == upper | ||
| ulogccdf = logccdf(d0, x) | ||
| if value_support(typeof(d)) === Discrete && isfinite(upper) | ||
| logsubexp(ulogccdf, logpdf(d0, x)) | ||
| else | ||
| ulogccdf | ||
| end | ||
| else | ||
| result = logpdf(d0, x) | ||
| lower < x < upper ? result : oftype(result, -Inf) | ||
| end | ||
| end | ||
|
|
||
| function cdf(d::Censored, x::Real) | ||
| result = cdf(d.uncensored, x) | ||
| return if x < d.lower | ||
| zero(result) | ||
| elseif x < d.upper | ||
| result | ||
| else | ||
| one(result) | ||
| end | ||
| end | ||
|
|
||
| function logcdf(d::Censored, x::Real) | ||
| result = logcdf(d.uncensored, x) | ||
| return if x < d.lower | ||
| oftype(result, -Inf) | ||
| elseif x < d.upper | ||
| result | ||
| else | ||
| zero(result) | ||
| end | ||
| end | ||
|
|
||
| function ccdf(d::Censored, x::Real) | ||
| result = ccdf(d.uncensored, x) | ||
| return if x > d.upper | ||
| zero(result) | ||
| elseif x > d.lower | ||
| result | ||
| else | ||
| one(result) | ||
| end | ||
| end | ||
|
|
||
| function logccdf(d::Censored, x::Real) | ||
| result = logccdf(d.uncensored, x) | ||
| return if x > d.upper | ||
| oftype(result, -Inf) | ||
| elseif x > d.lower | ||
| result | ||
| else | ||
| zero(result) | ||
| end | ||
| end | ||
|
|
||
|
|
||
| #### Sampling | ||
|
|
||
| function rand(rng::AbstractRNG, d::Censored) | ||
| return clamp(rand(rng, d.uncensored), d.lower, d.upper) | ||
| end | ||
Uh oh!
There was an error while loading. Please reload this page.