How to extract components in an array of arrays

Suppose I have an array of arrays, say a 2 x 2 matrix whose entries are 1 x 2 vectors, e.g.,

julia> A
2×2 Matrix{Matrix{Int64}}:
 [1 2]  [3 4]
 [5 6]  [7 8]

I am wondering, without using for loops, how to extract a 2 x 2 matrix consisting of the 1st entries of this matrix of matrices, i.e.,

2x2 Matrix{Int64}
1 3
5 7

Also, a matrix of its 2nd entries, i.e.,

2x2 Matrix{Int64}
2 4
6 8

Is there a nice and quick way to do this? (Of course, what I really want is to do the same for large matrices of matrices or vectors, but this 2x2 example sufficiently describe my intention.)
Thanks a lot in advance!

You can broadcast getindex.(A,1) which gets index 1 for each element of the array.

4 Likes

Thank you so much! This is quite helpful!

Specifically for the first and last elements, you can use first.(A) and last.(A).

BTW, there’s no such thing as a 1x2 vector. That is a matrix.

1 Like

Note that in Julia vocab, [1 2] is not a vector but a 1x2 matrix. If you don’t have a specific reason to store your data like this, using [1, 2] is more Julian.

1 Like

what is (are they?) the syntax to use to build by hand a matrix like A that has matrices as elements?

The following reproduces A:

A = [[[1 2]] [[3 4]]; [[5 6]] [[7 8]]]  
1 Like

I have tried these ways, but one doesn’t work and I didn’t like the other as much

julia> [[1 2] [3 4]; [5 6] [7 8]]
2×4 Matrix{Int64}:
 1  2  3  4
 5  6  7  8

julia> reshape([[1 2], [5 6], [3 4], [7 8]],2,2)
2×2 Matrix{Matrix{Int64}}:
 [1 2]  [3 4]
 [5 6]  [7 8]

instead, this creates an array of tuples.
Why in the case of vectors need to be wrapped inside another vector?

julia> [(1,2) (3,4); (5,6) (7,8)]
2×2 Matrix{Tuple{Int64, Int64}}:
 (1, 2)  (3, 4)
 (5, 6)  (7, 8)

anhoter way

julia> [[[1 2], [5 6]] [ [3 4], [7 8]]]
2×2 Matrix{Matrix{Int64}}:
 [1 2]  [3 4]
 [5 6]  [7 8]

I think this follows from Julia’s rule that square braces surrounding elements separated by spaces are equivalent to horizontal concatenation:

[A B] == hcat(A,B)
2 Likes

Thanks, you are right. What I meant was row vectors of length 2, which are 1 x 2 matrices.

BTW, what I really wanted to do is to generate a vector field on a 2D plane, say, the unit square, and draw that vector field using quiver plot in Plots.jl. So, here is an example on a very small grid.

julia> using LazyGrids # this allows to generate meshes like in MATLAB
julia> X, Y = ndgrid((collect(0:1), collect(0:1));
julia> X
2×2 LazyGrids.GridAV{Int64, 1, 2}:
 0  0
 1  1
julia> Y
2×2 LazyGrids.GridAV{Int64, 2, 2}:
 0  1
 0  1
julia> f(x,y) = [0.0 1.0; 1.0 0.0] * [x; y]; # Just a simple example of generating a vector at (x,y).
julia> A =  f.(X,Y) # pointwise evaluation of f on the mesh
2×2 Matrix{Vector{Float64}}:
 [0.0, 0.0]  [1.0, 0.0]
 [0.0, 1.0]  [1.0, 1.0]

Then, in order to use quiver plot, I need to extract the first components and the second components:

quiver(X[:], Y[:], quiver=(getindex.(A,1)[:], getindex.(A,2)[:]))

Perhaps, there is a better way to generate a vector field, which can easily supply to the quiver plot…

It should be more convenient to work with vectors as they are, instead of separate components:

julia> using RectiGrids
julia> using StaticArrays

# array of SVectors, instead of two separate arrays:
julia> XY = grid(SVector, 0:1, 0:1) 
2-dimensional KeyedArray(...) with keys:
↓   2-element UnitRange{Int64}
→   2-element UnitRange{Int64}
And data, 2×2 RectiGrids.RectiGridArr{Base.OneTo(2), SVector{2, Int64}, 2, Tuple{UnitRange{Int64}, UnitRange{Int64}}}:
      (0)        (1)
 (0)     [0, 0]     [0, 1]
 (1)     [1, 0]     [1, 1]

# f() takes the whole svector, not two separate components:
julia> f(xy) = [0. 1; 1 0] * xy
f (generic function with 1 method)

julia> A = f.(XY)
2-dimensional KeyedArray(...) with keys:
↓   2-element UnitRange{Int64}
→   2-element UnitRange{Int64}
And data, 2×2 Matrix{Vector{Float64}}:
      (0)            (1)
 (0)     [0.0, 0.0]     [1.0, 0.0]
 (1)     [0.0, 1.0]     [1.0, 1.0]

Only in the end, if you actually need to split them into components:

julia> using SplitApplyCombine

julia> invert(XY)
2-element Vector{Matrix{Int64}}:
 [0 0; 1 1]
 [0 1; 0 1]

julia> invert(A)
2-element Vector{Matrix{Float64}}:
 [0.0 1.0; 0.0 1.0]
 [0.0 0.0; 1.0 1.0]