Reputation: 413
I have just started exploring world of vectorization. I got the 1-D vectorization down but i am having trouble vectorizing the following code. I want to do away with at least one of the for loops if possible b/c I plan to use this on a much larger data set over many iterations so saving computation time is of the essence.
CityPairs = [7 3
3 1
3 1
1 7
7 1
3 4
5 1
4 6];
Offices = [1;3;7];
nOffices = size(Offices,1);
connection = zeros(nOffices);
for i = 1:nOffices
for j = 1:nOffices
connection(i,j) = sum(Offices(i) == CityPairs(:,1)...
& CityPairs(:,2) == Offices(j));
end
end
disp(connection)
In this example there are 7 cities, three of which have offices. I want a pairwise matrix for the cities with offices to capture the sum of all the one way connections between each. The answer for above problem should be:
0 0 1
2 0 0
1 1 0
Any suggestions are welcome. Thanks in advance.
Keith
Upvotes: 1
Views: 188
Reputation: 32930
Here's an alternative solution with sparse
:
dims = max(max(CityPairs), max(Offices));
A = sparse(CityPairs(:, 1), CityPairs(:, 2), 1, dims(1), dims(2));
result = full(A(Offices, Offices));
This should speed up your computation a bit1 when compared to the suggested bsxfun
solution.
1 Runs 5 times faster on MATLAB 2012a (Windows Server 2008 R2 running on a 2.27GHz 16-core Intel Xeon processor)
Upvotes: 2
Reputation: 413
Answer provided by Cedric Wannaz on MathWorks Forum
It is not trivial to "vectorize" this setup, as there are operations that require some look up table, and there is accumulation (unless you find a trick). This is likely to make the approach without FOR loops more complicated (code-wise) than the basic, loop-based approach. So let's start with the trick first ;-) ..
A = double( bsxfun(@eq, CityPairs(:,1), Offices.') ) ;
B = double( bsxfun(@eq, CityPairs(:,2), Offices.') ) ;
A.' * B
Alternate approach: http://www.mathworks.com/matlabcentral/answers/91294-vectorization-of-2-for-loops-in-matlab
Upvotes: 0
Reputation: 10676
Your task is some selective cross-tabulation. You can accomplish this easily by accumulating counts to the positions of interest, indexed by your Offices
:
% Row and col subs
[~,rsubs] = ismember(CityPairs(:,1),Offices);
[~,csubs] = ismember(CityPairs(:,2),Offices);
% Select where both belong to Offices, i.e. non 0
subs = [rsubs,csubs];
subs = subs(all(subs,2),:);
% Accumulate
accumarray(subs,1)
The result
ans =
0 0 1
2 0 0
1 1 0
If you have the Statistics Toolbox, you could use crosstab
directly, but then you would need to select the rows and columns of interest:
crosstab(CityPairs(:,1),CityPairs(:,2))
ans =
0 0 0 0 1
2 0 1 0 0
0 0 0 1 0
1 0 0 0 0
1 1 0 0 0
Upvotes: 2