diff --git a/src/DimensionalData.jl b/src/DimensionalData.jl index 89506640f..809e26564 100644 --- a/src/DimensionalData.jl +++ b/src/DimensionalData.jl @@ -57,7 +57,7 @@ export AbstractDimStack, DimStack export AbstractDimTable, DimTable -export DimIndices, DimKeys, DimPoints +export DimIndices, DimKeys, DimPoints, DimSelectors # getter methods export dims, refdims, metadata, name, lookup, bounds diff --git a/src/LookupArrays/lookup_arrays.jl b/src/LookupArrays/lookup_arrays.jl index 01a6de102..bce283374 100644 --- a/src/LookupArrays/lookup_arrays.jl +++ b/src/LookupArrays/lookup_arrays.jl @@ -187,7 +187,7 @@ for f in (:getindex, :view, :dotview) end function Adapt.adapt_structure(to, l::AbstractSampled) - rebuild(l; data=Adapt.adapt(to, parent(l)), metadata=NoMetadata()) + rebuild(l; data=Adapt.adapt(to, parent(l)), metadata=NoMetadata(), span=Adapt.adapt(to, span(l))) end # bounds diff --git a/src/LookupArrays/lookup_traits.jl b/src/LookupArrays/lookup_traits.jl index fcf220ee1..8778a44f4 100644 --- a/src/LookupArrays/lookup_traits.jl +++ b/src/LookupArrays/lookup_traits.jl @@ -187,6 +187,8 @@ abstract type Span <: LookupArrayTrait end struct NoSpan <: Span end +Adapt.adapt_structure(to, s::Span) = s + """ AutoSpan <: Span @@ -255,6 +257,8 @@ Explicit() = Explicit(AutoBounds()) val(span::Explicit) = span.val Base.:(==)(l1::Explicit, l2::Explicit) = val(l1) == val(l2) +Adapt.adapt_structure(to, s::Explicit) = Explicit(Adapt.adapt_structure(to, val(s))) + """ AutoIndex diff --git a/src/LookupArrays/selector.jl b/src/LookupArrays/selector.jl index c7ee0e3d2..e9356728f 100644 --- a/src/LookupArrays/selector.jl +++ b/src/LookupArrays/selector.jl @@ -1,3 +1,18 @@ +struct SelectorError{L,S} <: Exception + lookup::L + selector::S +end + +function Base.showerror(io::IO, ex::SelectorError) + if order(ex.lookup) isa Ordered + println(io, "SelectorError: attempt to select $(ex.selector) from lookup $(typeof(ex.lookup)) with bounds $(bounds(ex.lookup))") + else + println(io, "SelectorError: attempt to select $(ex.selector) from lookup $(typeof(ex.lookup)) with values $(ex.lookup)") + end +end +Base.showerror(io::IO, ex::SelectorError{<:Categorical}) = + println(io, "SelectorError: attempt to select $(ex.selector) from lookup $(typeof(ex.lookup)) with categories $(ex.lookup)") + """ Selector @@ -117,7 +132,7 @@ function at(lookup::NoLookup, sel::At; kw...) at >= 0.5 && error("atol must be small than 0.5 for NoLookup") isapprox(v, r; atol=at) || _selvalnotfound(lookup, v) end - r in lookup || throw(BoundsError(lookup, r)) + r in lookup || throw(SelectorError(lookup, sel)) return r end function at(lookup::LookupArray, sel::At; kw...) @@ -293,7 +308,7 @@ selectindices(l::LookupArray, sel::Contains{<:AbstractVector}) = _selectvec(l, s function contains(l::NoLookup, sel::Contains; kw...) i = Int(val(sel)) - i in l || throw(BoundsError(l, i)) + i in l || throw(SelectorError(l, i)) return i end contains(l::LookupArray, sel::Contains; kw...) = contains(sampling(l), l, sel; kw...) @@ -307,7 +322,7 @@ function contains(::Points, l::LookupArray, sel::Contains; kw...) end # Intervals ----------------------------------- function contains(sampling::Intervals, l::LookupArray, sel::Contains; err=_True()) - _locus_checkbounds(locus(l), l, sel) || return _boundserror_or_nothing(err) + _locus_checkbounds(locus(l), l, sel) || return _boundserror_or_nothing(err, l, sel) contains(order(l), span(l), sampling, locus(l), l, sel; err) end # Regular Intervals --------------------------- @@ -377,8 +392,8 @@ function contains( return i end -_boundserror_or_nothing(err::_True) = throw(BoundsError()) -_boundserror_or_nothing(err::_False) = nothing +_boundserror_or_nothing(err::_True, l, i) = throw(SelectorError(l, i)) +_boundserror_or_nothing(err::_False, l, i) = nothing _notcontained_or_nothing(err::_True, selval) = _notcontainederror(selval) _notcontained_or_nothing(err::_False, selval) = nothing diff --git a/src/dimindices.jl b/src/dimindices.jl index c4a2108e6..7ecd240f9 100644 --- a/src/dimindices.jl +++ b/src/dimindices.jl @@ -1,7 +1,22 @@ +abstract type AbstractDimIndices{T,N,D} <: AbstractDimArray{T,N,D,AbstractArray{T,N}} end -abstract type AbstractDimIndices{T,N} <: AbstractArray{T,N} end +# We need to be able to return a non-DimArray from `parent` +# to prevent stack overflows in dispatch. so we just wrap. +struct ParentWrapper{T,N,A<:AbstractDimIndices{T,N}} <: AbstractArray{T,N} + child::A +end + +Base.size(A::ParentWrapper) = size(A.child) +Base.getindex(A::ParentWrapper, I::Int...) = getindex(A.child, I...) dims(di::AbstractDimIndices) = di.dims +refdims(A::AbstractDimIndices) = () +data(A::AbstractDimIndices) = ParentWrapper(A) +metadata(A::AbstractDimIndices) = NoMetadata() +name(A::AbstractDimIndices) = Symbol("") + +rebuild(di::AbstractDimIndices; kw...) = rebuild(DimArray(di); kw...) +rebuild(di::AbstractDimIndices, args...) = rebuild(DimArray(di), args...) Base.size(di::AbstractDimIndices) = map(length, dims(di)) Base.axes(di::AbstractDimIndices) = map(d -> axes(d, 1), dims(di)) @@ -27,7 +42,7 @@ function _format(dims::Tuple) end """ - DimIndices <: AbstractArray + DimIndices <: AbstractDimArray DimIndices(x) DimIndices(dims::Tuple) @@ -40,7 +55,7 @@ This can be used to view/index into arbitrary dimensions over an array, and is especially useful when combined with `otherdims`, to iterate over the indices of unknown dimension. """ -struct DimIndices{T,N,D<:Tuple{Vararg{Dimension}}} <: AbstractDimIndices{T,N} +struct DimIndices{T,N,D<:Tuple{Vararg{Dimension}}} <: AbstractDimIndices{T,N,D} dims::D # Manual inner constructor for ambiguity only function DimIndices{T,N,D}(dims::Tuple{Vararg{Dimension}}) where {T,N,D<:Tuple{Vararg{Dimension}}} @@ -53,6 +68,11 @@ function DimIndices(dims::D) where {D<:Tuple{Vararg{Dimension}}} dims = N > 0 ? _format(dims) : dims DimIndices{T,N,typeof(dims)}(dims) end +function DimIndices(dims::NamedTuple{K}) where K + DimIndices(map((d, v) -> rebuild(d, v), key2dim(K), values(dims))) +end + +name(A::DimIndices) = :indices function Base.getindex(di::DimIndices, i1::Int, i2::Int, I::Int...) map(dims(di), (i1, i2, I...)) do d, i @@ -70,9 +90,8 @@ function Base.getindex(di::DimIndices{<:Any,N}, i::Int) where N end end - """ - DimPoints <: AbstractArray + DimPoints <: AbstractDimArray DimPoints(x; order) DimPoints(dims::Tuple; order) @@ -89,7 +108,7 @@ Either a `Dimension`, a `Tuple` of `Dimension` or an object that defines a - `order`: determines the order of the points, the same as the order of `dims` by default. """ -struct DimPoints{T,N,D<:DimTuple,O} <: AbstractDimIndices{T,N} +struct DimPoints{T,N,D<:DimTuple,O} <: AbstractDimIndices{T,N,D} dims::D order::O end @@ -100,6 +119,11 @@ function DimPoints(dims::DimTuple; order=dims) dims = N > 0 ? _format(dims) : dims DimPoints{T,N,typeof(dims),typeof(order)}(dims, order) end +function DimPoints(dims::NamedTuple{K}) where K + DimPoints(map((d, v) -> rebuild(d, v), key2dim(K), values(dims))) +end + +name(A::DimPoints) = :points function Base.getindex(dp::DimPoints, i1::Int, i2::Int, I::Int...) # Get dim-wrapped point values at i1, I... @@ -113,60 +137,76 @@ Base.getindex(di::DimPoints{<:Any,1}, i::Int) = (dims(di, 1)[i],) Base.getindex(di::DimPoints, i::Int) = di[Tuple(CartesianIndices(di)[i])...] """ - DimKeys <: AbstractArray + DimSelectors <: AbstractArray - DimKeys(x) - DimKeys(dims::Tuple) - DimKeys(dims::Dimension) + DimSelectors(x) + DimSelectors(dims::Tuple) + DimSelectors(dims::Dimension) Like `CartesianIndices`, but for the lookup values of Dimensions. Behaves as an `Array` of `Tuple` of `Dimension(At(lookupvalue))` for all combinations of the lookup values of `dims`. """ -struct DimKeys{T,N,D<:Tuple{Dimension,Vararg{Dimension}},S} <: AbstractDimIndices{T,N} +struct DimSelectors{T,N,D<:Tuple{Dimension,Vararg{Dimension}},S} <: AbstractDimIndices{T,N,D} dims::D selectors::S end -function DimKeys(dims::DimTuple; atol=nothing, selectors=_selectors(dims, atol)) - DimKeys(dims, selectors) +function DimSelectors(dims::DimTuple; + type::Type{<:LookupArrays.IntSelector}=At, atol=nothing, selectors=_selectors(dims, type, atol) +) + DimSelectors(dims, selectors) end -function DimKeys(dims::DimTuple, selectors) +function DimSelectors(dims::DimTuple, selectors) T = typeof(map(rebuild, dims, selectors)) N = length(dims) dims = N > 0 ? _format(dims) : dims - DimKeys{T,N,typeof(dims),typeof(selectors)}(dims, selectors) + DimSelectors{T,N,typeof(dims),typeof(selectors)}(dims, selectors) +end +function DimSelectors(dims::NamedTuple{K}; kw...) where K + DimSelectors(map((d, v) -> rebuild(d, v), key2dim(K), values(dims)); kw...) end -function _selectors(dims, atol) +name(A::DimSelectors) = :selectors + +@deprecate DimKeys DimSelectors + +function _selectors(dims, type, atol) map(dims) do d atol1 = _atol(eltype(d), atol) At{eltype(d),typeof(atol1),Nothing}(first(d), atol1, nothing) end end -function _selectors(dims, atol::Tuple) +function _selectors(dims, type, atol::Tuple) map(dims, atol) do d, a atol1 = _atol(eltype(d), a) At{eltype(d),typeof(atol1),Nothing}(first(d), atol1, nothing) end end -function _selectors(dims, atol::Nothing) +function _selectors(dims, type, atol::Nothing) map(dims) do d - atolx = _atol(eltype(d), nothing) + atol = _atol(eltype(d), nothing) v = first(val(d)) - At{typeof(v),typeof(atolx),Nothing}(v, atolx, nothing) + _construct_selector(type, v, atol) end end +_construct_selector(::Type{At}, v, atol) = + At{typeof(v),typeof(atol),Nothing}(v, atol, nothing) +_construct_selector(::Type{Near}, v, atol) = + Near{typeof(v)}(v) +_construct_selector(::Type{Contains}, v, atol) = + Contains{typeof(v)}(v) + _atol(::Type, atol) = atol _atol(T::Type{<:AbstractFloat}, atol::Nothing) = eps(T) -function Base.getindex(di::DimKeys, i1::Int, i2::Int, I::Int...) +function Base.getindex(di::DimSelectors, i1::Int, i2::Int, I::Int...) map(dims(di), di.selectors, (i1, i2, I...)) do d, s, i rebuild(d, rebuild(s; val=d[i])) # At selector with the value at i end end -function Base.getindex(di::DimKeys{<:Any,1}, i::Int) +function Base.getindex(di::DimSelectors{<:Any,1}, i::Int) d = dims(di, 1) (rebuild(d, rebuild(di.selectors[1]; val=d[i])),) end -Base.getindex(di::DimKeys, i::Int) = di[Tuple(CartesianIndices(di)[i])...] +Base.getindex(di::DimSelectors, i::Int) = di[Tuple(CartesianIndices(di)[i])...]