How to map a function over the given axis of a multi-dimension array and return another array?

I am trying to rotate some images, so I want to write something like

np.array(list(map(lambda x: rotate(x), images)))

in python, where the variable images is an array of shape [N, H, W]. Is there a method in julia that can do this in one line as well? I have tried

result = map(x->rotl90(images[x,:,:]), 1:size(images)[1])

but variable result is of type Vector{Matrix{Float64}}, and I do not know how to convert it to an Array again.

In general, how can I apply a function over a given axis of an Array object and convert the output variable from Vector{Array{Float64}} to Array{Float64} again? Since using map is a powerful way to simplify and parallelize the code, it will be very convenient to have a one-line solution for this kind of problem.

Thank you very much!

I think it would be better to organize your images so that they are stacked along the third dimension of the array. Unlike numpy, which uses ‘last-dimension-major’ ordering, Julia arrays are column-major, so it is natural to let the two first dimensions hold the image.

In that case, you can do

mapreduce(rotl90, (x,y)->cat(x,y; dims=3), eachslice(X; dims=3))

If you must orient them along the last two dimensions, it’s harder, but I think possible. But, think hard about organizing your data reasonably, instead of copying numpy.

Can’t you just write

map(rotate, images)

?

As @DNF says, it’s better for both performance and convenience to make the last array dimension enumerate your images.
Simple ways to convert between Vector{Array{T}} and Array{T} are available in the SplitApplyCombine.jl package:

julia> using SplitApplyCombine

# split by the last dimension, rotate each image, stack back by the last dimension
julia> combinedims(map(x -> rotate(x, 45), splitdims(images)))

# same, but split/combine is done lazily without extra copies:
julia> combinedims(map(x -> rotate(x, 45), splitdimsview(images)))

You could use mapslices:

result = mapslices(rotl90, images; dims = (2, 3))
5 Likes

Thank you @DNF and @aplavin for the suggestion of arranging data in the third dimension! From the snippet from @StevenWhitaker, I found that my understanding of mapslices is incorrect. The dims argument is how to pick the element to be applied from the list, rather than on which axis the function will apply. And mapslices returns another array, and this is very convenient. I think the method from @StevenWhitaker is the simplest solution for me, and thank you all for your help!