Skip to content

Commit

Permalink
Merge pull request #114 from rafaqz/metadata
Browse files Browse the repository at this point in the history
Metadata
  • Loading branch information
rafaqz authored Apr 3, 2020
2 parents cfb0826 + 43614cc commit fd749b3
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 45 deletions.
86 changes: 57 additions & 29 deletions src/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,38 @@ const StandardIndices = Union{AbstractArray,Colon,Integer}

# Interface methods ############################################################

dims(A::AbDimArray) = A.dims
@inline rebuild(A::AbstractArray, data, dims::Tuple=dims(A), refdims=refdims(A)) =
rebuild(A, data, dims, refdims, name(A))
# Rebuild for name-updating methods, to avoid having to add dims and refdims
@inline rebuild(A::AbstractArray, data, name::String) =
rebuild(A, data, dims(A), refdims(A), name)
"""
bounds(A::AbstractArray)
Returns a tuple of bounds for each array axis.
"""
bounds(A::AbstractArray) = bounds(dims(A))
bounds(A::AbstractArray, lookup::DimOrDimType) = bounds(dims(A), lookup)
"""
bounds(A::AbstractArray, dims)
Returns the bounds for the specified dimension(s).
`dims` can be a `Dimension`, a dimension type, or a tuple of either.
"""
bounds(A::AbstractArray, dims::DimOrDimType) =
bounds(DimensionalData.dims(A), dims)

"""
metadata(A::AbstractArray, dims)
Returns the bounds for the specified dimension(s).
`dims` can be a `Dimension`, a dimension type, or a tuple of either.
"""
metadata(A::AbstractDimensionalArray, dim) = metadata(dims(A, dim))
metadata(A::AbstractDimensionalArray, dims::Tuple) =
map(metadata, DimensionalData.dims(A, dims))

dims(A::AbDimArray) = A.dims
@inline rebuild(A::AbstractArray, data, dims::Tuple=dims(A), refdims=refdims(A),
name=name(A), metadata=metadata(A)) =
rebuild(A, data, dims, refdims, name, metadata)
# Rebuild for name-updating methods, to avoid having to add dims and refdims
@inline rebuild(A::AbstractArray, data, name::AbstractString) =
rebuild(A, data, dims(A), refdims(A), name, metadata)

# Array interface methods ######################################################

Expand Down Expand Up @@ -56,8 +79,7 @@ Base.copy!(dst::AbstractArray, src::AbDimArray) = copy!(dst, data(src))
Base.Array(A::AbDimArray) = data(A)

