|
141 | 141 | Distributions.var(d::BilinearExponential; mean=mean(d)) = first(quadgk(x->(x-mean)^2*pdf(d,x), d.μ-100*d.σ/d.shp, d.μ+100*d.σ/d.shp, maxevals=1000))
|
142 | 142 | Distributions.std(d::BilinearExponential; mean=mean(d)) = sqrt(var(d; mean))
|
143 | 143 | Distributions.skewness(d::BilinearExponential; mean=mean(d), std=std(d; mean)) = first(quadgk(x->(x-mean)^3*pdf(d,x), d.μ-100*d.σ/d.shp, d.μ+100*d.σ/d.shp, maxevals=1000))/std^3
|
144 |
| - Distributions.kurtosis(d::BilinearExponential; mean=mean(d), std=std(d; mean)) = first(quadgk(x->(x-mean)^4*pdf(d,x), d.μ-100*d.σ/d.shp, d.μ+100*d.σ/d.shp, maxevals=1000))/std^4 |
| 144 | + Distributions.kurtosis(d::BilinearExponential; mean=mean(d), std=std(d; mean)) = first(quadgk(x->(x-mean)^4*pdf(d,x), d.μ-100*d.σ/d.shp, d.μ+100*d.σ/d.shp, maxevals=1000))/std^4 - 3 |
145 | 145 |
|
146 | 146 | ## Affine transformations
|
147 | 147 | Base.:+(d::BilinearExponential{T}, c::Real) where {T} = BilinearExponential{T}(d.A, d.μ + c, d.σ, d.shp, d.skw)
|
|
152 | 152 | struct Radiocarbon{T<:Real} <: ContinuousUnivariateDistribution
|
153 | 153 | μ::T
|
154 | 154 | σ::T
|
| 155 | + dist::Vector{T} |
155 | 156 | ldist::Vector{T}
|
156 | 157 | lcdist::Vector{T}
|
157 | 158 | lccdist::Vector{T}
|
158 | 159 | end
|
159 | 160 |
|
160 | 161 | function Radiocarbon(μ::T, σ::T, ldist::Vector{T}) where {T<:Real}
|
161 | 162 | # Ensure normalization
|
162 |
| - normconst = sum(exp.(ldist)) * one(T) |
| 163 | + dist = exp.(ldist) |
| 164 | + normconst = sum(dist) * one(T) |
| 165 | + dist ./= normconst |
163 | 166 | ldist .-= normconst
|
164 | 167 |
|
165 | 168 | # Cumulative distributions
|
166 | 169 | lcdist = nanlogcumsumexp(ldist)
|
167 | 170 | lccdist = nanlogcumsumexp(ldist, reverse=true)
|
168 | 171 |
|
169 |
| - return Radiocarbon{T}(μ, σ, ldist, lcdist, lccdist) |
| 172 | + return Radiocarbon{T}(μ, σ, dist, ldist, lcdist, lccdist) |
170 | 173 | end
|
171 | 174 |
|
172 | 175 | function Radiocarbon(Age_14C::Real, Age_14C_sigma::Real, calibration::NamedTuple=intcal13)
|
173 | 176 | @assert calibration.Age_Calendar == 1:1:length(calibration.Age_14C)
|
174 | 177 | @assert step(calibration.Age_Calendar) == calibration.dt == 1
|
175 | 178 |
|
176 | 179 | ldist = normlogproduct.(Age_14C, Age_14C_sigma, calibration.Age_14C, calibration.Age_sigma)
|
177 |
| - dist = exp.(ldist) |
178 | 180 |
|
179 |
| - # Normalize |
| 181 | + # Ensure normalization |
| 182 | + dist = exp.(ldist) |
180 | 183 | normconst = sum(dist) * calibration.dt
|
181 | 184 | dist ./= normconst
|
182 | 185 | ldist .-= normconst
|
| 186 | + |
183 | 187 | μ = histmean(dist, calibration.Age_Calendar)
|
184 | 188 | σ = histstd(dist, calibration.Age_Calendar, corrected=false)
|
185 | 189 |
|
186 | 190 | # Cumulative distributions
|
187 | 191 | lcdist = nanlogcumsumexp(ldist)
|
188 | 192 | lccdist = nanlogcumsumexp(ldist, reverse=true)
|
189 | 193 |
|
190 |
| - return Radiocarbon(μ, σ, ldist, lcdist, lccdist) |
| 194 | + return Radiocarbon(μ, σ, dist, ldist, lcdist, lccdist) |
191 | 195 | end
|
192 | 196 |
|
193 | 197 | ## Conversions
|
|
205 | 209 |
|
206 | 210 | ## Evaluation
|
207 | 211 | @inline Distributions.pdf(d::Radiocarbon, x::Real) = exp(logpdf(d, x))
|
| 212 | + @inline function Distributions.pdf(d::Radiocarbon{T}, x::Real) where {T} |
| 213 | + if firstindex(d.ldist) <= x <= lastindex(d.ldist) |
| 214 | + linterp_at_index(d.dist, x, -maxintfloat(T)) |
| 215 | + else |
| 216 | + # Treat as a single Normal distrbution if outside of the calibration range |
| 217 | + pdf(Normal(d.μ, d.σ), x) |
| 218 | + end |
| 219 | + end |
208 | 220 | @inline function Distributions.logpdf(d::Radiocarbon{T}, x::Real) where {T}
|
209 | 221 | if firstindex(d.ldist) <= x <= lastindex(d.ldist)
|
210 | 222 | linterp_at_index(d.ldist, x, -maxintfloat(T))
|
|
236 | 248 | Distributions.mean(d::Radiocarbon) = d.μ
|
237 | 249 | Distributions.var(d::Radiocarbon) = d.σ * d.σ
|
238 | 250 | Distributions.std(d::Radiocarbon) = d.σ
|
| 251 | + Distributions.skewness(d::Radiocarbon) = histskewness(d.dist, eachindex(d.dist), corrected=false) |
| 252 | + Distributions.kurtosis(d::Radiocarbon) = histkurtosis(d.dist, eachindex(d.dist), corrected=false) |
239 | 253 |
|
240 | 254 | ## ---
|
241 | 255 |
|
|
0 commit comments