Stream: helpdesk (published)

Topic: Turn Iterators.product into matrix


view this post on Zulip Kevin Bonham (Jul 30 2021 at 16:55):

Suppose I have some number of iterables, and I want to turn their product into a matrix, with one column per iterable. I've gotten close with the following:

julia> x = 1:3; y = 4:10; z = 10:5:30;

julia> reduce(vcat, Iterators.product(x,y,z))
105-element Vector{Tuple{Int64, Int64, Int64}}:
 (1, 4, 10)
 (2, 4, 10)
 (3, 4, 10)
 (1, 5, 10)
 (2, 5, 10)
# ...

But now I need to get that into a matrix. I can hack something together with Iterators.flatten and a reshape, but it's... um... not pretty

julia> function make_matrix(args...)
           permutedims(reshape(collect(Iterators.flatten(reduce(vcat, Iterators.product(args...)))),
               length(args), *(length.(args)...)))
       end
make_matrix (generic function with 1 method)

julia> make_matrix(x,y,z)
105×3 Matrix{Int64}:
 1   4  10
 2   4  10
 3   4  10
 1   5  10
 2   5  10
 3   5  10
 1   6  10
 2   6  10
 3   6  10
 1   7  10

view this post on Zulip Takafumi Arakaki (tkf) (Jul 30 2021 at 16:57):

you can use collect(Iterators.product(x, y, z))

view this post on Zulip Maarten (Jul 30 2021 at 16:59):

best I can come up with, but it's also ugly

permutedims(reduce(hcat,map(x->[x...],Iterators.product(x,y,z))))

view this post on Zulip Kevin Bonham (Jul 30 2021 at 17:00):

@Takafumi Arakaki (tkf) That give you a 3D Array:

julia> collect(Iterators.product(x,y,z))
3×7×5 Array{Tuple{Int64, Int64, Int64}, 3}:

view this post on Zulip Kevin Bonham (Jul 30 2021 at 17:01):

@Maarten Oh, that's clever :-)

view this post on Zulip Maarten (Jul 30 2021 at 17:01):

you can get it slightly shorter using mapreduce, which I forgot about x)

view this post on Zulip Michael Abbott (Jul 30 2021 at 22:01):

transpose(reinterpret(reshape, Int, vec(collect(Iterators.product(x,y,z))))) is probably quite efficient.

view this post on Zulip Michael Abbott (Jul 30 2021 at 22:04):

Or using TensorCast; @cast out[(i,j,k),c] := (x[i], y[j], z[k])[c] c in 1:3

view this post on Zulip Takafumi Arakaki (tkf) (Jul 31 2021 at 02:25):

Oops, sorry I misread the question. And yeah, for a quick code, I'd use reinterpret like Michael does.

A variant of this is to do the reshape on the receiver side:

m = Matrix{Int}(undef, prod(length, (x, y, z)), 3)
reshape(reinterpret(Tuple{Int,Int,Int}, vec(m')), length.((x, y, z))) .= tuple.(reshape(x, :, 1, 1), reshape(y, 1, :, 1), reshape(z, 1, 1, :))

(which could be useful if you want contiguous memory as a result and avoid copy)

view this post on Zulip Nils (Aug 01 2021 at 08:42):

Probably not very efficient, but a concise one-liner:

julia> Matrix(DataFrame(Iterators.product(x, y, z)))
105×3 Matrix{Int64}:
 1   4  10
 2   4  10
...

view this post on Zulip Andrey Oskin (Aug 01 2021 at 10:16):

Nice idea about DataFrames. Extending this idea we can get the following

julia> Tables.matrix(Iterators.product(x, y, z))
105×3 Matrix{Int64}:
 1   4  10
 2   4  10
...

Last updated: Oct 02 2023 at 04:34 UTC