diff --git a/docs/src/api.md b/docs/src/api.md index c40c43312..a27cbb28c 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -53,7 +53,7 @@ Categorical Unaligned Transformed NoIndex -AutoIndex +Auto ``` Order of arrays and indices: diff --git a/src/DimensionalData.jl b/src/DimensionalData.jl index 66cada97c..4e024bbcb 100644 --- a/src/DimensionalData.jl +++ b/src/DimensionalData.jl @@ -28,7 +28,7 @@ export Sampling, Points, Intervals export Span, Regular, Irregular, AutoSpan -export IndexMode, AutoIndex, UnknownIndex, NoIndex +export IndexMode, Auto, UnknownIndex, NoIndex export Aligned, AbstractSampled, Sampled, AbstractCategorical, Categorical diff --git a/src/dimension.jl b/src/dimension.jl index cf8cc2694..240811364 100644 --- a/src/dimension.jl +++ b/src/dimension.jl @@ -1,23 +1,56 @@ """ -Dimensions tag the dimensions of an AbstractArray, or other dimensional data. +Dimension is the abstract supertype of all dimension types. -It can also contain spatial coordinates and their metadata. For simplicity, -the same types are used both for storing dimension information and for indexing. +Example concrete implementations are `X`, `Y`, `Z`, +`Ti` (Time), and the arbirary `Dim{:custom}` dimension. + +`Dimension`s label the axes of an `AbstractDimesnionalArray`, +or other dimensional data. They may also provide an alternate index +to lookup for each array axis. + + +Example: +```julia +using Dates +x = X(2:2:10) +y = Y(['a', 'b', 'c']) +ti = Ti(DateTime(2021, 1):Month(1):DateTime(2021, 12)) +``` + +```julia +A = DimensionalArray(rand(3, 5, 12), (y, x, ti)); +``` + +For simplicity, the same `Dimension` types are also used as wrappers +in `getindex`, like: + +```julia +x = A[X(2), Y(3)] +``` + +Dimension can also wrap [`Selectors`](@ref). + +```julia +x = A[X(Between(3, 4)), Y(At('b'))] +``` + +`Dimension` objects may have [`mode`](@ref) and [`metadata`](@ref) fields +to track additional information about the data and the index, and their relationship. """ abstract type Dimension{T,IM,M} end """ -Abstract supertype for independent dimensions. Will plot on the X axis. +Abstract supertype for independent dimensions. Thise will plot on the X axis. """ abstract type IndependentDim{T,IM,M} <: Dimension{T,IM,M} end """ -Abstract supertype for Dependent dimensions. Will plot on the Y axis. +Abstract supertype for Dependent dimensions. These will plot on the Y axis. """ abstract type DependentDim{T,IM,M} <: Dimension{T,IM,M} end """ -Abstract parent type for all X dimensions. +Abstract parent type for all X dimensions. """ abstract type XDim{T,IM,M} <: IndependentDim{T,IM,M} end @@ -33,7 +66,11 @@ abstract type ZDim{T,IM,M} <: Dimension{T,IM,M} end """ Abstract parent type for all time dimensions. -""" + +For an index with `Interval` sampling the locus will automatically be +set to `Start()`, as a date/time index generally defines the start of a +month, second etc, not the central point as is more common with spatial data. +`""" abstract type TimeDim{T,IM,M} <: IndependentDim{T,IM,M} end ConstructionBase.constructorof(d::Type{<:Dimension}) = basetypeof(d) @@ -130,10 +167,17 @@ Dimensions with user-set type paremeters abstract type ParametricDimension{X,T,IM,M} <: Dimension{T,IM,M} end """ + Dim{X}(val, mode, metadata) + Dim{X}(val=:; [mode=Auto()], [metadata=nothing]) + A generic dimension. For use when custom dims are required when loading data from a file. The sintax is ugly and verbose to use for indexing, ie `Dim{:lat}(1:9)` rather than `Lat(1:9)`. This is the main reason they are not the only type of dimension availabile. + +```julia +dim = Dim{:custom}(['a', 'b', 'c']) +``` """ struct Dim{X,T,IM<:IndexMode,M} <: ParametricDimension{X,T,IM,M} val::T @@ -143,13 +187,15 @@ struct Dim{X,T,IM<:IndexMode,M} <: ParametricDimension{X,T,IM,M} new{X,typeof(val),typeof(mode),typeof(metadata)}(val, mode, metadata) end -Dim{X}(val=:; mode=AutoIndex(), metadata=nothing) where X = +Dim{X}(val=:; mode=Auto(), metadata=nothing) where X = Dim{X}(val, mode, metadata) name(::Type{<:Dim{X}}) where X = "Dim $X" shortname(::Type{<:Dim{X}}) where X = "$X" basetypeof(::Type{<:Dim{X}}) where {X} = Dim{X} """ + AnonDim() + Anonymous dimension. Used when extra dimensions are created, such as during transpose of a vector. """ @@ -190,7 +236,7 @@ dimmacro(typ, supertype, name=string(typ), shortname=string(typ)) = mode::IM metadata::M end - $typ(val=:; mode=AutoIndex(), metadata=nothing) = + $typ(val=:; mode=Auto(), metadata=nothing) = $typ(val, mode, metadata) DimensionalData.name(::Type{<:$typ}) = $name DimensionalData.shortname(::Type{<:$typ}) = $shortname @@ -198,19 +244,45 @@ dimmacro(typ, supertype, name=string(typ), shortname=string(typ)) = # Define some common dimensions. @dim X XDim -@doc "X dimension. `X <: XDim <: IndependentDim`" X +@doc """ +X [`Dimension`](@ref). `X <: XDim <: IndependentDim` + +## Example: +```julia +x = X(2:2:10) +``` +""" X @dim Y YDim -@doc "Y dimension. `Y <: YDim <: DependentDim`" Y +@doc """ +Y [`Dimension`](@ref). `Y <: YDim <: DependentDim` + +## Example: +```julia +y = Y(['a', 'b', 'c']) +``` +""" Y @dim Z ZDim -@doc "Z dimension. `Z <: ZDim <: Dimension`" Z +@doc """ +Z [`Dimension`](@ref). `Z <: ZDim <: Dimension` + +## Example: +```julia +z = Z(10:10:100) +``` +""" Z @dim Ti TimeDim "Time" @doc """ -Time dimension. `Ti <: TimeDim <: IndependentDim` +Time [`Dimension`](@ref). `Ti <: TimeDim <: IndependentDim` `Time` is already used by Dates, so we use `Ti` to avoid clashing. + +## Example: +```julia +ti = Ti(DateTime(2021, 1):Month(1):DateTime(2021, 12)) +``` """ Ti # Time dimensions need to default to the Start() locus, as that is diff --git a/src/mode.jl b/src/mode.jl index 4f8088b22..aa451de7c 100644 --- a/src/mode.jl +++ b/src/mode.jl @@ -209,12 +209,12 @@ order(mode::NoIndex) = Ordered(Forward(), Forward(), Forward()) Automatic [`IndexMode`](@ref). Will be converted automatically to another `IndexMode` when possible. """ -struct AutoIndex{O<:Order} <: IndexMode +struct Auto{O<:Order} <: IndexMode order::O end -AutoIndex() = AutoIndex(AutoOrder()) +Auto() = Auto(AutoOrder()) -order(mode::AutoIndex) = mode.order +order(mode::Auto) = mode.order """ Supertype for [`IndexMode`](@ref) where the index is aligned with the array axes. @@ -388,9 +388,9 @@ identify(IM::Type{<:IndexMode}, dimtype::Type, index) = identify(IM(), dimtype, index) identify(mode::IndexMode, dimtype::Type, index) = mode -identify(mode::AutoIndex, dimtype::Type, index::AbstractArray) = +identify(mode::Auto, dimtype::Type, index::AbstractArray) = identify(Sampled(), dimtype, index) -identify(mode::AutoIndex, dimtype::Type, +identify(mode::Auto, dimtype::Type, index::AbstractArray{<:Union{AbstractChar,Symbol,AbstractString}}) = Categorical() identify(mode::AbstractCategorical, dimtype::Type, index) = mode diff --git a/test/dimension.jl b/test/dimension.jl index 667c153a6..1992d0a28 100644 --- a/test/dimension.jl +++ b/test/dimension.jl @@ -8,7 +8,7 @@ using DimensionalData: Forward, slicedims @test label(TestDim) == "Test dimension" @test shortname(TestDim) == "TestDim" @test val(TestDim(:test)) == :test - @test metadata(TestDim(1, AutoIndex(), "metadata")) == "metadata" + @test metadata(TestDim(1, Auto(), "metadata")) == "metadata" @test units(TestDim) == nothing @test label(TestDim) == "Test dimension" @test eltype(TestDim(1)) <: Int diff --git a/test/mode.jl b/test/mode.jl index 21a8c8902..deff87157 100644 --- a/test/mode.jl +++ b/test/mode.jl @@ -9,9 +9,9 @@ using DimensionalData: Forward, Reverse, @test identify(Sampled(sampling=Intervals()), Ti, 1:2:3) == Sampled(Ordered(), Regular(2), Intervals(Start())) - @test identify(AutoIndex(), X, 1:2:10) == + @test identify(Auto(), X, 1:2:10) == Sampled(Ordered(), Regular(2), Points()) - @test identify(AutoIndex(), X, [1, 2, 3, 4, 5]) == + @test identify(Auto(), X, [1, 2, 3, 4, 5]) == Sampled(Ordered(), Irregular(), Points()) @test identify(Sampled(), X, 1:2:10) == @@ -27,14 +27,14 @@ using DimensionalData: Forward, Reverse, @test identify(Sampled(order=Ordered(Reverse(), Forward(), Forward())), X, 10:-2:1) == Sampled(Ordered(Reverse(), Forward(), Forward()), Regular(-2), Points()) - @test identify(AutoIndex(), X, [:a, :b]) == Categorical() - @test identify(AutoIndex(), X, ["a", "b"]) == Categorical() - @test identify(AutoIndex(), X, ['a', 'b']) == Categorical() - @test identify(AutoIndex(), X, [1, 2, 3, 4]) == + @test identify(Auto(), X, [:a, :b]) == Categorical() + @test identify(Auto(), X, ["a", "b"]) == Categorical() + @test identify(Auto(), X, ['a', 'b']) == Categorical() + @test identify(Auto(), X, [1, 2, 3, 4]) == Sampled(span=Irregular()) - @test_broken identify(AutoIndex(AutoOrder()), X, [4, 3, 2, 1]) == + @test_broken identify(Auto(AutoOrder()), X, [4, 3, 2, 1]) == Sampled(Ordered(Reverse(), Forward(), Forward()), NoLocus()) - @test_broken identify(AutoIndex(AutoOrder()), X, [1, 3, 2, 9]) == + @test_broken identify(Auto(AutoOrder()), X, [1, 3, 2, 9]) == Sampled(Unordered(Forward(), NoLocus())) end