How can I get counterdiagonals of a matrix and concatenate them? - matlab

Short Version
How can I do concatMap in MATLAB? I'm trying to build a single vector from a series of smaller, differently sized vectors. I know I can do:
result = [];
for i=1:N
result = [result nextPart(i)];
end
but that has a serious speed impact and there must be a smarter way to do concatMap.
Long Version
I'm trying to write a MATLAB function that returns the counterdiagonals of a block. For example, if you have the block:
1 2 4
3 5 7
6 8 9
then counterDiagonals(block) should return [1 2 3 4 5 6 7 8 9].
I have a function that will find a single counter diagonal of a block. i.e. counterDiagonal(x, 3) will return [4 5 6].
Therefore, counterDiagonals should be as simple as concatMap counterDiagonal(x, i) (1:N) where N is (2*length(block)-1). How can I do this in MATLAB in an efficient way?

One problem with the accepted answer: if the matrix A had zeros, they will be incorrectly removed from the result.. Instead you should work on the indices of the elements:
A = [0 2 4; 3 5 7; 6 8 9]; %# Sample matrix (contains zeros)
ind = reshape(1:numel(A), size(A)); %# indices of elements
ind = fliplr( spdiags( fliplr(ind) ) ); %# get the anti-diagonals (or use ROT90)
ind(ind==0) = []; %# keep non-zero indices
result = A(ind); %# get elements in desired order
This is very similar to this answer I gave in a previous question (the difference was that the anti-digaonals were in reverse order).

I believe what you want to do can be accomplished using the functions ROT90 and SPDIAGS:
A = [1 2 4; 3 5 7; 6 8 9]; %# Sample matrix
result = rot90(A); %# Rotate the matrix counter-clockwise
result = spdiags(result); %# Find all the diagonals
result = result(result ~= 0).'; %'# Remove zero padding and format the results
%# into a row vector
And you should end up with result = [1 2 3 4 5 6 7 8 9].
EDIT: As Amro mentions in a comment, the above code assumes that there are no zeroes in the original matrix A. If there are zeroes in the original matrix, one solution is to replace them with a non-zero flag value that you know doesn't appear in the original matrix (like, for example, NaN), run the above code, then replace the flag values in the result:
A = [0 2 4; 3 0 7; 6 8 0]; %# Sample matrix
result = rot90(A); %# Rotate the matrix counter-clockwise
result(result == 0) = nan; %# Replace zeroes with NaN
result = spdiags(result); %# Find all the diagonals
result = result(result ~= 0).'; %'# Remove zero padding and format the results
%# into a row vector
result(isnan(result)) = 0; %# Put the original zeroes back

Short version:
If you preassign your result array, everything will be a lot faster.
result = zeros(1,knownLengthOfResultsArray); %# such as "numel(block)"
ct = 1;
for i=1:N
tmp = nextPart(i);
nTmp = length(tmp);
result(ct:ct+nTmp-1) = tmp;
ct = ct + nTmp;
end
Long version:
However, it may be even more efficient to rewrite your algorithm. See e.g. the answers to this question (use fliplr on your array first), or #gnovice's answer.

Related

MATLAB: Applying vectors of row and column indices without looping

I have a situation analogous to the following
z = magic(3) % Data matrix
y = [1 2 2]' % Column indices
So,
z =
8 1 6
3 5 7
4 9 2
y represents the column index I want for each row. It's saying I should take row 1 column 1, row 2 column 2, and row 3 column 2. The correct output is therefore 8 5 9.
I worked out I can get the correct output with the following
x = 1:3;
for i = 1:3
result(i) = z(x(i),y(i));
end
However, is it possible to do this without looping?
Two other possible ways I can suggest is to use sub2ind to find the linear indices that you can use to sample the matrix directly:
z = magic(3);
y = [1 2 2];
ind = sub2ind(size(z), 1:size(z,1), y);
result = z(ind);
We get:
>> result
result =
8 5 9
Another way is to use sparse to create a sparse matrix which you can turn into a logical matrix and then sample from the matrix with this logical matrix.
s = sparse(1:size(z,1), y, 1, size(z,1), size(z,2)) == 1; % Turn into logical
result = z(s);
We also get:
>> result
result =
8
5
9
Be advised that this only works provided that each row index linearly increases from 1 up to the end of the rows. This conveniently allows you to read the elements in the right order taking advantage of the column-major readout that MATLAB is based on. Also note that the output is also a column vector as opposed to a row vector.
The link posted by Adriaan is a great read for the next steps in accessing elements in a vectorized way: Linear indexing, logical indexing, and all that.
there are many ways to do this, one interesting way is to directly work out the indexes you want:
v = 0:size(y,2)-1; %generates a number from 0 to the size of your y vector -1
ind = y+v*size(z,2); %generates the indices you are looking for in each row
zinv = z';
zinv(ind)
>> ans =
8 5 9

