Skip to content

Commit ec24946

Browse files
committed
Add skewness and kurtosis for Radiocarbon, use effective kurtosis
1 parent 9f668ab commit ec24946

File tree

3 files changed

+24
-8
lines changed

3 files changed

+24
-8
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Distributions = "0.15 - 0.25"
2323
Isoplot = "0.3"
2424
KernelDensity = "0.4 - 0.6"
2525
LsqFit = "0.7 - 0.13"
26-
NaNStatistics = "0.6.39"
26+
NaNStatistics = "0.6.40"
2727
Plots = "1"
2828
ProgressMeter = "1"
2929
QuadGK = "2"

src/Utilities.jl

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
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))
142142
Distributions.std(d::BilinearExponential; mean=mean(d)) = sqrt(var(d; mean))
143143
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
145145

146146
## Affine transformations
147147
Base.:+(d::BilinearExponential{T}, c::Real) where {T} = BilinearExponential{T}(d.A, d.μ + c, d.σ, d.shp, d.skw)
@@ -152,42 +152,46 @@
152152
struct Radiocarbon{T<:Real} <: ContinuousUnivariateDistribution
153153
μ::T
154154
σ::T
155+
dist::Vector{T}
155156
ldist::Vector{T}
156157
lcdist::Vector{T}
157158
lccdist::Vector{T}
158159
end
159160

160161
function Radiocarbon::T, σ::T, ldist::Vector{T}) where {T<:Real}
161162
# Ensure normalization
162-
normconst = sum(exp.(ldist)) * one(T)
163+
dist = exp.(ldist)
164+
normconst = sum(dist) * one(T)
165+
dist ./= normconst
163166
ldist .-= normconst
164167

165168
# Cumulative distributions
166169
lcdist = nanlogcumsumexp(ldist)
167170
lccdist = nanlogcumsumexp(ldist, reverse=true)
168171

169-
return Radiocarbon{T}(μ, σ, ldist, lcdist, lccdist)
172+
return Radiocarbon{T}(μ, σ, dist, ldist, lcdist, lccdist)
170173
end
171174

172175
function Radiocarbon(Age_14C::Real, Age_14C_sigma::Real, calibration::NamedTuple=intcal13)
173176
@assert calibration.Age_Calendar == 1:1:length(calibration.Age_14C)
174177
@assert step(calibration.Age_Calendar) == calibration.dt == 1
175178

176179
ldist = normlogproduct.(Age_14C, Age_14C_sigma, calibration.Age_14C, calibration.Age_sigma)
177-
dist = exp.(ldist)
178180

179-
# Normalize
181+
# Ensure normalization
182+
dist = exp.(ldist)
180183
normconst = sum(dist) * calibration.dt
181184
dist ./= normconst
182185
ldist .-= normconst
186+
183187
μ = histmean(dist, calibration.Age_Calendar)
184188
σ = histstd(dist, calibration.Age_Calendar, corrected=false)
185189

186190
# Cumulative distributions
187191
lcdist = nanlogcumsumexp(ldist)
188192
lccdist = nanlogcumsumexp(ldist, reverse=true)
189193

190-
return Radiocarbon(μ, σ, ldist, lcdist, lccdist)
194+
return Radiocarbon(μ, σ, dist, ldist, lcdist, lccdist)
191195
end
192196

193197
## Conversions
@@ -205,6 +209,14 @@
205209

206210
## Evaluation
207211
@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
208220
@inline function Distributions.logpdf(d::Radiocarbon{T}, x::Real) where {T}
209221
if firstindex(d.ldist) <= x <= lastindex(d.ldist)
210222
linterp_at_index(d.ldist, x, -maxintfloat(T))
@@ -236,6 +248,8 @@
236248
Distributions.mean(d::Radiocarbon) = d.μ
237249
Distributions.var(d::Radiocarbon) = d.σ * d.σ
238250
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)
239253

240254
## ---
241255

test/testUtilities.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
@test var(d) 0.02080666596420308^2 atol = 1e-6
2525
@test std(d) 0.02080666596420308 atol = 1e-6
2626
@test skewness(d) -0.16449904171203025 atol = 1e-6
27-
@test kurtosis(d) 3.0865922917156188 atol = 1e-6
27+
@test kurtosis(d) 0.0865922917156188 atol = 1e-6
2828

2929
## --- Test Radiocarbon distribution
3030

@@ -45,6 +45,8 @@
4545
@test mean(d) 927.2556461305643
4646
@test var(d) 7.5077650707247^2
4747
@test std(d) 7.5077650707247
48+
@test skewness(d) -4.6327184250788696
49+
@test kurtosis(d) 67.68860469654331
4850

4951
## --- Other utility functions for log likelihoods
5052

0 commit comments

Comments
 (0)