Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve selectors #21

Merged
merged 4 commits into from
Dec 4, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
more tests for selectors
  • Loading branch information
rafaqz committed Dec 4, 2019
commit 0849b95df9422ce024e5953abfd157258283671b
2 changes: 1 addition & 1 deletion src/DimensionalData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ std(a; dims=Y())
"""
module DimensionalData

using ConstructionBase, LinearAlgebra, RecipesBase, Statistics
using ConstructionBase, LinearAlgebra, RecipesBase, Statistics, Dates

using Base: tail, OneTo

Expand Down
8 changes: 6 additions & 2 deletions src/grid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ struct Ordered{D,A} <: Order
end
Ordered() = Ordered(Forward(), Forward())

indexorder(order::Ordered) = order.index
arrayorder(order::Ordered) = order.array

"""
Trait indicating that the array or dimension has no order.
"""
struct Unordered <: Order end

indexorder(order::Ordered) = order.index
indexorder(order::Unordered) = Unordered()
arrayorder(order::Ordered) = order.array
arrayorder(order::Unordered) = Unordered()

"""
Expand All @@ -54,6 +55,9 @@ reverseindex(o::Ordered) = Ordered(reverse(indexorder(o)), arrayorder(o))
reversearray(o::Unordered) = Unordered()
reversearray(o::Ordered) = Ordered(indexorder(o), reverse(arrayorder(o)))

isrev(::Forward) = false
isrev(::Reverse) = true


"""
Indicates wether the cell value is specific to the locus point
Expand Down
111 changes: 67 additions & 44 deletions src/selector.jl
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
"""
Selectors indicate that index values are not indices, but points to
Selectors indicate that index values are not indices, but points to
be selected from the dimension values, such as DateTime objects on a Time dimension.
"""
abstract type Selector{T} end

val(m::Selector) = m.val

(::Type{T})(args...) where T <: Selector = T{typeof(args)}(args)
val(m::Selector) = m.val

"""
At(x)
Selector that exactly matches the value on the passed-in dimensions, or throws an error.
For ranges and arrays, every value must match an existing value - not just the end points.
Selector that exactly matches the value on the passed-in dimensions, or throws an error.
For ranges and arrays, every value must match an existing value - not just the end points.
"""
struct At{T} <: Selector{T}
struct At{T,A,R} <: Selector{T}
val::T
atol::A
rtol::R
end
At(val::Union{Number,AbstractArray{<:Number},Tuple{<:Number,Vararg}};
atol=zero(eltype(val)),
rtol=(atol > zero(eltype(val)) ? zero(rtol) : Base.rtoldefault(eltype(val)))
) = At{typeof.((val, atol, rtol))...}(val, atol, rtol)
At(val; atol=nothing, rtol=nothing) =
At{typeof.((val, atol, rtol))...}(val, atol, rtol)

atol(sel::At) = sel.atol
rtol(sel::At) = sel.rtol

"""
Near(x)
Expand All @@ -35,35 +44,38 @@ Selector that retreive all indices located between 2 values.
struct Between{T<:Union{Tuple{Any,Any},Nothing}} <: Selector{T}
val::T
end
Between(args...) = Between{typeof(args)}(args)
Between(x::Tuple) = Between{typeof(x)}(x)

# Get the dims in the same order as the grid
# This would be called after RegularGrid and/or CategoricalGrid
# dimensions are removed
dims2indices(grid::TransformedGrid, dims::Tuple, lookups::Tuple, emptyval) =
dims2indices(grid::TransformedGrid, dims::Tuple, lookups::Tuple, emptyval) =
sel2indices(grid, dims, map(val, permutedims(dimz, dims(grid))))

