VHarisop
VHarisop

Reputation: 2826

Apply function to pairs of columns in Julia

I have a pair of matrices, say Ws, Xs, of equal dimension and a function myFunc(w, x) which takes two vectors as input. I want to apply this function to pairs of columns (think of it as zip-ing the columns) and mapping this function to them.

Is there a non-iterative way to do this? If there were only two columns in each of Ws, Xs, I can do

allCols = permutedims(reshape(hcat(Ws, Xs), d, 2), [1, 3, 2])
mapslices(x -> myFunc(x[:, 1], x[:, 2]), allCols, dims=[1, 2])

but I'm having trouble moving to an arbitrary number of columns.

Edit: using vcat and the correct dimensions seems to fix this:

# assume d is column size
wxArray = reshape(vcat(Ws, Xs), 2, d)  # group pairs of columns together
mapslices(x -> myFunc(x[:, 1], x[:, 2]), wxArray, dims=[1,2])

Upvotes: 2

Views: 1271

Answers (1)

Bogumił Kamiński
Bogumił Kamiński

Reputation: 69949

You can use eachcol function like this (I give three ways just to show different possible approaches but eachcol is crucial in all of them):

julia> Ws = rand(2,3)
2×3 Array{Float64,2}:
 0.164036  0.233236  0.937968
 0.724233  0.102248  0.55047

julia> Xs = rand(2,3)
2×3 Array{Float64,2}:
 0.0493071  0.735849  0.643352
 0.909295   0.276808  0.396145

julia> using LinearAlgebra

julia> dot.(eachcol(Ws), eachcol(Xs))
3-element Array{Float64,1}:
 0.6666296397421881
 0.19992972241709792
 0.8215096642236619

julia> dot.(eachcol.((Ws, Xs))...)
3-element Array{Float64,1}:
 0.6666296397421881
 0.19992972241709792
 0.8215096642236619

julia> map(dot, eachcol(Ws), eachcol(Xs))
3-element Array{Float64,1}:
 0.6666296397421881
 0.19992972241709792
 0.8215096642236619

This requires Julia 1.1.

EDIT

If you are on Julia 1.0 and do want to avoid iteration while not mind some extra allocations (the solution above avoids allocations) you can also use cat function (which is a bit simpler than your approach I think):

julia> Ws = rand(2,3)
2×3 Array{Float64,2}:
 0.975749  0.660932  0.391192
 0.619872  0.278402  0.799096

julia> Xs = rand(2,3)
2×3 Array{Float64,2}:
 0.0326003  0.272455  0.713046
 0.389058   0.886105  0.950822

julia> mapslices(x -> (x[:,1], x[:,2]), cat(Ws, Xs; dims=3), dims=[1,3])[1,:,1]
3-element Array{Tuple{Array{Float64,1},Array{Float64,1}},1}:
 ([0.975749, 0.619872], [0.0326003, 0.389058])
 ([0.660932, 0.278402], [0.272455, 0.886105])
 ([0.391192, 0.799096], [0.713046, 0.950822])

of course you can also simply do this:

julia> map(i -> (Ws[:,i], Xs[:,i]), axes(Ws, 2))
3-element Array{Tuple{Array{Float64,1},Array{Float64,1}},1}:
 ([0.975749, 0.619872], [0.0326003, 0.389058])
 ([0.660932, 0.278402], [0.272455, 0.886105])
 ([0.391192, 0.799096], [0.713046, 0.950822])

or more fancy:

julia> (i -> (Ws[:,i], Xs[:,i])).(axes(Ws, 2))
3-element Array{Tuple{Array{Float64,1},Array{Float64,1}},1}:
 ([0.975749, 0.619872], [0.0326003, 0.389058])
 ([0.660932, 0.278402], [0.272455, 0.886105])
 ([0.391192, 0.799096], [0.713046, 0.950822])

Upvotes: 2

Related Questions