how to apply horzcat to output arrayfun

2 visualizaciones (últimos 30 días)
François Fabre
François Fabre el 30 de Abr. de 2020
Comentada: François Fabre el 30 de Abr. de 2020
Here's a simplified example of my problem:
A = [2 7 11;
5 9 11]; % This is my starting array
I'd like to obtain
B = [2 3 4 5 7 8 9 11]; % in other words [2:5 7:9 11:11]
Right now, my solution is
B = arrayfun(@(x,y) colon(x,y), A(1,:), A(2,:), 'UniformOutput', false);
Then I use [B{:}] as a comma-separated-list to index another array.
Since I'm doing this in a for loop where A can have hundred of thousands of columns, I'd like to avoid to create a cell by horizontally concatenating output arguments from arrayfun to improve memory layout efficiency.
Do you know if it's possible or if there's a smarter approach?
Thanks in advance!
  2 comentarios
Stephen23
Stephen23 el 30 de Abr. de 2020
Note that you don't need to define an anonymous function, just define a function handle to colon :
B = arrayfun(@colon, A(1,:), A(2,:), 'Uni',0);
% ^^^^^^ this is all you need
François Fabre
François Fabre el 30 de Abr. de 2020
Indeed, it's a good trick to know. Thank you!

Iniciar sesión para comentar.

Respuesta aceptada

Guillaume
Guillaume el 30 de Abr. de 2020
I like the functional programming aspect of arrayfun (it clearly says: apply this function to all the elements of these sequence) but note that if speed is critical then an explicit loop is likely to be faster.
With arrayfun you don't have a choice but going through an intermediate cell array. With your example, you don't have to split it in two lines:
B = cell2mat(arrayfun(@(x,y) colon(x,y), A(1,:), A(2,:), 'UniformOutput', false)); %works because the cell array coming out of arrayfun is a row vector of row vectors
With an explicit loop, in the case of your example you can easily calculate where the sequences land in the final array and avoid the cell array altogether:
seqlengths = A(2, :) - A(1, :) + 1;
seqstarts = cumsum([1, seqlengths(1:end-1)]);
B = zeros(1, seqstarts(end)-1);
for colidx = 1:size(A, 2)
B(seqstarts(colidx) + (0:seqlengths(colidx)-1)) = A(1, colidx) : A(2, colidx);
end
Whether the increase in code complexity is worth the gain is up to do. Of course the above may not work for your real use case.
  1 comentario
François Fabre
François Fabre el 30 de Abr. de 2020
I compared the three methods when A has 20000 columns :
- arrayfun + [B{:}] - > 1.6s
- cell2mat(arrayfun()) - > 2.5-2.7s
- your explicite for loop - > 2.4-2.5s
However, your approach is the right one. I found here different ways to vectorize the notion of colon. In my case the fastest (0.8-0.9s with 20000 columns) is a slightly different implementation than yours:
seqlengths = A(2, :) - A(1, :) + 1;
% find end and beginning indices for each chunk
partialLength = cumsum(seqlengths);
cumStart = [1 partialLength(1:end-1)+1];
% preallocate output
% then loop through start/stop pairs, in order, and fill
numtot = sum(seqlengths);
B = zeros(1,numtot);
for colidx = 1:length(A(1, :))
B(cumStart(colidx):partialLength(colidx)) = A(1, colidx) : A(2, colidx);
end
Thank you for your help!

Iniciar sesión para comentar.

Más respuestas (0)

Categorías

Más información sobre Matrix Indexing en Help Center y File Exchange.

Etiquetas

Productos


Versión

R2020a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by