From e5d5d9bbacdc3d058581b564e4cdde46931e2c32 Mon Sep 17 00:00:00 2001 From: "Zachary P. Christensen" Date: Sat, 1 Aug 2020 10:38:30 -0400 Subject: [PATCH] Common way of setting metadata interface. --- Project.toml | 2 +- docs/src/metadata.md | 3 +- src/AxisIndices.jl | 14 ++++++ src/Interface/Interface.jl | 2 - src/Metadata/MetaAxis.jl | 4 -- src/Metadata/MetaAxisArray.jl | 4 -- src/Metadata/Metadata.jl | 13 +++++- src/Metadata/MetadataArray.jl | 71 ++++++++++++++++++++++++++++++ src/Metadata/NamedMetaAxisArray.jl | 4 -- src/Metadata/interface.jl | 25 ++++++++--- src/PrettyArrays/PrettyArrays.jl | 1 - test/runtests.jl | 3 +- 12 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 src/Metadata/MetadataArray.jl diff --git a/Project.toml b/Project.toml index 501f0910..30b9a0f7 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "AxisIndices" uuid = "f52c9ee2-1b1c-4fd8-8546-6350938c7f11" authors = ["Tokazama "] -version = "0.6.2" +version = "0.6.3" [deps] ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" diff --git a/docs/src/metadata.md b/docs/src/metadata.md index 2c0e3f66..3f1c3d99 100644 --- a/docs/src/metadata.md +++ b/docs/src/metadata.md @@ -2,6 +2,7 @@ ```@docs +Metadata.MetadataArray Metadata.MetaAxis Metadata.MetaAxisArray Metadata.MetaCartesianAxes @@ -19,4 +20,4 @@ Metadata.metadata Metadata.metaproperty Metadata.metaproperty! Metadata.metadata_type -``` \ No newline at end of file +``` diff --git a/src/AxisIndices.jl b/src/AxisIndices.jl index a2693d7f..751b5197 100644 --- a/src/AxisIndices.jl +++ b/src/AxisIndices.jl @@ -102,4 +102,18 @@ PrettyArrays.@assign_show NamedMetaCartesianAxes PrettyArrays.@assign_show NamedMetaLinearAxes +### +### Overload property methods for metadata +### + +Metadata.@metadata_properties MetaAxis + +Metadata.@metadata_properties MetadataArray + +Metadata.@metadata_properties NamedMetaLinearAxes + +Metadata.@metadata_properties NamedMetaCartesianAxes + +Metadata.@metadata_properties NamedMetaAxisArray + end diff --git a/src/Interface/Interface.jl b/src/Interface/Interface.jl index ab9a4e40..81452217 100644 --- a/src/Interface/Interface.jl +++ b/src/Interface/Interface.jl @@ -8,8 +8,6 @@ using StaticRanges using StaticRanges: OneToUnion using StaticRanges: resize_last -import MetadataArrays: MetadataArray - using MappedArrays: ReadonlyMultiMappedArray, MultiMappedArray, ReadonlyMappedArray using EllipsisNotation: Ellipsis using Base: @propagate_inbounds, OneTo, Fix2, tail, front diff --git a/src/Metadata/MetaAxis.jl b/src/Metadata/MetaAxis.jl index b9a05596..81837b88 100644 --- a/src/Metadata/MetaAxis.jl +++ b/src/Metadata/MetaAxis.jl @@ -59,10 +59,6 @@ function Interface.unsafe_reconstruct(axis::MetaAxis, ks) return MetaAxis(Interface.unsafe_reconstruct(parent(axis), ks), metadata(axis)) end -Base.getproperty(axis::MetaAxis, k::Symbol) = metaproperty(axis, k) - -Base.setproperty!(axis::MetaAxis, k::Symbol, val) = metaproperty!(axis, k, val) - """ meta(x) diff --git a/src/Metadata/MetaAxisArray.jl b/src/Metadata/MetaAxisArray.jl index 4e263f89..754f2426 100644 --- a/src/Metadata/MetaAxisArray.jl +++ b/src/Metadata/MetaAxisArray.jl @@ -55,8 +55,4 @@ function MetaAxisArray{T,N}(init::ArrayInitializer, axs::Tuple; metadata=nothing return MetaAxisArray(AxisArray{T,N}(init, axs); metadata=metadata, kwargs...) end -Base.getproperty(A::MetaAxisArray, k::Symbol) = metaproperty(A, k) - -Base.setproperty!(A::MetaAxisArray, k::Symbol, val) = metaproperty!(A, k, val) - Base.parent(A::MetaAxisArray) = getfield(A, :parent) diff --git a/src/Metadata/Metadata.jl b/src/Metadata/Metadata.jl index 88b01da9..ea732d85 100644 --- a/src/Metadata/Metadata.jl +++ b/src/Metadata/Metadata.jl @@ -22,6 +22,7 @@ using EllipsisNotation: Ellipsis using Base: @propagate_inbounds, Fix2 export + MetadataArray, MetaAxis, MetaAxisArray, MetaCartesianAxes, @@ -41,8 +42,8 @@ export metaproperty!, metadata_type -import MetadataArrays: MetadataArray +include("MetadataArray.jl") include("interface.jl") include("MetaAxis.jl") include("MetaCartesianAxes.jl") @@ -52,4 +53,14 @@ include("NamedMetaAxisArray.jl") include("NamedMetaCartesianAxes.jl") include("NamedMetaLinearAxes.jl") +macro metadata_properties(T) + quote + @inline Base.getproperty(x::$T, k::Symbol) = Metadata.metaproperty(x, k) + + @inline Base.setproperty!(x::$T, k::Symbol, val) = Metadata.metaproperty!(x, k, val) + + @inline Base.propertynames(x::$T) = Metadata.metanames(x) + end +end + end diff --git a/src/Metadata/MetadataArray.jl b/src/Metadata/MetadataArray.jl new file mode 100644 index 00000000..695e9c92 --- /dev/null +++ b/src/Metadata/MetadataArray.jl @@ -0,0 +1,71 @@ + +""" + MetadataArray(parent::AbstractArray, metadata) + +Custom `AbstractArray` object to store an `AbstractArray` `parent` as well as some `metadata`. + +# Examples + +```jldoctest metadataarray +julia> using AxisIndices + +julia> v = ["John", "John", "Jane", "Louise"]; + +julia> s = MetadataArray(v, Dict("John" => "Treatment", "Louise" => "Placebo", "Jane" => "Placebo")) +4-element MetadataArray{String,1,Dict{String,String},Array{String,1}}: + "John" + "John" + "Jane" + "Louise" + +julia> metadata(s) +Dict{String,String} with 3 entries: + "John" => "Treatment" + "Jane" => "Placebo" + "Louise" => "Placebo" +``` +""" +struct MetadataArray{T, N, M, A<:AbstractArray} <: AbstractArray{T, N} + parent::A + metadata::M +end + +Base.parent(A::MetadataArray) = getfield(A, :parent) + +function MetadataArray(v::AbstractArray{T, N}, m::M = ()) where {T, N, M} + return MetadataArray{T, N, M, typeof(v)}(v, m) +end + +""" + MetadataVector{T, M, S<:AbstractArray} + +Shorthand for `MetadataArray{T, 1, M, S}`. +""" +const MetadataVector{T, M, S<:AbstractArray} = MetadataArray{T, 1, M, S} + +MetadataVector(v::AbstractVector, n = ()) = MetadataArray(v, n) + +Base.size(s::MetadataArray) = Base.size(parent(s)) + +Base.axes(s::MetadataArray) = Base.axes(parent(s)) + +Base.IndexStyle(T::Type{<:MetadataArray}) = IndexStyle(parent_type(T)) + +@propagate_inbounds function Base.getindex(A::MetadataArray, args::Int...) + return getindex(parent(A), args...) +end + +Base.@propagate_inbounds function Base.setindex!(A::MetadataArray, val, args::Int...) + return setindex!(A, val, args...) +end + +ArrayInterface.parent_type(::Type{MetadataArray{T, M, N, A}}) where {T,M,N,A} = A + +function Base.similar(A::MetadataArray, ::Type{S}, dims::Dims) where S + return MetadataArray(similar(parent(A), S, dims), metadata(A)) +end + +function Base.reshape(s::MetadataArray, d::Dims) + return MetadataArray(reshape(parent(s), d), metadata(s)) +end + diff --git a/src/Metadata/NamedMetaAxisArray.jl b/src/Metadata/NamedMetaAxisArray.jl index 036ea23c..f563c64b 100644 --- a/src/Metadata/NamedMetaAxisArray.jl +++ b/src/Metadata/NamedMetaAxisArray.jl @@ -57,10 +57,6 @@ function NamedMetaAxisArray(A::AbstractArray, axs::NamedTuple{L}; metadata=nothi return NamedMetaAxisArray{L}(A, values(axs); metadata=metadata, kwargs...) end -Base.getproperty(A::NamedMetaAxisArray, k::Symbol) = metaproperty(A, k) - -Base.setproperty!(A::NamedMetaAxisArray, k::Symbol, val) = metaproperty!(A, k, val) - for f in (:getindex, :view, :dotview) @eval begin @propagate_inbounds function Base.$f(A::NamedMetaAxisArray; named_inds...) diff --git a/src/Metadata/interface.jl b/src/Metadata/interface.jl index 397c8b0d..b2f3776d 100644 --- a/src/Metadata/interface.jl +++ b/src/Metadata/interface.jl @@ -22,31 +22,37 @@ metadata(x) = nothing metadata(x::SubArray) = metadata(parent(x)) metadata(x::Base.ReshapedArray) = metadata(parent(x)) # define our own metadata method -Metadata.metadata(x::MetadataArray) = getfield(x, :metadata) metadata(A::NamedDimsArray) = metadata(parent(A)) metadata(A::AbstractAxisArray) = metadata(parent(A)) metadata(axis::AbstractAxis) = metadata(indices(axis)) +metadata(A::MetadataArray) = getfield(A, :metadata) """ metaproperty(x, meta_key) Return the metadata of `x` paired to `meta_key`. """ -metaproperty(x, meta_key::Symbol) = getindex(metadata(x), meta_key) +@inline metaproperty(x, meta_key::Symbol) = _metaproperty(metadata(x), meta_key) +_metaproperty(x::AbstractDict{Symbol}, meta_key::Symbol) = getindex(x, meta_key) +_metaproperty(x, meta_key::Symbol) = getproperty(x, meta_key) """ metadata!(x, meta_key, val) Set the metadata of `x` paired to `meta_key`. """ -metaproperty!(x, meta_key::Symbol, val) = setindex!(metadata(x), val, meta_key) +@inline metaproperty!(x, meta_key::Symbol, val) = _metaproperty!(metadata(x), meta_key, val) +_metaproperty!(x::AbstractDict{Symbol}, meta_key::Symbol, val) = setindex!(x, val, meta_key) +_metaproperty!(x, meta_key::Symbol, val) = setproperty!(x, meta_key, val) """ has_metaproperty(x, meta_key) -> Bool Returns true if `x` has a property in its metadata structure paired to `meta_key`. """ -has_metaproperty(x, meta_key::Symbol) = haskey(metadata(x), meta_key) +@inline has_metaproperty(x, meta_key::Symbol) = _has_metaproperty(metadata(x), meta_key) +_has_metaproperty(x::AbstractDict{Symbol}, meta_key::Symbol) = haskey(x, meta_key) +_has_metaproperty(x, meta_key::Symbol) = hasproperty(x, meta_key) """ axis_meta(x) @@ -67,14 +73,14 @@ axis_meta(x::AbstractArray, i) = metadata(axes(x, i)) Return the metadata of `x` paired to `meta_key` at axis `i`. """ -axis_metaproperty(x, i, meta_key::Symbol) = getindex(axis_meta(x, i), meta_key) +axis_metaproperty(x, i, meta_key::Symbol) = _metaproperty(axis_meta(x, i), meta_key) """ axis_metaproperty!(x, meta_key, val) Set the metadata of `x` paired to `meta_key` at axis `i`. """ -axis_metaproperty!(x, i, meta_key::Symbol, val) = setindex!(axis_meta(x, i), val, meta_key) +axis_metaproperty!(x, i, meta_key::Symbol, val) = _metaproperty!(axis_meta(x, i), meta_key, val) """ has_axis_metaproperty(x, dim, meta_key) @@ -82,7 +88,7 @@ axis_metaproperty!(x, i, meta_key::Symbol, val) = setindex!(axis_meta(x, i), val Returns true if `x` has a property in its metadata structure paired to `meta_key` stored at the axis corresponding to `dim`. """ -has_axis_metaproperty(x, i, meta_key::Symbol) = haskey(axis_meta(x, i), meta_key) +has_axis_metaproperty(x, i, meta_key::Symbol) = _has_metaproperty(axis_meta(x, i), meta_key) """ has_metadata(x) -> Bool @@ -118,6 +124,11 @@ function metadata_type(::Type{A}) where {A<:AbstractArray} end metadata_type(::Type{<:AbstractAxis{K,I,Ks,Inds}}) where {K,I,Ks,Inds} = metadata_type(Inds) +# This allows dictionaries's keys to be treated like property names +@inline metanames(x) = _metanames(metadata(x)) +_metanames(m::AbstractDict) = keys(m) +_metanames(x) = propertynames(x) + # TODO document combine_metadata function combine_metadata(x::AbstractUnitRange, y::AbstractUnitRange) return combine_metadata(metadata(x), metadata(y)) diff --git a/src/PrettyArrays/PrettyArrays.jl b/src/PrettyArrays/PrettyArrays.jl index 3847d43e..5e564319 100644 --- a/src/PrettyArrays/PrettyArrays.jl +++ b/src/PrettyArrays/PrettyArrays.jl @@ -12,7 +12,6 @@ using PrettyTables using Base: tail import NamedDims: NamedDimsArray -import MetadataArrays: MetadataArray export pretty_array, diff --git a/test/runtests.jl b/test/runtests.jl index 987124fd..e4dd8ee6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,10 +12,9 @@ using TableTraitsUtils using MappedArrays using Dates using Documenter -import MetadataArrays #= -pkgs = (MetadataArrays,Documenter,Dates,MappedArrays,Statistics,TableTraits,TableTraitsUtils,LinearAlgebra,Tables,IntervalSets,NamedDims,StaticRanges,StaticArrays,Base,Core); +pkgs = (Documenter,Dates,MappedArrays,Statistics,TableTraits,TableTraitsUtils,LinearAlgebra,Tables,IntervalSets,NamedDims,StaticRanges,StaticArrays,Base,Core); ambs = detect_ambiguities(pkgs...); using AxisIndices ambs = setdiff(detect_ambiguities(AxisIndices, pkgs...), ambs);