How can I vectorise this range find over columns in a matrix in MATLAB? - matlab

Essentially I have an image mask and I want to find the width of the image in each column. Is there a way to vectorise this for speed? I tried to figure out a way with arrayfun but haven't hit on anything yet.
r = zeros(1,cols);
for i = 1 : cols
r(i) = range(find(img(:,i)));
end

The following code does the same as yours in a vectorized manner:
imglog = img~=0; %// convert to 0 and 1 values
[~, i1] = max(imglog); %// i1 is the position of the first 1
[~, i2] = max(flipud(imglog)); %// size(img,1)+1-i2 is the position of the last 1
r = size(img,1)+1-i2 - i1;
It exploits the fact that the second output of max gives the position of the first maximizer (for each column).

I am not sure whether this is faster, but num2cell + cellfun seems to be the only way to vectorize general functions on columns:
r = cellfun(#(x)range(find(x)),num2cell(img,1));

find + unique approach -
[row1,col1] = find(img);
[~,start1] = unique(col1,'first');
[~,stop1] = unique(col1);
r = row1(stop1) - row1(start1);

Related

Merge matrixes that are generated by function [duplicate]

In MatLab, I have a matrix SimC which has dimension 22 x 4. I re-generate this matrix 10 times using a for loop.
I want to end up with a matrix U that contains SimC(1) in rows 1 to 22, SimC(2) in rows 23 to 45 and so on. Hence U should have dimension 220 x 4 in the end.
Thank you!!
Edit:
nTrials = 10;
n = 22;
U = zeros(nTrials * n , 4) %Dimension of the final output matrix
for i = 1 : nTrials
SimC = SomeSimulation() %This generates an nx4 matrix
U = vertcat(SimC)
end
Unfortunately the above doesn't work as U = vertcat(SimC) only gives back SimC instead of concatenating.
vertcat is a good choice, but it will result in a growing matrix. This is not good practice on larger programs because it can really slow down. In your problem, though, you aren't looping through too many times, so vertcat is fine.
To use vertcat, you would NOT pre-allocate the full final size of the U matrix...just create an empty U. Then, when invoking vertcat, you need to give it both matrices that you want to concatenate:
nTrials = 10;
n = 22;
U = [] %create an empty output matrix
for i = 1 : nTrials
SimC = SomeSimulation(); %This generates an nx4 matrix
U = vertcat(U,SimC); %concatenate the two matrices
end
The better way to do this, since you already know the final size, is to pre-allocate your full U (as you did) and then put your values into U via computing the correct indices. Something like this:
nTrials = 10;
n = 22;
U = U = zeros(nTrials * n , 4); %create a full output matrix
for i = 1 : nTrials
SimC = SomeSimulation(); %This generates an nx4 matrix
indices = (i-1)*n+[1:n]; %here are the rows where you want to put the latest output
U(indices,:)=SimC; %copies SimC into the correct rows of U
end

Matlab - subtract a vector to each row of a matrix without for statement

I have a piece a code for an exam, what They want me to do in order to achieve a better grade is to implement the same thing without a second "for" statement.
The code is:
piv = 1:n; %// piv: position vector
for k = 1:n-1 %// for each column :
if ((max(abs(A(piv(k:n),k)))) > eps(normA)) %// if pivot is non zero
[~, I] = max(A(piv(k:n),k)); %// find the max index
I = I + (k-1);
piv([k,I]) = piv([I,k]); %// swap pivot elements
A(piv(k+1:n),k) = A(piv(k+1:n),k)/A(piv(k),k); %// calculate the multipliers and save them in the column
for j = k+1:n
A(piv(j),k+1:n) = A(piv(j),k+1:n) - (A(piv(k),k+1:n)*A(piv(j),k)); %// multiply for multipliers and subtract them by the row
end
end
end
This is the Gauss factorizing method but doesn't matter, the matter is I need to have the same result without the second for e and the j variable.
You can certainly kill the innermost loop with bsxfun. I am leaving it to you to explain to your prof on how it does what it does. Going through the bsxfun docs would be a good idea and in this process you might learn some vectorization techniques. Here's the implementation -
parte2 = bsxfun(#times,A(piv(k),k+1:n),A(piv(k+1:n),k))
A(piv(k+1:n),k+1:n) = A(piv(k+1:n),k+1:n) - parte2

How can I build a Scilab / MATLAB program that averages a 3D matrix?

I need to make a scilab / MATLAB program that averages the values of a 3D matrix in cubes of a given size(N x N x N).I am eternally grateful to anyone who can help me.
Thanks in advance
In MATLAB, mat2cell and cellfun make a great team for working on N-dimensional non-overlapping blocks, as I think is the case in the question. An example scenario:
[IN]: A = [30x30x30] array
[IN]: bd = [5 5 5], size of cube
[OUT]: B = [6x6x6] array of block means
To accomplish the above, the solution is:
dims = [30 30 30]; bd = [5 5 5];
A = rand(dims);
f = floor(dims./bd);
remDims = mod(dims,bd); % handle dims that are not a multiple of block size
Ac = mat2cell(A,...
[bd(1)*ones(f(1),1); remDims(1)*ones(remDims(1)>0)], ....
[bd(2)*ones(f(2),1); remDims(2)*ones(remDims(2)>0)], ....
[bd(3)*ones(f(3),1); remDims(3)*ones(remDims(3)>0)] );
B = cellfun(#(x) mean(x(:)),Ac);
If you need a full size output with the mean values replicated, there is a straightforward solution involving the 'UniformOutput' option of cellfun followed by cell2mat.
If you want overlapping cubes and the same size output as input, you can simply do convn(A,ones(blockDims)/prod(blockDims),'same').
EDIT: Simplifications, clarity, generality and fixes.
N = 10; %Same as OP's parameter
M = 10*N;%The input matrix's size in each dimensiona, assumes M is an integer multiple of N
Mat = rand(M,M,M); % A random input matrix
avgs = zeros((M/N)^3,1); %Initializing output vector
l=1; %indexing
for i=1:M/N %indexing 1st coord
for j=1:M/N %indexing 2nd coord
for k=1:M/N % indexing third coord
temp = Mat((i-1)*N+1:i*N,(j-1)*N+1:j*N,(k-1)*N+1:k*N); %temporary copy
avg(l) = mean(temp(:)); %averaging operation on the N*N*N copy
l = l+1; %increment indexing
end
end
end
The for loops and copying can be eliminated once you get the gist of indexing.

Populate vectors using for loop

I have a solution to creating a vector for just one element of a matrix:
[dx,dy] = gradient(Im);
orient11 = [(-dx(1,1)) (dy(1,1)) 0];
where
size(orient11) =
0 0 0
ie for the first element of orient, namely orient11, is a vector. How do I do this for all the other elements, so I have orient12, orient13....orientnn. I know I need a for loop, however what object do I store the vectors into from the for loop? I have discovered I can't create a matrix of vectors.
Thanks in advance.
You can try building an N-by-N-by-3 matrix, but it won't be so convenient to manipulate. This is because extracting a vector from this matrix would yield a 1-by-1-by-3 vector, which you would need to reshape. Definitely not fun.
Instead, I suggest that you build an N-by-N cell array of 1-by-3 vectors, like so:
[dx, dy] = gradient(Im);
vec = #(i)[-dx(i), dy(i), 0];
orient = arrayfun(vec, reshape(1:numel(dx), size(dx)), 'UniformOutput', 0);
To access a vector, use the curly braces. For example, the vector at the (1, 2) position would be:
orient12 = orient{1, 2};
Hope it helps!
v = -2:0.2:2;
[x,y] = meshgrid(v);
z = x .* exp(-x.^2 - y.^2);
[px,py] = gradient(z,.2,.2);
orient11 = [(-px(1,1)) (py(1,1)) 0]; % based off of your concatination there.
size(orient11)
I then get:
ans =
1 3
If you're looking to just grab the first column of data from the gradients you have and want to just stack zeros with them, you can do this:
orient11 = [(-px(:,1)) (py(:,1)) zeros(size(px,1),1)];
Instead of a for loop.
Update:
Orient = zeros(size(px,1),3,size(px,2));
for n = 1:size(px,1)
Orient(:,:,n) = [(-px(:,n)) (py(:,n)) zeros(size(px,1),1)];
end
The layout of Orient is now your -px, py, 0 in layers. Each layer represents the column from the initial data. So if you wanted to get access to row 4 column 14, you would call Orient(4,:,14).
Hope that makes sense and helps!

Apply function to all rows

I have a function, ranker, that takes a vector and assigns numerical ranks to it in ascending order. For example,
ranker([5 1 3 600]) = [3 1 2 4] or
ranker([42 300 42 42 1 42] = [3.5 6 3.5 3.5 1 3.5] .
I am using a matrix, variable_data and I want to apply the ranker function to each row for all rows in variable data. This is my current solution, but I feel there is a way to vectorize it and have it as equally fast :p
variable_ranks = nan(size(variable_data));
for i=1:1:numel(nmac_ids)
variable_ranks(i,:) = ranker(abs(variable_data(i,:)));
end
If you place the matrix rows into a cell array, you can then apply a function to each cell.
Consider this simple example of applying the SORT function to each row
a = rand(10,3);
b = cell2mat( cellfun(#sort, num2cell(a,2), 'UniformOutput',false) );
%# same as: b = sort(a,2);
You can even do this:
b = cell2mat( arrayfun(#(i) sort(a(i,:)), 1:size(a,1), 'UniformOutput',false)' );
Again, you version with the for loop is probably faster..
With collaboration from Amro and Jonas
variable_ranks = tiedrank(variable_data')';
Ranker has been replaced by the Matlab function in the Stat toolbox (sorry for those who don't have it),
[R,TIEADJ] = tiedrank(X) computes the
ranks of the values in the vector X.
If any X values are tied, tiedrank
computes their average rank. The
return value TIEADJ is an adjustment
for ties required by the nonparametric
tests signrank and ranksum, and for
the computation of Spearman's rank
correlation.
TIEDRANK will compute along columns in Matlab 7.9.0 (R2009b), however it is undocumented. So by transposing the input matrix, rows turn into columns and will rank them. The second transpose is then used to organize the data in the same manner as the input. There in essence is a very classy hack :p
One way would be to rewrite ranker to take array input
sizeData = size(variable_data);
[sortedData,almostRanks] = sort(abs(variable_data),2);
[rowIdx,colIdx] = ndgrid(1:sizeData(1),1:sizeData(2));
linIdx = sub2ind(sizeData,rowIdx,almostRanks);
variable_ranks = variable_data;
variable_ranks(linIdx) = colIdx;
%# break ties by finding subsequent equal entries in sorted data
[rr,cc] = find(diff(sortedData,1,2) == 0);
ii = sub2ind(sizeData,rr,cc);
ii2 = sub2ind(sizeData,rr,cc+1);
ii = sub2ind(sizeData,rr,almostRanks(ii));
ii2 = sub2ind(sizeData,rr,almostRanks(ii2));
variable_ranks(ii) = variable_ranks(ii2);
EDIT
Instead, you can just use TIEDRANK from TMW (thanks, #Amro):
variable_rank = tiedrank(variable_data')';
I wrote a function that does this, it's on the FileExchange tiedrank_(X,dim). And it looks like this...
%[Step 0a]: force dim to be 1, and compress everything else into a single
%dimension. We will reverse this process at the end.
if dim > 1
otherDims = 1:length(size(X));
otherDims(dim) = [];
perm = [dim otherDims];
X = permute(X,perm);
end
originalSiz = size(X);
X = reshape(X,originalSiz(1),[]);
siz = size(X);
%[Step 1]: sort and get sorting indicies
[X,Ind] = sort(X,1);
%[Step 2]: create matrix [D], which has +1 at the start of consecutive runs
% and -1 at the end, with zeros elsewhere.
D = zeros(siz,'int8');
D(2:end-1,:) = diff(X(1:end-1,:) == X(2:end,:));
D(1,:) = X(1,:) == X(2,:);
D(end,:) = -( X(end,:) == X(end-1,:) );
clear X
%[Step 3]: calculate the averaged rank for each consecutive run
[a,~] = find(D);
a = reshape(a,2,[]);
h = sum(a,1)/2;
%[Step 4]: insert the troublseome ranks in the relevant places
L = zeros(siz);
L(D==1) = h;
L(D==-1) = -h;
L = cumsum(L);
L(D==-1) = h; %cumsum set these ranks to zero, but we wanted them to be h
clear D h
%[Step 5]: insert the simple ranks (i.e. the ones that didn't clash)
[L(~L),~] = find(~L);
%[Step 6]: assign the ranks to the relevant position in the matrix
Ind = bsxfun(#plus,Ind,(0:siz(2)-1)*siz(1)); %equivalent to using sub2ind + repmat
r(Ind) = L;
%[Step 0b]: As promissed, we reinstate the correct dimensional shape and order
r = reshape(r,originalSiz);
if dim > 1
r = ipermute(r,perm);
end
I hope that helps someone.