sel2indices(A::AbstractArray, lookup) = sel2indices(dims(A), lookup)
sel2indices(dims::Tuple, lookup) = sel2indices(dims, (lookup,))
sel2indices(dims::Tuple, lookup::Tuple) = sel2indices(map(grid, dims), dims, lookup)
sel2indices(grids, dims::Tuple, lookup::Tuple) =
(sel2indices(grids[1], dims[1], lookup[1]),
(sel2indices(grids[1], dims[1], lookup[1]),
sel2indices(tail(grids), tail(dims), tail(lookup))...)
sel2indices(grids::Tuple{}, dims::Tuple{}, lookup::Tuple{}) = ()

# At selector
sel2indices(grid, dim::AbDim, sel::At) = at(dim, val(sel))
sel2indices(grid, dim::AbDim, sel::At{<:Tuple}) = [at.(Ref(dim), val(sel))...]
sel2indices(grid, dim::AbDim, sel::At{<:AbstractVector}) = at.(Ref(dim), val(sel))
sel2indices(grid, dim::AbDim, sel::At) = at(dim, sel)
sel2indices(grid, dim::AbDim, sel::At{<:Tuple}) =
[at.(Ref(dim), val(sel), atol(sel), rtol(sel))...]
sel2indices(grid, dim::AbDim, sel::At{<:AbstractVector}) =
at.(Ref(dim), val(sel), atol(sel), rtol(sel))

# Near selector
sel2indices(grid::T, dim::AbDim, sel::Near) where T<:Union{CategoricalGrid,UnknownGrid} =
sel2indices(grid::T, dim::AbDim, sel::Near) where T<:Union{CategoricalGrid,UnknownGrid} =
throw(ArgumentError("`Near` has no meaning in a `$T`. Use `At`"))
sel2indices(grid::AbstractAllignedGrid, dim::AbDim, sel::Near) =
sel2indices(grid::AbstractAllignedGrid, dim::AbDim, sel::Near) =
near(dim, val(sel))
sel2indices(grid::AbstractAllignedGrid, dim::AbDim, sel::Near{<:Tuple}) =
sel2indices(grid::AbstractAllignedGrid, dim::AbDim, sel::Near{<:Tuple}) =
[near.(Ref(dim), val(sel))...]
sel2indices(grid::AbstractAllignedGrid, dim::AbDim, sel::Near{<:AbstractVector}) =
sel2indices(grid::AbstractAllignedGrid, dim::AbDim, sel::Near{<:AbstractVector}) =
near.(Ref(dim), val(sel))

# Between selector
Expand All @@ -72,61 +84,72 @@ sel2indices(grid, dim::AbDim, sel::Between{<:Tuple}) = between(dim, val(sel))

# Transformed grid

# We use the transformation from the first TransformedGrid dim.
# We use the transformation from the first TransformedGrid dim.
# In practice the others could be empty.
sel2indices(grids::Tuple{Vararg{<:TransformedGrid}}, dims::AbDimTuple,
sel::Tuple{Vararg{<:Selector}}) =
sel2indices(grids::Tuple{Vararg{<:TransformedGrid}}, dims::AbDimTuple,
sel::Tuple{Vararg{<:Selector}}) =
map(to_int, sel, val(dims[1])([map(val, sel)...]))

to_int(::At, x) = convert(Int, x)
to_int(::Near, x) = round(Int, x)
to_int(::At, x) = convert(Int, x)
to_int(::Near, x) = round(Int, x)

# Do the input values need some kind of scalar conversion?
# Do the input values need some kind of scalar conversion?
# what is the scale of these lookup matrices?
sel2indices(grid::LookupGrid, sel::Tuple{Vararg{At}}) =
sel2indices(grid::LookupGrid, sel::Tuple{Vararg{At}}) =
lookup(grid)[map(val, sel)...]


at(dim::AbDim, selval) = at(val(dim), selval)
at(dim, selval) = begin
at(dim::AbDim, sel::At) = at(dim, val(sel), atol(sel), rtol(sel))
at(dim::AbDim, selval, atol::Nothing, rtol::Nothing) = begin
ind = findfirst(x -> x == selval, val(dim))
ind == nothing ? throw(ArgumentError("$selval not found in $dim")) : ind
end
at(dim::AbDim, selval, atol, rtol) = begin
# This is not particularly efficient.
# It should be separated out for unordered categorical
# It should be separated out for unordered
# dims and otherwise treated as an ordered list.
ind = findfirst(x -> x == selval, val(dim))
ind = findfirst(x -> isapprox(x, selval; atol=atol, rtol=rtol), val(dim))
ind == nothing ? throw(ArgumentError("$selval not found in $dim")) : ind
end

near(list, selval) = begin
list = val(list)
ind = searchsortedfirst(list, selval)
if ind <= firstindex(list)
ind
elseif abs(list[ind] - selval) < abs(list[ind-1] - selval)
ind

near(dim::AbDim, selval) =
near(indexorder(dim), dim::AbDim, selval)
near(indexorder::Unordered, dim, selval) =
throw(ArgumentError("`Near` has no meaning in an `Unordered` grid"))
near(indexorder, dim, selval) = begin
index = val(dim)
i = searchsortedfirst(index, selval; rev=isrev(indexorder))
if i > lastindex(index)
lastindex(index)
elseif i <= firstindex(index)
firstindex(index)
elseif abs(index[i] - selval) < abs(index[i-1] - selval)
i
else
ind - 1
i - 1
end
end

between(dim::AbDim, sel) = between(indexorder(dim), dim, sel)
between(::Unordered, dim::AbDim, sel) =
between(::Unordered, dim::AbDim, sel) =
throw(ArgumentError("Cannot use `Between` on an unordered dimension"))
between(::Forward, dim::AbDim, sel) =
between(::Forward, dim::AbDim, sel) =
rangeorder(dim, searchsortedfirst(val(dim), first(sel)), searchsortedlast(val(dim), last(sel)))
between(::Reverse, dim::AbDim, sel) =
rangeorder(dim, searchsortedlast(val(dim), last(sel); rev=true),
searchsortedfirst(val(dim), first(sel); rev=true))
between(::Reverse, dim::AbDim, sel) =
rangeorder(dim, searchsortedfirst(val(dim), last(sel); rev=true),
searchsortedlast(val(dim), first(sel); rev=true))

rangeorder(dim::AbDim, lower, upper) = rangeorder(arrayorder(dim), dim, lower, upper)
rangeorder(::Forward, dim::AbDim, lower, upper) = lower:upper
rangeorder(::Reverse, dim::AbDim, lower, upper) = length(val(dim)) - upper + 1:length(val(dim)) - lower + 1


# Selector indexing without dim wrappers. Must be in the right order!
Base.@propagate_inbounds Base.getindex(a::AbstractArray, I::Vararg{Selector}) =
getindex(a, sel2indices(a, I)...)
Base.@propagate_inbounds Base.setindex!(a::AbstractArray, x, I::Vararg{Selector}) =
Base.@propagate_inbounds Base.getindex(a::AbstractArray, I::Vararg{Selector}) =
getindex(a, sel2indices(a, I)...)
Base.@propagate_inbounds Base.setindex!(a::AbstractArray, x, I::Vararg{Selector}) =
setindex!(a, x, sel2indices(a, I)...)
Base.view(a::AbstractArray, I::Vararg{Selector}) =
Base.view(a::AbstractArray, I::Vararg{Selector}) =
view(a, sel2indices(a, I)...)

2 changes: 2 additions & 0 deletions test/benchmarks.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using DimensionalData, BenchmarkTools

#= Benchmarks

Test how much the recalculation of coordinates and dim types
Expand Down
4 changes: 4 additions & 0 deletions test/dimension.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
using DimensionalData, Test, Unitful

using DimensionalData: X, Y, Z, Time, Forward, @dim, slicedims

@dim TestDim "Test dimension"

@testset "dims creation macro" begin
Expand Down
4 changes: 4 additions & 0 deletions test/grid.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
using DimensionalData, Test, Unitful

using DimensionalData: X, Y, Z, Time, Forward

@testset "bounds" begin

end
Expand Down
4 changes: 4 additions & 0 deletions test/methods.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
using DimensionalData, Statistics, Test, Unitful, SparseArrays

using DimensionalData: X, Y, Z, Time

using LinearAlgebra: Transpose

@testset "dimension reducing methods" begin
Expand Down
5 changes: 5 additions & 0 deletions test/primitives.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
using DimensionalData, Test

using DimensionalData: val, basetypeof, slicedims, dims2indices, formatdims,
@dim, reducedims, dimnum, X, Y, Z, Time, Forward

dimz = (X(), Y())

@testset "permutedims" begin
Expand Down
5 changes: 0 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
using DimensionalData, Statistics, Test, BenchmarkTools, Unitful, SparseArrays

using DimensionalData: val, basetypeof, slicedims, dims2indices, formatdims,
@dim, reducedims, dimnum, X, Y, Z, Time, Forward

include("dimension.jl")
include("primitives.jl")
include("array.jl")
Expand Down
Loading