Filter elements from a 3D matrix without loop

I have a 3d matrix H(i,j,k) with dimensions (i=1:m,j=1:n,k=1:o). I will use a simple case with m=n=o = 2:
H(:,:,1) =[1 2; 3 4];
H(:,:,2) =[5 6; 7 8];
I want to filter this matrix and project it to an (m,n) matrix by selecting for each j in 1:n a different k in 1:0.
For instance, I would like to retrieve (j,k) = {(1,2), (2,1)}, resulting in matrix G:
G = [5 2; 7 4];
This can be achieved with a for loop:
filter = [2 1]; % meaning filter (j,k) = {(1,2), (2,1)}
for i = 1:length(filter)
G(:,i) = squeeze(H(:,i,filter(i)));
end
But I'm wondering if it is possible to avoid the for loop via some smart indexing.
You can create all the linear indices to get such an output with the expansion needed for the first dimension with bsxfun. The implementation would look like this -
szH = size(H)
offset = (filter-1)*szH(1)*szH(2) + (0:numel(filter)-1)*szH(1)
out = H(bsxfun(#plus,[1:szH(1)].',offset))
How does it work
(filter-1)*szH(1)*szH(2) and (0:numel(filter)-1)*szH(1) gets the linear indices considering only the third and second dimension elements respectively. Adding these two gives us the offset linear indices.
Add the first dimenion linear indices 1:szH(1) to offset array in elementwise fashion with bsxfun to give us the actual linear indices, which when indexed into H would be the output.
Sample run -
H(:,:,1) =
1 2
3 4
H(:,:,2) =
5 6
7 8
filter =
2 1
out =
5 2
7 4

visualizing 2D-graphs of n-dim. array via surf() in matlab

I want to show 2dim. Surface plots for different combinations of 2 parameters of a 3- or higher-dimensional array in matlab. The data for the non-shown dimensions are integrated (i.e. summed in the remaining dimensions). I am using surf(), and for parameter combinations other than (1,2) (eg. (1,3), (2,3) ...) I have to rearrange the data matrices in order to make it work.
I am looking for an alternative command (or shorter code) which does this work.
Here's the code:
a=zeros(3,3,2);
a(:,:,1) = [1 2 3 ;4 5 6; 7 8 9; 10 11 12]; % // data matrix
a(:,:,2) = -[1 2 3 ;4 5 6; 7 8 9; 10 11 12]*2; % // data matrix
ai=[[1 2 3 4]' [5 6 7 0]' [8 9 0 0]']; % // parameter vector
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
aux13 = sum(a,2);
for i = 1:2; mat13(:,i) = aux13(:,:,i);
surf(ai(1:2,3),ai(1:4,1),mat13)
aux23 = sum(a,1);
for i = 1:2; mat23(i,:) = aux23(:,:,i);
surf(ai(1:3,2),ai(1:2,3),mat23)
In other words, I am looking for a way to use surf for matrices mat13 and mat23 without the aux13, aux23 variables and the for loop.
First your example doesn't run because you declare a=zeros(3,3,2); as a matrix [3x3x2] but you immediately try to populate it as a [4x3x2] matrix, so I had to adjust your first line to: a=zeros(4,3,2);
If I run your code with that adjustment, your auxiliary variable and for loops are to reform/reshape a matrix stripped of it's singleton dimension. Matlab provide a handy function for that : squeeze.
For example, your variable aux13 is of dimension [4x1x2], then mat13=squeeze(aux13); achieve the same thing than your for loop. Your matrix mat13 is now of dimension [4x2].
Since no for loop is needed, you can completely bypass your auxiliary variable by calling squeeze directly on the result of your summation: mat13=squeeze( sum(a,2) );
Full example, the code below does exactly the same than your code sample:
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
mat13 = squeeze( sum(a,2) ) ;
surf(ai(1:2,3),ai(1:4,1),mat13)
mat23 = squeeze( sum(a,1) ) ;
mat23 = mat23.' ; %'// <= note the "transpose" operation here
surf(ai(1:3,2),ai(1:2,3),mat23)
Note that I had to transpose mat23 to make it match the one in your example.
sum(a,1) is [1x3x2] => squeeze that and you obtain a [3x2] matrix but your code arrange the same values in a [2x3] matrix, so the use of the transpose. The transpose operator has a shorthand notation .'.
I used it in the example in a separate line just to highlight it. Once understood you can simply write the full operation in one line:
mat23 = squeeze(sum(a,1)).' ;
The way you write your loops isn't exactly MATLAB syntax. Below is the correct loop syntax shown.
On line 2 and 3, you are trying to load (4x3)-matrices into (3x3)-matrices. That is why you get a subscript error. You could resolve it by making the zeros-matrix bigger. Here's some Syntax fixed:
a=zeros(4,3,2);
a(:,:,1) = [1 2 3 ;4 5 6; 7 8 9; 10 11 12]; % // data matrix
a(:,:,2) = -[1 2 3 ;4 5 6; 7 8 9; 10 11 12]*2; % // data matrix
ai=[[1 2 3 4]' [5 6 7 0]' [8 9 0 0]']; % // parameter vector
mat12 = sum(a,3);
surf(ai(1:3,2),ai(1:4,1),mat12)
aux13 = sum(a,2);
for i = 1:2 mat13(:,i) = aux13(:,:,i);
surf(ai(1:2,3),ai(1:4,1),mat13)
end
aux23 = sum(a,1);
for i = 1:2 mat23(i,:) = aux23(:,:,i);
surf(ai(1:3,2),ai(1:2,3),mat23)
end
Now, what are you exactly trying to do inside those loops?

Eliminating zeros in a matrix - Matlab

Hi I have the following matrix:
A= 1 2 3;
0 4 0;
1 0 9
I want matrix A to be:
A= 1 2 3;
1 4 9
PS - semicolon represents the end of each column and new column starts.
How can I do that in Matlab 2014a? Any help?
Thanks
The problem you run into with your problem statement is the fact that you don't know the shape of the "squeezed" matrix ahead of time - and in particular, you cannot know whether the number of nonzero elements is a multiple of either the rows or columns of the original matrix.
As was pointed out, there is a simple function, nonzeros, that returns the nonzero elements of the input, ordered by columns. In your case,
A = [1 2 3;
0 4 0;
1 0 9];
B = nonzeros(A)
produces
1
1
2
4
3
9
What you wanted was
1 2 3
1 4 9
which happens to be what you get when you "squeeze out" the zeros by column. This would be obtained (when the number of zeros in each column is the same) with
reshape(B, 2, 3);
I think it would be better to assume that the number of elements may not be the same in each column - then you need to create a sparse array. That is actually very easy:
S = sparse(A);
The resulting object S is a sparse array - that is, it contains only the non-zero elements. It is very efficient (both for storage and computation) when lots of elements are zero: once more than 1/3 of the elements are nonzero it quickly becomes slower / bigger. But it has the advantage of maintaining the shape of your matrix regardless of the distribution of zeros.
A more robust solution would have to check the number of nonzero elements in each column and decide what the shape of the final matrix will be:
cc = sum(A~=0);
will count the number of nonzero elements in each column of the matrix.
nmin = min(cc);
nmax = max(cc);
finds the smallest and largest number of nonzero elements in any column
[i j s] = find(A); % the i, j coordinates and value of nonzero elements of A
nc = size(A, 2); % number of columns
B = zeros(nmax, nc);
for k = 1:nc
B(1:cc(k), k) = s(j == k);
end
Now B has all the nonzero elements: for columns with fewer nonzero elements, there will be zero padding at the end. Finally you can decide if / how much you want to trim your matrix B - if you want to have no zeros at all, you will need to trim some values from the longer columns. For example:
B = B(1:nmin, :);
Simple solution:
A = [1 2 3;0 4 0;1 0 9]
A =
1 2 3
0 4 0
1 0 9
A(A==0) = [];
A =
1 1 2 4 3 9
reshape(A,2,3)
ans =
1 2 3
1 4 9
It's very simple though and might be slow. Do you need to perform this operation on very large/many matrices?
From your question it's not clear what you want (how to arrange the non-zero values, specially if the number of zeros in each column is not the same). Maybe this:
A = reshape(nonzeros(A),[],size(A,2));
Matlab's logical indexing is extremely powerful. The best way to do this is create a logical array:
>> lZeros = A==0
then use this logical array to index into A and delete these zeros
>> A(lZeros) = []
Finally, reshape the array to your desired size using the built in reshape command
>> A = reshape(A, 2, 3)

extract every n row in a matrix to another matrix in a loop

I'm a total beginner to matlab and I'm currently writing a script for extracting data from a thermographic video.
Firstly the video is cut in separate frames. The first frame is opened as a sample picture to define the coordinates of sampling points. The goal is then to select the rgb values of those defined coordinates from a set of frames and save them into a matrix.
Now I have a problem separating the matrix to n smaller matrices.
e.g I'm defining the number of points to be selected to n=2 , with a picture count of 31. Now it returns a matrix stating the rgb codes for 31 pictures, each at 2 points, in a 62x3 double matrix...
Now I want to extract the 1st, 3rd, 5th....etc... row to a new matrix...this should be done in a loop, according to the number of n points...e.g 5 points on each picture equals 5 matrices, containing values of 31 pictures....
this is an extract of my code to analyse the pictures, it returns the matrix 'values'
files = dir('*.jpg');
num_files = numel(files);
images = cell(1, num_files);
cal=imread(files(1).name);
n = input('number of selection points?: ');
imshow(cal);
[x,y] = ginput(n);
eval(get(1,'CloseRequestFcn'))
%# x = input('x-value?: '); manual x selection
%# y = input('y-value?: '); manual y selection
for k = 1:num_files
images{k} = imread(files(k).name);
end
matrix=cell2mat(images);
count=(0:size(matrix,1):size(matrix,1)*num_files);
for k = 1:num_files
a(k)= mat2cell(impixel(matrix,x+count(k),y));
end
values = cat(1,a{:})
Easy fix
Do you mean that if you have:
n = 2;
k = 2; % for example
matrix = [1 2 3;
4 5 6;
7 8 9;
8 7 6];
you want it to become
b{1} = [1 2 3;
7 8 9];
b{2} = [4 5 6;
8 7 6];
This can be easily done with:
for ii = 1:n
b{ii} = matrix(1:n:end,:);
end
Better fix
Of course it's also possible to just reshape your data matrix and use that instead of the smaller matrices: (continuing with my sample data ^^)
>> d=reshape(matrix',3,2,[]);
>> squeeze(d(:,1,:))
ans =
1 7
2 8
3 9
>> squeeze(d(:,2,:))
ans =
4 8
5 7
6 6
Good practice
Or, my preferred choice: save the data immediately in an easy to access way. Here I think it will be an matrix of size: [num_files x num_points x 3]
If you want all the first points:
rgb_data(:,1,:)
only the red channel of those points:
rgb_data(:,1,1)
and so on.
I think this is possible with this:
rgb_data = zeros(num_files, num_points, 3);
for kk = 1:num_files
rgb_data(kk,:,:) = impixel(images{kk},x+count(k),y);
end
But I don't understand the complete meaning of your code (eg: why matrix=cell2mat(images) ??? and then of course:
count=(0:size(matrix,1):size(matrix,1)*num_files);
is just count=0:num_files;
so I'm not sure what would come out of impixel(matrix,x+count(k),y) and I used images{k} :)