Skip to content

Commit

Permalink
Add CDAI and methods (#9)
Browse files Browse the repository at this point in the history
* resize logo to layout

* add cdai

* update README.md
  • Loading branch information
simonsteiger authored Aug 2, 2024
1 parent bae84e4 commit 0444866
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 68 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This package implements the most common composite scores used for evaluating dis
- DAS28ESR
- DAS28CRP
- SDAI
- CDAI
- ACR / EULAR Boolean remission
- Revised ACR / EULAR Boolean remission
- Three-item ACR / EULAR Boolean remission
Expand Down
8 changes: 5 additions & 3 deletions docs/logo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ plt3 = add_pie!(data3, [purple, green, red])

translate_all!(4.3, 3.7, plt1, plt2, plt3)

#hidedecorations!(ax)
#hidespines!(ax)
hidedecorations!(ax)
hidespines!(ax)

colsize!(f.layout, 1, Aspect(1, 1.0))

resize_to_layout!(f)

f

# Assuming that the active project is docs
# CairoMakie.save(joinpath("src", "assets", "logo.svg"), f)
CairoMakie.save(joinpath("src", "assets", "logo.svg"), f)
20 changes: 10 additions & 10 deletions docs/src/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/RheumaComposites.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export DAS28
export DAS28ESR
export DAS28CRP
export SDAI
export CDAI
export faceted, Faceted
export BooleanRemission
export revised, Revised, threeitem, ThreeItem
Expand All @@ -36,6 +37,7 @@ include("types/components.jl")
include("types/composites.jl")
include("types/das28.jl")
include("types/sdai.jl")
include("types/cdai.jl")
include("types/boolean.jl")
include("types/modified.jl")
include("utils/weight.jl")
Expand Down
50 changes: 50 additions & 0 deletions src/types/cdai.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""
CDAI(; t28, s28, pga, ega)
Store component measures of the Clinical Disease Activity Index, or CDAI.
# Components
- `t28` 28 tender joint count
- `s28` 28 swollen joint count
- `pga` patient's global assessment
- `ega` evaluator's global assessment
!!! warning "Units"
Currently, `pga` and `ega` must be a length (typically millimeters or centimeters).
See also [`Unitful.@u_str`](@extref).
# External links
* [CDAI calculator](https://www.mdcalc.com/calc/2177/clinical-disease-activity-index-cdai-rheumatoid-arthritis)
See also [`score`](@ref), [`isremission`](@ref).
"""
struct CDAI <: ContinuousComposite
t28::Int64
s28::Int64
pga::Unitful.AbstractQuantity
ega::Unitful.AbstractQuantity
function CDAI(;
t28,
s28,
pga::Unitful.AbstractQuantity,
ega::Unitful.AbstractQuantity,
)
valid_joints.([t28, s28])
valid_vas.([pga, ega])

# Must convert because weights do not adjust to measurement
return new(
t28,
s28,
uconvert(units.xdai_vas, pga),
uconvert(units.xdai_vas, ega),
)
end
end

WeightingScheme(::Type{<:CDAI}) = IsUnweighted()

"Return the evaluator's global assessment."
ega(x::CDAI) = x.ega
6 changes: 3 additions & 3 deletions src/types/sdai.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ struct SDAI <: ContinuousComposite
return new(
t28,
s28,
uconvert(units.sdai_vas, pga),
uconvert(units.sdai_vas, ega),
uconvert(units.sdai_crp, crp)
uconvert(units.xdai_vas, pga),
uconvert(units.xdai_vas, ega),
uconvert(units.xdai_crp, crp)
)
end
end
Expand Down
8 changes: 8 additions & 0 deletions src/utils/categorise.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ function categorise(::Type{SDAI}, v)
return out
end

function categorise(::Type{CDAI}, v)
out = v < 2.8 ? "Remission" :
v <= 10.0 ? "Low" :
v <= 22.0 ? "Moderate" :
"High"
return out
end

categorise(x::ContinuousComposite) = categorise(typeof(x), score(x))

categorise(x::Faceted{<:ContinuousComposite}) = categorise(typeof(x.c0), score(x.c0))
64 changes: 14 additions & 50 deletions src/utils/remission.jl
Original file line number Diff line number Diff line change
@@ -1,75 +1,39 @@
"""
isremission(x::DAS28ESR)
isremission(x::AbstractComposite)
Check whether a DAS28ESR fulfils remission criteria (< 2.4).
Check whether a composite fulfils remission criteria.
# Examples
```jldoctest
julia> DAS28ESR(t28=4, s28=5, pga=44u"mm", apr=23u"mm/hr") |> isremission
false
```
isremission(x::DAS28CRP)
Check whether a DAS28CRP fulfils remission criteria (< 2.6).
# Examples
```jldoctest
julia> DAS28CRP(t28=0, s28=1, pga=12u"mm", apr=9u"mg/L") |> isremission
true
```
isremission(x::SDAI)
Check whether an SDAI fulfils remission criteria (<= 3.3).
# Examples
```jldoctest
julia> SDAI(t28=0, s28=0, pga=12u"mm", ega=8u"mm", crp=4u"mg/L") |> isremission
true
```
isremission(x::BooleanRemission)
Check whether a BooleanRemission fulfils remission criteria (all T28, S28, PGA, CRP <= 1), where PGA is measured in cm and CRP measured in mg/dL.
See also [`revised`](@ref), [`threeitem`](@ref).
# Examples
```jldoctest
julia> BooleanRemission(t28=1, s28=0, pga=14u"mm", crp=0.4u"mg/dl") |>
isremission
false
julia> BooleanRemission(t28=1, s28=0, pga=14u"mm", crp=0.4u"mg/dl") |>
revised |>
isremission
true
julia> BooleanRemission(t28=1, s28=0, pga=14u"mm", crp=0.4u"mg/dl") |>
threeitem |>
isremission
true
```
"""
isremission(x::DAS28ESR) = score(x) < 2.6
isremission(x::DAS28CRP) = score(x) < 2.4
isremission(::Type{DAS28ESR}, x) = score(x) < 2.6
isremission(::Type{DAS28CRP}, x) = score(x) < 2.4

isremission(x::SDAI) = score(x) <= 3.3
isremission(::Type{SDAI}, x) = score(x) <= 3.3
isremission(::Type{CDAI}, x) = score(x) <= 2.8

isremission(x::PGA) = x.value <= 10.0u"mm"
isremission(x::SJC28) = x.value == 0
isremission(::Type{PGA}, x) = x.value <= 10.0u"mm"
isremission(::Type{SJC28}, x) = x.value == 0

function isremission(x::BooleanRemission)
function isremission(::Type{BooleanRemission}, x)
return t28(x) <= 1 && s28(x) <= 1 && pga(x) <= 10.0u"mm" && crp(x) <= 1.0u"mg/dL"
end

function isremission(x::Revised{<:BooleanRemission})
function isremission(::Type{<:Revised{<:BooleanRemission}}, x)
return t28(x) <= 1 && s28(x) <= 1 && pga(x) <= 20.0u"mm" && crp(x) <= 1.0u"mg/dL"
end

function isremission(x::ThreeItem{<:BooleanRemission})
function isremission(::Type{<:ThreeItem{<:BooleanRemission}}, x)
return t28(x) <= 1 && s28(x) <= 1 && crp(x) <= 1.0u"mg/dL"
end

isremission(x::AbstractComposite) = isremission(typeof(x), x)
isremission(x::AbstractComponent) = isremission(typeof(x), x)
5 changes: 3 additions & 2 deletions src/utils/units.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ units = (
das28_vas=u"mm",
das28_crp=u"mg/L",
das28_esr=u"mm/hr",
sdai_vas=u"cm",
sdai_crp=u"mg/dL",
# xdai is for both c- and sdai
xdai_vas=u"cm",
xdai_crp=u"mg/dL",
brem_vas=u"mm",
brem_crp=u"mg/dL",
)
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ end
include("types/sdai.jl")
end

@testset "CDAI" begin
include("types/cdai.jl")
end

@testset "Boolean" begin
include("types/boolean.jl")
end
40 changes: 40 additions & 0 deletions test/types/cdai.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Dummy CDAIs for testing
cdai = CDAI(t28=1, s28=0, pga=1u"cm", ega=0u"cm")
cdai_ref = 2.0

# Intercept
i_cdai = RheumaComposites.intercept(cdai)

# Test if different unit scales lead to same results
cdai_u1 = CDAI(t28=0, s28=1, pga=10u"mm", ega=10u"mm")
cdai_u2 = CDAI(t28=0, s28=1, pga=1u"cm", ega=1u"cm")

@testset "Construct CDAI" begin
@test cdai isa AbstractComposite
@test cdai isa ContinuousComposite
@test cdai isa CDAI
@test t28(cdai) isa Real
@test s28(cdai) isa Real
@test pga(cdai) isa Unitful.AbstractQuantity
@test ega(cdai) isa Unitful.AbstractQuantity
end

@testset "Score CDAI" begin
@test i_cdai == 0.0
@test score(cdai) isa Float64
@test score(cdai) i_cdai + sum(weight(cdai)) atol = 1e-3
@test score(cdai) cdai_ref atol = 1e-2
@test score(cdai_u1) score(cdai_u2) atol = 1e-3
end

@testset "CDAI Remission" begin
@test isremission(cdai)
@test !isremission(CDAI(t28=3, s28=4, pga=4u"cm", ega=4u"cm"))
end

@testset "Categorise CDAI" begin
@test categorise(cdai) == "Remission"
@test categorise.(CDAI, [2.79, 2.81]) == ["Remission", "Low"]
@test categorise.(CDAI, [9.99, 10.01]) == ["Low", "Moderate"]
@test categorise.(CDAI, [21.99, 22.01]) == ["Moderate", "High"]
end

0 comments on commit 0444866

Please sign in to comment.