Broadcasting across columns of a matrix

I am struggling to use the dot operator/ broadcasting correctly though when I’d like to broadcast across columns of a matrix.

Suppose I have a function:

function myfun(col_vec, scalar)
   #does something complex operation 
   ...
   return output
end

I’d like a ``vector" version that operates on a matrix by column:

function myfun_vec(mat, vec)
   out = length(output)
   for ix = 1:length(vec)
      out[ix] = myfun(mat[:, ix], vec[ix])
   end
   out
end

Can I use dot syntax / broadcasting to avoid implementing myfun_vec instead? Note

   myfun. ( mat, vec)  

does not return what I want. I mostly want the “.” version so that I can write things like:

myfun. (mat, 2)  #should return myfun_vec(mat, 2 * ones(dim(mat, 2) )

If redefine the data structure of mat to be a vector of vectors and then everything works fine, but that seems super cludgey and doesn’t match surrounding code.

1 Like

Here is my try,

julia> myfun(col_vec, scalar) = scalar * col_vec
myfun (generic function with 1 method)

julia> mat = collect(reshape(1:10, 2, 5))
2×5 Array{Int64,2}:
 1  3  5  7   9
 2  4  6  8  10

julia> hcat(myfun.([mat[:, i] for i = 1 : size(mat, 2)], collect(1:size(mat, 2)))...)
2×5 Array{Int64,2}:
 1  6  15  28  45
 2  8  18  32  50

I think that in order to use the syntax, myfun.(arg1, arg2), the collections arg1 and arg2 must have the same length.

One option if you know the number of rows of your matrix is to reinterpret it as a vector of SVectors from StaticArrays.jl. This doesn’t copy any data and works very nicely if you have a small number of rows:

julia> A = rand(2, 5)
2×5 Array{Float64,2}:
 0.969289  0.743624  0.280098  0.401384  0.0525567
 0.765344  0.951552  0.913606  0.208708  0.136869 

julia> using StaticArrays

julia> v = reinterpret(SVector{2, Float64}, A);

julia> println.(v)
[0.969289, 0.765344]
[0.743624, 0.951552]
[0.280098, 0.913606]
[0.401384, 0.208708]
[0.0525567, 0.136869]

You can try:

myfun.( [view(mat,:,i) for i in 1:size(mat,2)], vec)

Still, views are allocating but not too much.

Here are some alternatives using mapslices. Both assume that you have a function myfun(col_vec, scalar) as above. Note ii) adds an additional method to myfun().

Cases:
i) The scalars in vec are the same:

mapslices(x->myfun(x,2), mat, dims=1)

ii) The scalars in vec are the same or different e.g. vec = reshape(rand(3), 1,3)

myfun(col_vec) = myfun(col_vec[2:end], col_vec[1])
mapslices(myfun, vcat(vec,mat), dims=1)
1 Like