diff --git a/README.md b/README.md index eaf6fb5..d16befb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # MetidaBase.jl + +[![Tier 1](https://github.com/PharmCat/MetidaBase.jl/actions/workflows/Tier1.yml/badge.svg)](https://github.com/PharmCat/MetidaBase.jl/actions/workflows/Tier1.yml) + +[![codecov](https://codecov.io/gh/PharmCat/MetidaBase.jl/branch/main/graph/badge.svg?token=C6D0R654F9)](https://codecov.io/gh/PharmCat/MetidaBase.jl) + + Metida base package. Used for: diff --git a/src/MetidaBase.jl b/src/MetidaBase.jl index 5426cc6..1068074 100644 --- a/src/MetidaBase.jl +++ b/src/MetidaBase.jl @@ -11,10 +11,11 @@ module MetidaBase import StatsModels: StatisticalModel import Tables: istable, columnaccess, columns, getcolumn, columnnames - import Base: getindex, length, ht_keyindex, show, pushfirst!, iterate + import Base: getindex, length, ht_keyindex, show, pushfirst!, iterate, size include("abstracttype.jl") include("types.jl") include("utils.jl") + include("iterators.jl") end diff --git a/src/iterators.jl b/src/iterators.jl new file mode 100644 index 0000000..b8d7db4 --- /dev/null +++ b/src/iterators.jl @@ -0,0 +1,69 @@ +################################################################################ +struct SkipNonPositive{T} + x::T +end +skipnonpositive(itr) = SkipNonPositive(itr) + +Base.IteratorEltype(::Type{SkipNonPositive{T}}) where {T} = Base.IteratorEltype(T) +#Base.eltype(::Type{SkipNonPositive{T}}) where {T} = nonmissingtype(eltype(T)) +function Base.iterate(itr::SkipNonPositive, state...) + y = iterate(itr.x, state...) + y === nothing && return nothing + item, state = y + while !ispositive(item) + y = iterate(itr.x, state) + y === nothing && return nothing + item, state = y + end + item, state +end +Base.IndexStyle(::Type{<:SkipNonPositive{T}}) where {T} = IndexStyle(T) +Base.eachindex(itr::SkipNonPositive) = + Iterators.filter(i -> ispositive(@inbounds(itr.x[i])), eachindex(itr.x)) +Base.keys(itr::SkipNonPositive) = + Iterators.filter(i -> ispositive(@inbounds(itr.x[i])), keys(itr.x)) +Base.@propagate_inbounds function getindex(itr::SkipNonPositive, I...) + v = itr.x[I...] + !ispositive(v) && throw(ErrorException("the value at index $I is non positive")) + v +end +function Base.length(itr::SkipNonPositive) + n = 0 + for i in itr n+=1 end + n +end +################################################################################ +struct SkipNaNorMissing{T} + x::T +end +skipnanormissing(itr) = SkipNaNorMissing(itr) + +Base.IteratorEltype(::Type{SkipNaNorMissing{T}}) where {T} = Base.IteratorEltype(T) +#Base.eltype(::Type{SkipNaNorMissing{T}}) where {T} = nonmissingtype(eltype(T)) +function Base.iterate(itr::SkipNaNorMissing, state...) + y = iterate(itr.x, state...) + y === nothing && return nothing + item, state = y + while isnanormissing(item) + y = iterate(itr.x, state) + y === nothing && return nothing + item, state = y + end + item, state +end +Base.IndexStyle(::Type{<:SkipNaNorMissing{T}}) where {T} = IndexStyle(T) +Base.eachindex(itr::SkipNaNorMissing) = + Iterators.filter(i -> isnanormissing(@inbounds(itr.x[i])), eachindex(itr.x)) +Base.keys(itr::SkipNaNorMissing) = + Iterators.filter(i -> isnanormissing(@inbounds(itr.x[i])), keys(itr.x)) +Base.@propagate_inbounds function getindex(itr::SkipNaNorMissing, I...) + v = itr.x[I...] + !isnanormissing(v) && throw(ErrorException("The value at index $I is NaN or missing!")) + v +end +function Base.length(itr::SkipNaNorMissing) + n = 0 + for i in itr n+=1 end + n +end +################################################################################ diff --git a/src/types.jl b/src/types.jl index 0ab1087..a8dfbab 100644 --- a/src/types.jl +++ b/src/types.jl @@ -74,6 +74,16 @@ function Base.pushfirst!(t::MetidaTable, row::NamedTuple) t end +function Base.size(t::MetidaTable, i::Int) + if i == 1 + return length(t.table[1]) + elseif i == 2 + return length(t.table) + else + error("Wrong dimention!") + end +end + function Base.show(io::IO, table::MetidaTable) pretty_table(io, table; tf = PrettyTables.tf_compact) end diff --git a/src/utils.jl b/src/utils.jl index b5eb963..6c538a1 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -17,9 +17,19 @@ function sortbyvec!(a, vec) sort!(a, by = x -> findfirst(y -> x == y, vec)) end +################################################################################ +################################################################################ + isnanormissing(x::Number) = isnan(x) +isnanormissing(x::AbstractFloat) = isnan(x) isnanormissing(x::Missing) = true +ispositive(::Missing) = false +ispositive(x::AbstractFloat) = isnan(x) ? false : x > zero(x) +ispositive(x) = x > zero(x) + +################################################################################ +################################################################################ # STATISTICS diff --git a/test/runtests.jl b/test/runtests.jl index bf7382d..7199dab 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,6 +14,9 @@ using Test mt[:, :b] Base.show(io, mt) + @test size(mt, 1) == 5 + @test size(mt, 2) == 2 + struct ExampleIDStruct <: MetidaBase.AbstractSubject #time #obs @@ -51,6 +54,7 @@ using Test @test exrsds[:, :r1][1] == 3 @test exrsds[1, :r1] == 3 + @test MetidaBase.getid(exidds[3], :a) == 2 sort!(exidds, :a) @test MetidaBase.getid(exidds[3], :a) == 3 @@ -58,12 +62,22 @@ using Test sort!(exrsds, :a) + @test first(exrsds) == exrsds[1] + MetidaBase.uniqueidlist(exidds, [:a]) MetidaBase.uniqueidlist(exidds, :a) MetidaBase.subset(exidds, Dict(:a => 1)) - #println(exrsds[:, :r1]) - #println(MetidaBase.getid(exrsds, :, :a1)) + mt = MetidaBase.metida_table(exrsds) + v1 = [1,2,-6,missing,NaN] + itr1 = MetidaBase.skipnanormissing(v1) + for i in itr1 + @test !MetidaBase.isnanormissing(i) + end + itr2 = MetidaBase.skipnonpositive(v1) + for i in itr2 + @test MetidaBase.ispositive(i) + end end