Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-dimensional array comprehension in Julia

I can create the following (length(X),) array:

[F(x) for x in X]

However, if F(x) returns an array, is there anyway way to create a multidimensional array using comprehension where each row is F(x), and the dimensions of the array are (length(X),length(F(x))?

like image 831
jarbus Avatar asked Dec 30 '22 20:12

jarbus


2 Answers

There are many ways to do this in Julia! Following your use of comprehensions, a simple way to turn the array output of F(x) into a 2-dimensional array would be to concatenate the results, either with vcat, hcat, or the more generic cat, and using a reduce operation over the arguments:

F(x) = [x, x*2, x*3]
X = collect(1:5)
reduce(vcat, [F(x)' for x in X])
# 5×3 Array{Int64,2}:
#  1   2   3
#  2   4   6
#  3   6   9
#  4   8  12
#  5  10  15

Because you specifically requested a length(X) by length(F(x)) array, the adjoint operator ' has to be used on the output of F(x) to orient it properly for vcat. You can also do the same with hcat and transpose the result:

reduce(hcat, [F(x) for x in X])'
# 5×3 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
#  1   2   3
#  2   4   6
#  3   6   9
#  4   8  12
#  5  10  15

The reduce call is required because vcat and hcat concatenate together all of their arguments--if you just give them [F(x) for x in X], they will assume you are passing one single array of arrays and simply return that because there is nothing to concatenate.

You should also take a look at function broadcasting, a feature that sets Julia apart from many other languages. It can help reduce the verbosity of code that in other languages relies on list comprehensions. Just add a period after your function name and before the arguments to automatically broadcast the function over all of the elements of the argument!

Y = collect(2:2:10)
reduce(hcat, F.(Y))'
# 5×3 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
#   2   4   6
#   4   8  12
#   6  12  18
#   8  16  24
#  10  20  30

Advanced: Function Composition

As @CameronBiegnanek points out, Julia supports function composition using the operator. Composing two functions together like (G ∘ F)(x) translates to G(F(x)), so you can compose the adjoint operation with your function F like this:

reduce(vcat, (adjoint ∘ F).(X))

But this kind of operation (mapping a function over all elements of an array, then reducing all of those outputs using another function) is a common way of processing data known as a "map-reduce" operation. Julia has a built-in mapreduce function to do just that!

mapreduce(adjoint ∘ F, vcat, X)
like image 50
PaSTE Avatar answered Feb 05 '23 06:02

PaSTE


If F(x) returns a tuple or a StaticArray, the upcoming Julia 1.6 will allow you to do what you're asking like this:

reinterpret(reshape, T, [F(x) for x in X])

Demo:

julia> reinterpret(reshape, Int, [(i, i+1) for i=1:4])
2×4 reinterpret(reshape, Int64, ::Vector{Tuple{Int64, Int64}}) with eltype Int64:
 1  2  3  4
 2  3  4  5
like image 29
tholy Avatar answered Feb 05 '23 04:02

tholy