LazyStack.jl
This package exports one function, stack
, for turning a list of arrays
into one AbstractArray
. Given several arrays with the same eltype
,
or an array of such arrays, it returns a lazy Stacked{T,N}
view of these:
stack([zeros(2,2), ones(2,2)]) # isa Stacked{Float64, 3, <:Vector{<:Matrix}}
stack([1,2,3], 4:6) # isa Stacked{Int, 2, <:Tuple{<:Vector, <:UnitRange}}
Given a generator, it instead iterates through the elements and writes into a new array.
Given a function and then some arrays, it behaves like map(f, A, B)
but immediately writes
into a new array:
stack([i,2i] for i in 1:5) # isa Matrix{Int} # size(ans) == (2, 5)
stack(*, eachcol(ones(2,4)), 1:4) # == Matrix(stack(map(*, eachcol(...), 1:4)))
The same stack_iter
method is also used for any list of arrays of heterogeneous element type,
and for arrays of tuples. Notice that like map(identity, Any[1, 1.0, 5im])
, this promotes using
promote_typejoin
, to Number
here, rather than to Complex{Float64}
:
stack([1,2], [3.0, 4.0], [5im, 6im]) # isa Matrix{Number} # size(ans) == (2, 3)
stack([(i,2.0,3//j) for i=1:4, j=1:5])# isa Array{Real, 3} # size(ans) == (3, 4, 5)
The slices must all have the same size
, but they (and the container)
can have any number of dimensions. stack
always places the slice dimensions first.
There are no options.
Ragged stack
There is also a version which does not demand that slices have equal size
(or equal ndims
),
which always returns a new Array
. You can control the position of slices using OffsetArrays
:
rstack([1:n for n in 1:10]) # upper triangular Matrix{Int}
rstack(OffsetArray(fill(n,4), rand(-2:2)) for n in 1:10; fill=NaN)
Other packages
This one plays well with OffsetArrays.jl, NamedDims.jl, and Zygote.jl.
Besides which, there are several other ways to achieve similar things:
- For an array of arrays, you can also use
JuliennedArrays.Align
. This requires (or enables) you to specify which dimensions of the output belong to the sub-arrays, instead of writingPermutedDimsArray(stack(...), ...)
. - There is also
RecursiveArrayTools.VectorOfArray
which as its name hints only allows a one-dimensional container. Linear indexing retreives a slice, not an element, which is sometimes surprising. - For a tuple of arrays,
LazyArrays.Hcat
is at present faster to index thanstack
, but doesn't allow arbitrary dimensions. - For a generator of arrays, the built-in
reduce(hcat,...)
may work, but it slow compared tostack
: see test/speed.jl for some examples.
And a few more:
- When writing this I missed
SplitApplyCombine.combinedimsview
, which is very similar tostack
, but doesn't handle tuples. - Newer than this is StackViews.jl handles both, with
StackView(A,B,dims=4) == StackView([A,B],4)
creating a 4th dimension; the container is always one-dimensional. Flux.stack
similarly takes a dimension, but eagerly creates anArray
.
The package ArraysOfArrays.jl solves the opposite problem, of accessing one large array as if it were many slices. As does JuliennedArrays.Slices
, and of course Base.eachslice
.
After writing this I learned of JuliaLang#31644 which extends reduce(hcat,...)
to work on generators. Also relevant is JuliaLang#32310 which extends eachslice
to produce a multi-dimensional container.