Julia arrays with dimension labels, and scaling
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Sometimes I collect data as a vector of matrices, or a vector of those, and sometimes I smash that into a 4-tensor. Then I make lots of mistakes about which dimensions are which. So because I had more important things to do, I thought I'd write a simple package to remember their names for me.

using DimArrays

list = [];
for i=1:33
    slowcalc = sqrt(i) .* randn(3,13) .+ i
    push!(list, DimArray(slowcalc, :a, :b, :c ))  ## add labels for 1st and 2nd dimensions  

list3 = nest(list, :iter)  ## now i is the 3rd index, and named "iter"

mean(list3, :iter)  ## equivalent to squeeze(mean(list3,3),3)

And for quick-and-dirty plots, I want the axes & series labelled so that I know which is which, and can quickly decide if I need a transpose:

using Plots

plot(slicedim(list3, :b, 1)' , legend=:bottomright)

Here slicedim(list3, :b, 1) == list3[:,1,:] in contents, but retains the labels.

Besides each dimension's name (a Symbol, strings will be converted) it can also store a function, which is used in plotting to scale the axes etc. (But only the output, getindex uses original integer indices). You can pass a number by which to scale the index, or a dictionary, instead of a function. For example, this plots data sampled every 4 iterations correctly over the above:

saveevery = 4
list4 = DimArray([], :iter, saveevery);  ## equivalent to function  i->4i
for i=1:33
    slowcalc = sqrt(i) .* randn(3,23) .+ i
    slownice = DimArray(slowcalc, [:a, :b], [Dict(1=>"one", 2=>"two", 3=>"three")], :stuff )
                                            ## equivalent to  i->Dict(...)[i]
    rem(i,saveevery)==0 && push!(list4, slownice)

plot!(mean(nest(list4), :b)', s=:dash)

If you do not provide a name for a dimension (or give an empty string "") then you can still refer to it by default names like size(x, :row) == size(x,1) or maximum(y, :col) etc. However these defaults are not stored, and not manipulated by transpose(x) or kron(x,y).

For now, the list of functions supported is:

  • sum, mean, std, maximum, minimum, squeeze: all can be called with a dimension's name. and then squeeze that dimension, like mean(..., :b) above. They can also be called with a list of dimensions: sum(x, [1,:c]) etc.
  • slicedim, size understand a dimension's name.
  • push!, append!, hcat, vcat, transpose, ctranspose, permutedims.
  • broadcast, both by scalars DimArray(rand(3,3)) .+ 10 and for combining arrays DimArray(rand(3),"rand col") .* DimArray(ones(1,4),"", "one row").
  • Matrix multiplication * will warn (once) if you multiply along directions with mismatched names... unsure if that's a good idea? And kronecker products produce new names like :a_b.
  • collect, implicitly used by comprehensions like [ sqrt(n) for n in DimVector(1:10, "int")' ].

Since DimArray <: AbstractArray anything else will fall back on their methods, and forget the dimension labels. Functions exported by using DimArrays are:

  • DimArray, DimVector, DimMatrix, nest.

Of course I'm not the first person to have this idea, nor to write up his own package... as I discovered half way into writing this. Both NamedArrays and AxisArrays do something similar. This package is lighter-weight, and implements a few things I wanted like push! (both of the others are immutable), and plot recipes, but makes little attempt to be high-performance. You can convert to these via AxisArray(a::DimArray) and NamedArray(a::DimArray), preserving axis (dimension) names.

For other views on vectors of arrays etc, see RecursiveArrayTools and JuliennedArrays.


  • Maybe make things like x[:, 1:10:end] update the functions.
  • Figure out Julia 0.7's new broadcasting machinery.

Michael Abbott, January 2018, mostly.

Build Status