# Need to cover a few type signatures to avoid ambiguity with base
# Don't remove these even though they look redundant
Base.similar(A::AbDimArray) = rebuild(A, similar(data(A)), "")
# Don't remove these even though they look redundant Base.similar(A::AbDimArray) = rebuild(A, similar(data(A)), "")
Base.similar(A::AbDimArray, ::Type{T}) where T = rebuild(A, similar(data(A), T), "")
# If the shape changes, use the wrapped array:
Base.similar(A::AbDimArray, ::Type{T}, I::Tuple{Int,Vararg{Int}}) where T = similar(data(A), T, I)
Expand All @@ -73,64 +95,70 @@ The main subtype of `AbstractDimensionalArray`.
Maintains and updates its dimensions through transformations and moves dimensions to
`refdims` after reducing operations (like e.g. `mean`).
"""
struct DimensionalArray{T,N,D<:Tuple,R<:Tuple,A<:AbstractArray{T,N}} <: AbstractDimensionalArray{T,N,D,A}
struct DimensionalArray{T,N,D<:Tuple,R<:Tuple,A<:AbstractArray{T,N},Na<:AbstractString,Me} <: AbstractDimensionalArray{T,N,D,A}
data::A
dims::D
refdims::R
name::String
function DimensionalArray(data::A, dims::D, refdims::R, name::String) where A <:AbstractArray{T,N} where D where R where T where N
name::Na
metadata::Me
function DimensionalArray(data::A, dims::D, refdims::R, name::Na, metadata::Me
) where {D,R,A<:AbstractArray{T,N},Na,Me} where {T,N}
map(dims, size(data)) do d, s
if !(val(d) isa Colon) && length(d) != s
throw(DimensionMismatch(
"dims must have same size as data. This was not true for $dims and size $(size(data)) $(A)."
))
end
end
new{T,N,D,R,A}(data, dims, refdims, name)
new{T,N,D,R,A,Na,Me}(data, dims, refdims, name, metadata)
end
end
"""
DimensionalArray(data, dims::Tuple [, name::String]; refdims=())
Constructor with optional `name` and `refdims`.
The `name` is propagated across most sensible operations, even reducing ones.
DimensionalArray(data, dims::Tuple [, name::String]; refdims=(), metadata=nothing)
Constructor with optional `name`, and keyword `refdims` and `metadata`.
Example:
```julia
using Dates, DimensionalData
timespan = DateTime(2001):Month(1):DateTime(2001,12)
A = DimensionalArray(rand(12,10), (Ti(timespan), X(10:10:100)))
A[X<|Near([12, 35]), Ti<|At(DateTime(2001,5))]
A[Near(DateTime(2001, 5, 4)), Between(20, 50)]
ti = (Ti(DateTime(2001):Month(1):DateTime(2001,12)),
x = X(10:10:100))
A = DimensionalArray(rand(12,10), (ti, x), "example")
julia> A[X(Near([12, 35])), Ti(At(DateTime(2001,5)))];
julia> A[Near(DateTime(2001, 5, 4)), Between(20, 50)];
```
"""
DimensionalArray(A::AbstractArray, dims, name::String = ""; refdims=()) =
DimensionalArray(A, formatdims(A, _to_tuple(dims)), refdims, name)
DimensionalArray(A::AbstractArray, dims, name::String=""; refdims=(), metadata=nothing) =
DimensionalArray(A, formatdims(A, _to_tuple(dims)), refdims, name, metadata)
_to_tuple(t::T where T <: Tuple) = t
_to_tuple(t) = tuple(t)

# Getters
refdims(A::DimensionalArray) = A.refdims
data(A::DimensionalArray) = A.data
name(A::DimensionalArray) = A.name
metadata(A::DimensionalArray) = A.metadata
label(A::DimensionalArray) = name(A)

# DimensionalArray interface
# AbstractDimensionalArray interface
@inline rebuild(A::DimensionalArray, data::AbstractArray, dims::Tuple,
refdims::Tuple, name::String) =
DimensionalArray(data, dims, refdims, name)
refdims::Tuple, name::AbstractString, metadata) =
DimensionalArray(data, dims, refdims, name, metadata)

# Array interface (AbstractDimensionalArray takes care of everything else)
Base.@propagate_inbounds Base.setindex!(A::DimensionalArray, x, I::Vararg{StandardIndices}) =
setindex!(data(A), x, I...)

# Applying function across dimension should give an Array
"""
DimensionalArray(f::Function, dim::Dimension [, name])
Apply function `f` across the values of the dimension `dim`
(using broadcasting), and return the result as a dimensional array with
(using `broadcast`), and return the result as a dimensional array with
the given dimension. Optionally provide a name for the result.
"""
DimensionalArray(f::Function, dim::Dimension, name=string(nameof(f))*"("*name(dim)*")") =
DimensionalArray(f.(val(dim)), (dim,), name)
DimensionalArray(f::Function, dim::Dimension, name=string(nameof(f), "(", name(dim), ")")) =
DimensionalArray(f.(val(dim)), (dim,), name)

51 changes: 35 additions & 16 deletions test/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ using DimensionalData, Test, Unitful, OffsetArrays, SparseArrays
using DimensionalData: Start

a = [1 2; 3 4]
dimz = (X((143.0, 145.0)), Y((-38.0, -36.0)))
da = DimensionalArray(a, dimz)
dimz = (X((143.0, 145.0); metadata=Dict(:meta => "X")),
Y((-38.0, -36.0); metadata=Dict(:meta => "Y")))
da = DimensionalArray(a, dimz, "test"; metadata=Dict(:meta => "da"))

@testset "getindex for single integers returns values" begin
@test da[X(1), Y(2)] == 2
Expand All @@ -14,8 +15,12 @@ end
a = da[X(1:2), Y(1)]
@test a == [1, 3]
@test typeof(a) <: DimensionalArray{Int,1}
@test dims(a) == (X(LinRange(143.0, 145.0, 2); mode=Sampled(span=Regular(2.0))),)
@test refdims(a) == (Y(-38.0; mode=Sampled(span=Regular(2.0))),)
@test dims(a) == (X(LinRange(143.0, 145.0, 2);
mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "X")),)
@test refdims(a) == (Y(-38.0; mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "Y")),)
@test name(a) == "test"
@test metadata(a) == Dict(:meta => "da")
@test metadata(a, X) == Dict(:meta => "X")
@test bounds(a) == ((143.0, 145.0),)
@test bounds(a, X) == (143.0, 145.0)
# @test locus(mode(dims(da, X))) == Start()
Expand All @@ -24,8 +29,11 @@ end
@test a == [1, 2]
@test typeof(a) <: DimensionalArray{Int,1}
@test typeof(data(a)) <: Array{Int,1}
@test dims(a) == (Y(LinRange(-38.0, -36.0, 2); mode=Sampled(span=Regular(2.0))),)
@test refdims(a) == (X(143.0; mode=Sampled(span=Regular(2.0))),)
@test dims(a) == (Y(LinRange(-38.0, -36.0, 2);
mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "Y")),)
@test refdims(a) == (X(143.0; mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "X")),)
@test name(a) == "test"
@test metadata(a) == Dict(:meta => "da")
@test bounds(a) == ((-38.0, -36.0),)
@test bounds(a, Y()) == (-38.0, -36.0)

Expand All @@ -34,9 +42,12 @@ end
@test typeof(a) <: DimensionalArray{Int,2}
@test typeof(data(a)) <: Array{Int,2}
@test typeof(dims(a)) <: Tuple{<:X,<:Y}
@test dims(a) == (X(LinRange(143.0, 145.0, 2); mode=Sampled(span=Regular(2.0))),
Y(LinRange(-38.0, -36.0, 2); mode=Sampled(span=Regular(2.0))))
@test dims(a) == (X(LinRange(143.0, 145.0, 2);
mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "X")),
Y(LinRange(-38.0, -36.0, 2);
mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "Y")))
@test refdims(a) == ()
@test name(a) == "test"
@test bounds(a) == ((143.0, 145.0), (-38.0, -36.0))
@test bounds(a, X) == (143.0, 145.0)
end
Expand All @@ -48,34 +59,42 @@ end
@test typeof(data(v)) <:SubArray{Int,0}
@test typeof(dims(v)) == Tuple{}
@test dims(v) == ()
@test refdims(v) == (X(143.0; mode=Sampled(span=Regular(2.0))),
Y(-38.0; mode=Sampled(span=Regular(2.0))))
@test refdims(v) == (X(143.0; mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "X")),
Y(-38.0; mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "Y")))
@test name(v) == "test"
@test metadata(v) == Dict(:meta => "da")
@test bounds(v) == ()

v = view(da, Y(1), X(1:2))
@test v == [1, 3]
@test typeof(v) <: DimensionalArray{Int,1}
@test typeof(data(v)) <: SubArray{Int,1}
@test typeof(dims(v)) <: Tuple{<:X}
@test dims(v) == (X(LinRange(143.0, 145.0, 2); mode=Sampled(span=Regular(2.0))),)
@test refdims(v) == (Y(-38.0; mode=Sampled(span=Regular(2.0))),)
@test dims(v) == (X(LinRange(143.0, 145.0, 2);
mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "X")),)
@test refdims(v) == (Y(-38.0; mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "Y")),)
@test name(v) == "test"
@test metadata(v) == Dict(:meta => "da")
@test bounds(v) == ((143.0, 145.0),)

v = view(da, Y(1:2), X(1:1))
@test v == [1 2]
@test typeof(v) <: DimensionalArray{Int,2}
@test typeof(data(v)) <: SubArray{Int,2}
@test typeof(dims(v)) <: Tuple{<:X,<:Y}
@test dims(v) == (X(LinRange(143.0, 143.0, 1); mode=Sampled(span=Regular(2.0))),
Y(LinRange(-38, -36, 2); mode=Sampled(span=Regular(2.0))))
@test dims(v) == (X(LinRange(143.0, 143.0, 1);
mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "X")),
Y(LinRange(-38, -36, 2);
mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "Y")))
@test bounds(v) == ((143.0, 143.0), (-38.0, -36.0))

v = view(da, Y(Base.OneTo(2)), X(1))
@test v == [1, 2]
@test typeof(data(v)) <: SubArray{Int,1}
@test typeof(dims(v)) <: Tuple{<:Y}
@test dims(v) == (Y(LinRange(-38.0, -36.0, 2); mode=Sampled(span=Regular(2.0))),)
@test refdims(v) == (X(143.0; mode=Sampled(span=Regular(2.0))),)
@test dims(v) == (Y(LinRange(-38.0, -36.0, 2);
mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "Y")),)
@test refdims(v) == (X(143.0; mode=Sampled(span=Regular(2.0)), metadata=Dict(:meta => "X")),)
@test bounds(v) == ((-38.0, -36.0),)
end

Expand Down

0 comments on commit fd749b3

Please sign in to comment.