diff --git a/README.md b/README.md index 3cdce51..17a6dd5 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,28 @@ julia> SLArray{Tuple{2,2}}(a=1, b=2, c=3, d=4) 2 4 ``` +One can also specify the indices directly. +```julia +julia> EFG = @SLArray (2,2) (e=1:3, f=4, g=2:4); +julia> y = EFG(1.0,2.5,3.0,5.0) +2×2 SLArray{Tuple{2,2},Float64,2,4,(e = 1:3, f = 4, g = 2:4)}: + 1.0 3.0 + 2.5 5.0 + +julia> y.g +3-element view(reshape(::StaticArrays.SArray{Tuple{2,2},Float64,2,4}, 4), 2:4) with eltype Float64: + 2.5 + 3.0 + 5.0 + +julia> Arr = @SLArray (2, 2) (a = (2, :), b = 3); +julia> z = Arr(1, 2, 3, 4); +julia> z.a +2-element view(::StaticArrays.SArray{Tuple{2,2},Int64,2,4}, 2, :) with eltype Int64: + 2 + 4 +``` + Constructing copies with some items changed is supported by a keyword constructor whose first argument is the source and additonal keyword arguments change several entries. @@ -76,26 +98,20 @@ julia> B2 = SLArray(B; c=30 ) 2 4 ``` -One can also specify the indices directly. -```julia -julia> EFG = @SLArray (2,2) (e=1:3, f=4, g=2:4); -julia> y = EFG(1.0,2.5,3.0,5.0) -2×2 SLArray{Tuple{2,2},Float64,2,4,(e = 1:3, f = 4, g = 2:4)}: - 1.0 3.0 - 2.5 5.0 +Creating subsets is supported by function `subset`. +It differs from slicing via `[]` by retaining the labels, instead of returning an Array. -julia> y.g -3-element view(reshape(::StaticArrays.SArray{Tuple{2,2},Float64,2,4}, 4), 2:4) with eltype Float64: - 2.5 - 3.0 - 5.0 -julia> Arr = @SLArray (2, 2) (a = (2, :), b = 3); -julia> z = Arr(1, 2, 3, 4); -julia> z.a -2-element view(::StaticArrays.SArray{Tuple{2,2},Int64,2,4}, 2, :) with eltype Int64: - 2 - 4 +```julia +julia> zs = SLVector(a=1.0,b=2.0,c=3.0); +julia> zsSub = subset(zs, (:c,:a)) +2-element SLArray{Tuple{2},Float64,1,2,(:c, :a)}: + 3.0 + 1.0 +julia> zs[[:c,:a]] +2-element Array{Float64,1}: + 3.0 + 1.0 ``` ## LArrays diff --git a/src/LabelledArrays.jl b/src/LabelledArrays.jl index d145981..ec01c9a 100644 --- a/src/LabelledArrays.jl +++ b/src/LabelledArrays.jl @@ -13,5 +13,6 @@ export SLArray, LArray, SLVector, LVector, @SLVector, @LArray, @LVector, @SLArra export @SLSliced, @LSliced export symbols, dimSymbols, rowSymbols, colSymbols +export subset end # module diff --git a/src/larray.jl b/src/larray.jl index 580ba2e..ab5b208 100644 --- a/src/larray.jl +++ b/src/larray.jl @@ -261,3 +261,38 @@ function SLArray(v1::Union{SLArray{S,T,N,L,Syms},LArray{T,N,D,Syms}}; kwargs...) t2 = merge(convert(NamedTuple, v1), kwargs.data) SLArray{S}(t2) end + + +@inline subset(lvec::LArray, s::Tuple{N,Symbol}) where N = subset(lvec, Val(s)) +@inline function subset(lvec::LArray{T,N,D,Syms}, ::Val{SymSub}) where {T,N,D,Syms,SymSub} + subArr = lvec[SVector(SymSub)] + #SLVector(NamedTuple{SymSub}(subArr)) # not type stable + LArray{T,1,D,SymSub}(subArr) +end + +@inline subset(lvec::LArray, s::Tuple{N,I}) where {N,I<:Integer} = subsetInt(lvec, Val(s)) +@inline function subsetInt(lvec::LArray{T,N,D,Syms}, ::Val{SymSub}) where {T,N,D,Syms,SymSub} + symb = Syms[collect(SymSub)] + subArr = lvec[SVector(symb)] + LArray{T,1,D,symb}(subArr) +end + +@inline subset(lvec::LArray{T,N,D,Syms}, ::Tuple{}) where {T,N,D,Syms} = + LArray{T,1,D,()}(@SVector T[]) + + +function subset(lvec::Union{SLArray,LArray}, ind::SVector{N,I}) where {N,I <: Integer} + labels = symbols(lvec)[ind] + subset(lvec,labels) +end +subset(lvec::Union{SLArray,LArray}, labels::SVector{N,T}) where {N,T <: Symbol} = + subset(lvec,Tuple(labels)) +subset(lvec::Union{SLArray,LArray}, labels::SLArray{T,Symbol,1,N,Sym}) where {T,N,Sym} = + subset(lvec,Tuple(labels)) +function subset(lvec::Union{SLArray,LArray}, ind::SLArray{T,I,1,N,Sym}) where {T,I<:Integer,N,Sym} + labels = symbols(lvec)[ind] + subset(lvec,Tuple(labels)) +end + + + diff --git a/src/slarray.jl b/src/slarray.jl index 7738ac5..f8d49bb 100644 --- a/src/slarray.jl +++ b/src/slarray.jl @@ -101,6 +101,7 @@ function Base.getindex(x::SLArray, inds::StaticVector{<:Any, Int}) end # Note: This could in the future return an SLArray with the right names +# see issue #59 for rather using subset to return SLVector function Base.getindex(x::SLArray,s::AbstractArray{Symbol,1}) [getindex(x,si) for si in s] end @@ -181,6 +182,7 @@ macro SLVector(T,syms) end end + """ symbols(::SLArray{T,N,D,Syms}) @@ -192,3 +194,48 @@ For example: symbols(z) # Tuple{Symbol,Symbol,Symbol} == (:a, :b, :c) """ symbols(::SLArray{S,T,N,L,Syms}) where {S,T,N,L,Syms} = Syms isa NamedTuple ? keys(Syms) : Syms + + +""" + subset(::SLArray, indicesTuple) + +Creates a new SLArray containing only the given indices. +The indices are given as a Tuple of symbols or a Tuple of Integer positions. + +Note, that this differs from subsetting a Labelled array by getindex or `[]` +by retaining the labels, instead of returning an Array. + +It works with vectors and arrays, where each element is labelled, but not for +complex cases, where a label refers to several items. + +For example: + + zs = SLVector(a=1, b=2, c=3) + zsSub = subset(zs, (:c,:a)) + zsSub = subset(zs, Val((:c,:a))) # type safe version using Val() + + zsSub = subset(zs, (3,1)) + zsSub = subset(zs, @SVector[:c,:a]) # @SVector from StaticArrays + zsSub = subset(zs, @SVector[3,1]) + zsSub = subset(zs, SLVector(i1=:c,i2=:a)) + zsSub = subset(zs, SLVector(i1=3,i2=1)) +""" +@inline subset(lvec::SLArray, s::Tuple{N,Symbol}) where N = subset(lvec, Val(s)) +@inline function subset(lvec::SLArray{S,T,N,L,Syms}, ::Val{SymSub}) where {S,T,N,L,Syms,SymSub} + subArr = lvec[SVector(SymSub)] + #SLVector(NamedTuple{SymSub}(subArr)) # not type stable + SLArray{Tuple{length(SymSub)},T,1,length(SymSub),SymSub}(subArr) +end + +@inline subset(lvec::SLArray, s::Tuple{N,I}) where {N,I<:Integer} = subsetInt(lvec, Val(s)) +@inline function subsetInt(lvec::SLArray{S,T,N,L,Syms}, ::Val{SymSub}) where {S,T,N,L,Syms,SymSub} + symb = Syms[collect(SymSub)] + subArr = lvec[SVector(symb)] + SLArray{Tuple{length(SymSub)},T,1,length(SymSub),symb}(subArr) +end + +@inline subset(lvec::SLArray{S,T,N,L,Syms}, ::Tuple{}) where {S,T,N,L,Syms} = + SLArray{Tuple{0},T,1,0,()}( @SVector T[] ) + + +# for providing indices as SVector or SLVector see larrays.jl diff --git a/test/larrays.jl b/test/larrays.jl index 06c1e9d..8739a14 100644 --- a/test/larrays.jl +++ b/test/larrays.jl @@ -1,4 +1,4 @@ -using LabelledArrays, Test, InteractiveUtils +using LabelledArrays, StaticArrays, Test, InteractiveUtils @testset "Basic interface" begin vals = [1.0,2.0,3.0] @@ -124,3 +124,42 @@ end z = @LArray [1 2; 3 4] (a = (2, :), b = 2:3) @test z.a == [3, 4] end + +@testset "subset" begin + z = @LArray [1.,2.,3.] (:a,:b,:c); + zSub = subset(z, (:c,:a)) + @test zSub == @LArray [3.,1.] (:c,:a) + @test symbols(zSub) == (:c,:a) + # + zSub = subset(z, (3,1)) + @test zSub == @LArray [3.,1.] (:c,:a) + @test symbols(zSub) == (:c,:a) + # + ind = @SVector[3,1] #SVector(3,1) + zSub = subset(z, ind) + @test zSub == SLVector(c=3.0,a=1.0) + # + ind = @SVector[:c,:a] #SVector(:c,:a) + zSub = subset(z, ind) + @test zSub == SLVector(c=3.0,a=1.0) + # + ind = (@SLVector (:n1,:n2))([:c,:a]) + zSub = subset(z, ind) + @test zSub == SLVector(c=3.0,a=1.0) + # + ind = (@SLVector (:n1,:n2))([3,1]) + zSub = subset(z, ind) + @test zSub == SLVector(c=3.0,a=1.0) + + zSub = subset(z, ()) + @test zSub == @SVector eltype(z)[] + + #zs[SVector(:c,:a)] + #@inferred z[SVector(:c,:a)] + + #subset(z, Val((:c,:a))) + #@code_warntype subset(z, Val((:c,:a))) + @inferred subset(z, Val((:c,:a))) + +end + diff --git a/test/slarrays.jl b/test/slarrays.jl index 270f9c4..00bf4dd 100644 --- a/test/slarrays.jl +++ b/test/slarrays.jl @@ -76,3 +76,44 @@ end z = Arr(1, 2, 3, 4) @test z.a == [2, 4] end + +@testset "subset" begin + #zs = SLVector(a=1.0,b=2.0,c=3.0,d=4.0) + zs = SLArray{Tuple{2,2}}(a=1.0,b=2.0,c=3.0,d=4.0); + zsSub = subset(zs, (:c,:a)) + @test zsSub == SLVector(c=3.0,a=1.0) + # + # workaround with testing type inside subset + zsSub = subset(zs, (3,1)) + @test zsSub == SLVector(c=3.0,a=1.0) + @test symbols(zsSub) == (:c,:a) + # + ind = SVector(3,1) + zsSub = subset(zs, ind) + @test zsSub == SLVector(c=3.0,a=1.0) + # + ind = SVector(:c,:a) + zsSub = subset(zs, ind) + @test zsSub == SLVector(c=3.0,a=1.0) + # + #ind = @SLVector (:n1,:n2) (:c,:a) + zsSub = subset(zs, ind) + @test zsSub == SLVector(c=3.0,a=1.0) + # + ind = (@SLVector (:n1,:n2))(3,1) + zsSub = subset(zs, ind) + @test zsSub == SLVector(c=3.0,a=1.0) + + zsSub = subset(zs, ()) + #@test zsSub == SLVector() + @test length(zsSub) == 0 + @test zsSub isa SLArray + @test symbols(zsSub) == () + @test eltype(zsSub) == eltype(zs) + + + #subset(zs, Val((:c,:a))) + #@code_warntype subset(zs, Val((:c,:a))) + @inferred subset(zs, Val((:c,:a))) +end +