# Q: array of vectors?

**URL:** https://discourse.julialang.org/t/q-array-of-vectors/89169
**Category:** General Usage
**Tags:** question
**Created:** [October 23, 2022, 11:39pm UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169 "2022-10-23T23:39:55Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![Audrius-St](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/audrius-st/32/24175_2.png) [@Audrius-St](https://discourse.julialang.org/u/Audrius-St)
#### Post date: [October 23, 2022, 11:39pm UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/1 "2022-10-23T23:39:55Z")

</div>

Hello,

What is the optimum representation to use to store vectors over a discrete field.  
For example, the discrete field being the 2D pixels of an image or the 3D voxels of an image volume?

I’ll use the 2D pixel field as an example, as the generalization to 3D should be straightforward.

A 2D array with can be instantiated as

an\_array::Array{Float64, 2} = zeros(Float64, (n\_rows, n\_cols))

wheree n\_rows and n\_cols are the number of rows and columns of, say, an image, respectively.

A 3D vector can be instantiated as

a\_vector::Vector{Float64} = [0.0, 0.0, 0.0]

How can I combine the two data structures such that a vector is associated with each pixel?

A concrete example would be the gradient vector at each pixel of an image.

---

<div class="post-metadata">

### Author: ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)
#### Post date: [October 23, 2022, 11:44pm UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/2 "2022-10-23T23:44:48Z")

</div>

```julia
a = fill(Vector{Float32}, nrows, ncols)

```

But I think you’d want a static type (StaticVector?).

Edit:

```julia
using StaticArrays
a = fill(SVector{3,Float32}, nrows, ncols)

```

---

<div class="post-metadata">

### Author: ![Krastanov](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/krastanov/32/6817_2.png) [@Krastanov](https://discourse.julialang.org/u/Krastanov)
#### Post date: [October 23, 2022, 11:44pm UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/3 "2022-10-23T23:44:54Z")

</div>

Unless something special is happening, a dense multi-axis array (tensor) is the most efficient representation.

The axes can be `[n_rows, n_cols, n_vector]` which would represent an `n_vector`-dimensional vector over `n_rows`-by-`n_vector` grid of points (which covers your example of a gradient).

Then you probably can abstract that away a bit and provide a pretty API instead of indexing it manually.

Depending on the type of computations you will be performing, the order of the axes could also be crucially important for performance.

---

<div class="post-metadata">

### Author: ![Krastanov](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/krastanov/32/6817_2.png) [@Krastanov](https://discourse.julialang.org/u/Krastanov)
#### Post date: [October 23, 2022, 11:45pm UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/4 "2022-10-23T23:45:48Z")

</div>

> [@PetrKryslUCSD](#):
>
> `a = fill(Vector{Float32, 3}, nrows, ncols)`

This would not be the “optimum” representation as it would be quite non-local in memory. But it is convenient and pretty if speed is not of upmost importance.

---

<div class="post-metadata">

### Author: ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)
#### Post date: [October 24, 2022, 12:52am UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/5 "2022-10-24T00:52:23Z")

</div>

Definitely seconding the `Matrix{SVector{3, Float64}}` suggestion. Having the 3-element value at each point represented as an actual 3-element array creates a clear distinction between the 2D coordinates of the pixel and its stored values: You index a pixel with `M[i, j]` and you access a particular element of that pixel with `M[i, j][2]`. Otherwise it’s easy to accidentally forget whether the 2nd element of some pixel is `M[i, j, 2]` or `M[2, i, j]` in an unlabeled 3D array.

Furthermore, an `Array{SVector{3, Float64}, 2}` is stored in _exactly_ the same layout as a contiguous array of `Float64`s, and you can reinterpret one as the other for free. That means you can always use the 3D array representation if you need to:

```julia
julia> M = rand(SVector{3, Float64}, 2, 4)
2×4 Matrix{SVector{3, Float64}}:
 [0.365512, 0.245407, 0.960372] [0.670765, 0.445472, 0.670487] [0.386643, 0.217216, 0.364761] [0.0798776, 0.318391, 0.297401]
 [0.301753, 0.307553, 0.800906] [0.899388, 0.033749, 0.285046] [0.362781, 0.590045, 0.0470405] [0.916995, 0.729333, 0.066537]

julia> reshape(reinterpret(Float64, M), :, 2, 4)
3×2×4 reshape(reinterpret(Float64, ::Matrix{SVector{3, Float64}}), 3, 2, 4) with eltype Float64:
[:, :, 1] =
 0.365512 0.301753
 0.245407 0.307553
 0.960372 0.800906

```

---

<div class="post-metadata">

### Author: ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)
#### Post date: [October 24, 2022, 12:58am UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/6 "2022-10-24T00:58:33Z")

</div>

> [@Krastanov](#):
>
> The axes can be `[n_rows, n_cols, n_vector]` which would represent an `n_vector`-dimensional vector over `n_rows`-by-`n_vector` grid of points (which covers your example of a gradient).

BTW, this is probably the wrong order since Julia is column-major (or first-axis-major in general). You probably want to store the n\_vector elements adjacent to one another, which suggests `[n_vector, n_rows, n_cols]` as the order. This is all the more reason to use an `Array{SVector{...}}` instead, since it ensures that the individual pixel elements are contiguous without you having to remember which order the axes should go in.

---

<div class="post-metadata">

### Author: ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)
#### Post date: [October 24, 2022, 1:19am UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/7 "2022-10-24T01:19:44Z")

</div>

It might be worth it to look at `Images.jl` and the whole ecosystem around it. Images are basically generic because of the many pixel types available. Leveraging the tools from the ecosystem is bound to payoff later.

---

<div class="post-metadata">

### Author: ![Audrius-St](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/audrius-st/32/24175_2.png) [@Audrius-St](https://discourse.julialang.org/u/Audrius-St)
#### Post date: [October 24, 2022, 2:37am UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/8 "2022-10-24T02:37:18Z")

</div>

Agreed. No point in reinventing an advanced, well-developed, wheel.  
However, my understanding is that the  
`Images.jl`  
package is for 2D images, whereas my eventual application involves 3D image volumes.

---

<div class="post-metadata">

### Author: ![Audrius-St](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/audrius-st/32/24175_2.png) [@Audrius-St](https://discourse.julialang.org/u/Audrius-St)
#### Post date: [October 24, 2022, 2:42am UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/9 "2022-10-24T02:42:18Z")

</div>

My thanks to all who provided comments and solutions.  
Much appreciated.

---

<div class="post-metadata">

### Author: ![PetrKryslUCSD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petrkryslucsd/32/215825_2.png) [@PetrKryslUCSD](https://discourse.julialang.org/u/PetrKryslUCSD)
#### Post date: [October 24, 2022, 3:16am UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/10 "2022-10-24T03:16:48Z")

</div>

Actually, my code fragment was not right. To initialize, use

```julia
nrows, ncols = 2, 3
a = fill(Vector{Float32}([0,0,0]), nrows, ncols)

```

---

<div class="post-metadata">

### Author: ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)
#### Post date: [October 24, 2022, 4:01am UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/11 "2022-10-24T04:01:18Z")

</div>

> [@Audrius-St](#):
>
> However, my understanding is that the  
> `Images.jl`  
> package is for 2D images, whereas my eventual application involves 3D image volumes.

Not at all! Images.jl actually works very well for images of any dimension (a big advantage over tools in other languages). The core image type in Images.jl is simply a standard Julia `Array` of pixels. For example, a standard RGB image using float values for the channels is just an `Array{RGB{Float32}, 2}` i.e. a 2D array whose element type is RGB. As with the `SVector` example posted above, this is exactly equivalent to a 3D array of `Float32`, and you can use the `channelview()` function to map between the two. Likewise, you can create a 3D image volume as an `Array{RGB{Float32}, 3}`.

Here’s a random 3D image volume:

```julia
julia> img = rand(RGB{Float32}, 2, 2, 2)
2×2×2 Array{RGB{Float32},3} with eltype RGB{Float32}:

```

and here it is reinterpreted as a 4D array of numbers:

```julia
julia> channelview(img)
3×2×2×2 reinterpret(reshape, Float32, ::Array{RGB{Float32},3}) with eltype Float32:

```

---

<div class="post-metadata">

### Author: ![Audrius-St](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/audrius-st/32/24175_2.png) [@Audrius-St](https://discourse.julialang.org/u/Audrius-St)
#### Post date: [October 25, 2022, 1:53am UTC](https://discourse.julialang.org/t/q-array-of-vectors/89169/12 "2022-10-25T01:53:46Z")

</div>

> [@rdeits](#):
>
> Not at all! Images.jl actually works very well for images of any dimension (a big advantage over tools in other languages). . . .

Thanks for correcting my misunderstanding and for your examples.  
I’m too used to image analysis packages being limited to only two dimensions in other languages and jumped to conclusions after a cursory look.

Will investigate Images.jl further. Looks like there are a lot of useful tools.
