Create vector from elements other than diagonal ones - matlab

I would like to create a column vector from the elements of a matrix A of size (3,3) that are not on the diagonal. Thus, I would have 6 elements in that output vector. How can I do this?

Use eye and logical negation, although this is no better than Divakar's original answer, and possibly significantly slower for very large matrices.
>> A = magic(4)
A =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> A(~eye(size(A)))
ans =
5
9
4
2
7
14
3
10
15
13
8
12

Use this to get such a column vector, assuming A is the input matrix -
column_vector = A(eye(size(A))==0)
If you don't care about the order of the elements in the output, you can also use a combination of setdiff and diag -
column_vector = setdiff(A,diag(A))

You can also use linear indexing to access the diagonal elements and null them. This will automatically reshape itself to a single vector:
A(1:size(A,1)+1:end) = [];
Bear in mind that this will mutate the original matrix A. If you don't want this to happen, make a copy of your matrix then perform the above operation on that copy. In other words:
Acopy = A;
Acopy(1:size(A,1)+1:end) = [];
Acopy will contain the final result. You need to create a vector starting from 1 and going to the end in increments of the rows of the matrix A added with 1 due to the fact that linear indices are column-major, so the linear indices used to access a matrix progress down each row first for a particular column. size(A,1) will allow us to offset by each column and we add 1 each time to ensure we get the diagonal coefficient for each column in the matrix.

Assuming that the matrix is square,
v = A(mod(0:numel(A)-1, size(A,1)+1) > 0).';

Related

Why does a column vector return a row vector when used to index matrices?

Imagine the following matrix is defined in MATLAB:
>> matrix=10:18
matrix =
10 11 12 13 14 15 16 17 18
Now I want to use another matrix to index the first one
>> index=[1,2;3,4]
index =
1 2
3 4
>> matrix(index)
ans =
10 11
12 13
So far so good, the size of the answer matches that of the matrix 'index'. If I use a row vector as the indexing matrix, the output is a row vector too. But the problem appears when I use a column vector as the indexing matrix:
>> index=(1:3)'
index =
1
2
3
>> matrix(index)
ans =
10 11 12
As you can see, here the size of the answer does not agree with the size of the matrix 'index'. This inconsistency in the sizes of the indexing matrix and the ans matrix prevents me from writing a piece of code accepting an indexing matrix of an arbitrary size.
I wonder if someone else has come across this problem before and has found some sort of solution to it; in other words, how can I force MATLAB to give me an ans matrix of the same size as the arbitrarily sized indexing matrix?
Cheers
Solution
#Dev-iL has nicely explained here why this is the way Matlab behaves, and #Dan has presented a general solution here. However, there was a simpler ad-hoc workaround for my code which I've explained here.
The reason comes from the function subsref.m, which is called when using parentheses:
%SUBSREF Subscripted reference.
% A(I) is an array formed from the elements of A specified by the
% subscript vector I. The resulting array is the same size as I except
% for the special case where A and I are both vectors. In this case,
% A(I) has the same number of elements as I but has the orientation of A.
As you can see, in the special case of vectors, the shape of the result will be the same as that of matrix in your example.
As for a workaround, IMHO Dan has the cleanest solution.
Preallocate the output matrix with the size of index matrix.
Then assign corresponding indices the corresponding values
out = zeros(size(index));
out(index) = matrix(index);
Example
index = (1:3)'
index =
1
2
3
>> out = zeros(size(index))
out =
0
0
0
>> in = magic(3)
in =
8 1 6
3 5 7
4 9 2
>> out(index) = in(index)
out =
8
3
4
>>
Dev-iL has explained why it is like that. Here is a potential workaround:
reshape(matrix(index),size(index))

Matlab complex matrix magnitude

I have a matrix S something like:
1 4 7
2 5 8
3 6 9
Then I make a=complex(S{2},S{3}) and wanted to find the abs(a);. This is not possible in MATLAB as a is not an integer - it is a matrix. How can I get the magnitude of each row of matrix a?
PS: the matrix is read from a text file using textscan() as S = textscan(fileID,'%d %d %d', 'delimiter','\t');.
Second question:
Assuming again hav the following S matrix.
1 4 7 2 1
2 5 8 3 4
3 6 9 6 8
Now I wanted to arrange them in such way that column 2,3 and 4,5 alternate like this:
4 2
7 1
5 3
8 4
6 6
9 8
How can I do that without using a loop?
Thanks.
Going with my assumption in the comments, I'm going to assume that the second column consists of your real component of your matrix while the third column consists of your imaginary components. Your matrix S is actually a cell array of elements. You don't need to use complex then abs. You can simply take each of the columns, individually square them, add them together and take the square root. What I would do is convert the cell array into a 2D matrix, cast it to double to allow for floating point precision when finding the magnitude, and do what I just did above. This is necessary because abs and sqrt will only work for floating-point numbers. Your elements in S are already int32 due to the %d delimiter from textread. In other words:
Smat = double(cell2mat(S));
realComp = Smat(:,2);
imagComp = Smat(:,3);
mag = sqrt(realComp.^2 + imagComp.^2);
mag will thus return the magnitude of each row for you, assuming that the second column is the real component and the third component is the imaginary component as we specified.
However, if you're dead set on using complex and abs, you can do it like so:
Smat = double(cell2mat(S));
imagNumbers = complex(Smat(:,2), Smat(:,3));
mag = abs(imagNumbers);
This should still give you the same results as we talked about above.
Edit
Seeing your edit in your post above, we can achieve that quite easily by subsetting the matrix, then applying reshape to each part of the matrix you want. In other words:
Smat = double(cell2mat(S));
realMat = Smat(:,2:3); %// Grab second and third columns
imagMat = Smat(:,4:5); %// Grab fourth and fifth columns
realCol = reshape(realMat.', [], 1); % // Form the columns like you specified
imagCol = reshape(imagMat.', [], 1);
finalMatrix = [realCol imagCol];
finalMatrix should contain those two columns that you specified above in a single matrix.

Matlab column aggregation

Is there a function in MATLAB which allows to aggregate (or we can say sum) columns in a matrix per defined number of columns?
For example I have:
A =
1 2 3 4 5 6
9 10 11 12 13 14
17 18 19 20 21 22
I wish to aggregate every 2 columns, like this: col1+col2, and then col3+col4, and then col5+col6, so my output is:
A_agg =
3 7 11
19 23 27
35 39 43
I couldn't find a built-in function and was trying to write a for loop but I couldn't manage to do it since I am quite new to programming. Do you have any suggestions/solutions how this could be solved with a for loop, or maybe there is a built-in function?
Since sum operates down columns in a matrix, I first reshape A so that it has 2 rows and 9 columns, then sum down each column. Then reshape back to the desired output matrix A_agg.
A=[1 2 3 4 5 6
9 10 11 12 13 14
17 18 19 20 21 22]
[m,n]=size(A);
A_agg=reshape(sum(reshape(A',2,[])),m,[])'
You can use a combination of mat2cell and cellfun. You can use mat2cell to split up your matrices into individual 2 column chunks. Each chunk would be stored as a cell in a cell array. You can then use cellfun to take each cell and sum row-wise. After you're done, you can use cell2mat to convert back.
Using your example:
A = [1:6;9:14;17:22];
B = mat2cell(A, 3, [2 2 2]);
C = cellfun(#(x) sum(x,2), B, 'UniformOutput', false);
A_agg = cell2mat(C);
A_agg should thus give you:
A_agg =
3 7 11
19 23 27
35 39 43
Let's walk through the code slowly:
A is defined as we had before. B will be a cell array, and will segment your matrix into matrices of 2 columns per cell. The first parameter is the matrix you want to decompose (in our case A). The second parameter tells you how many rows each segment should have. Because we want all of the matrices to have the same number of rows, we thus supply one number which is 3. After, you specify the number of columns you want per matrix. Because there are 6 columns, we need 3 matrices, and so you'd specify a vector of [2 2 2].
C is the output of cellfun, where cellfun applies a function to every single element in a cell matrix. What you want to do here is for each cell (essentially each matrix), you want to sum row-wise. The first parameter is an anonymous function that takes in a matrix from each cell, and sums row-wise. The second parameter is the cell array we just created. You'll notice that we have an additional flag to set: UniformOutput. The reason why you have to set UniformOutput = false is because if you apply cellfun without that flag, the expected result at the end of the function you apply to each cell is scalar. Because we are outputting a column vector instead, we have to set this flag to false.
A_agg will thus aggregate all of your cells back to matrix form.
If you want to do this for any size matrix, bear in mind that there has to be an even amount of columns for this work. What I mean by even is that the number of columns has to be evenly divisible by 2. You would thus re-run the code like so:
B = mat2cell(A, size(A,1), 2*ones(1, size(A,2)/2));
C = cellfun(#(x) sum(x,2), B, 'UniformOutput', false);
A_agg = cell2mat(C);
Another possibility, if you have the Image Processing Toolbox, is to use blockproc. Let n denote the number of columns to be aggregated (2 in your example). Then:
A_agg = blockproc(A, [size(A,1) n], #(x) sum(x.data, 2));

Locations of pixels and setting them to `1`

In matlab, after meeting a specific criterion, I used to return back the pixel itself and store it in the vector pixels as follows:
pixels(index) = y(i,j);
Now, I would like to return the location of those pixels. Should I do the following?
pixels(index) = i,j;
EDIT
If I want to then set those indexes to the value 1, I do the following, right?
for i=1:m
for j=1:n
y(i,j)=1
end
end
Thanks.
It is extremely inefficient to do so in a nested loop in Matlab.
Using sub2ind can help you do so much faster:
y( sub2ind( size(y), i, j ) ) = 1;
EDIT - sub2ind
What sub2ind does?
Suppose you have a matrix M of size [4 6]:
M = [ 1 5 9 13 17 21
2 6 10 14 18 22
3 7 11 15 19 23
4 8 12 16 20 24 ];
You wish to access two elements: the one at the first row and second column, and another at the fourth row and the fifth column.
In that case you have the rows you wish to access r = [ 1 4 ] and the columns you wish to access c = [ 2 5 ]. However, if you try and access
>> M( r, c )
This is a 2x2 matrix
ans =
5 17
8 20
And not the two elements you were looking for (which are 5 and 20).
What sub2ind does is convert the row/column indices you have into linear indices
>> sub2ind( size(M), r, c )
ans =
5 20
which happens to be the linear indices of the requested entries.
You can think of linear indices as the single index required to access an element in a matrix in the case that the matrix was converted to a vector stacking its columns one after the other.
A few comments:
Matlab has a few ways of indexing matrices: by row / column indices (like i and j in your question). By linear indices (like index in your question). However, the more efficient way is to use logical indexing: that is, using a matrix of the same size as y with true for the entries you wish to set / get.
So, in your example, if you could get such a logical matrix instead of index or i and j it would have been better.
Matlab has many advantages over other programing languages. One of them is its ability to perform vector/matrix operations extremely efficient. Resorting to loops, or worse, nested loops, is something that should be avoided in Matlab.
It is not a good practice to use i and j as variables in Matlab.
If you want to find the occurrence of a value y(i,j) simply evaluate
idx = (pixels == y(i,j));
Depending on your variables you can then probably do
index(idx) = 1;

matrix get min values of a matrix before max values occurred

I was trying to get the min values of a matrix before the max values of the matrix occurred. I have two matrices: matrix data and matrix a. Matrix a is a subset of matrix data and is composed of the max values of matrix data. I have the following code but obviously doing something wrong.
edit:
Matrix a are the max values of matrix data. I derived it from:
for x=1:size(data,1)
a(x)=max(data(x,:));
end
a=a'
clear x
matrix b code:
for x=1:size(data,1)
b(x)=min(data(x,(x<data==a)));
end
b=b'
clear x
matrix data matrix a matrix b
1 2 3 4 4 1
6 5 4 7 7 4
9 6 12 5 12 6
I need all the min values that occurred before to matrix a occurred in matrix data
Short and simple:
[a,idxmax] = max(data,[],2);
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii))), 1:size(data,1));
which is the same as
b=NaN(1,size(data,1)); % preallocation!
for ii=1:size(data,1)
b(ii) = min(data(ii,1:idxmax(ii)));
end
Ignore maximum itself
If you want minimum of everything really before (and not including the maximum), it's possible that the maximum is the first number, and you try taking minimum of an empty matrix. Solution then is to use cell output, which can be empty:
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii)-1)), 1:size(data,1),'uni',false);
Replace empty cells with NaN
If you want to replace empty cells to Nan and then back to a matrix use this:
b(cellfun(#isempty,b))={NaN};
b=cell2mat(b);
or simply use the earlier version and replace b(ii) with NaN when it is equal to a(ii) same outcome:
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii))), 1:size(data,1));
b(b'==a) = NaN
Example:
data=magic(4)
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
outputs:
a' = 16 11 12 15
b =
16 5 6 4
and
b =[1x0 double] [5] [6] [4]
for the 2nd solution using cell output and ignoring the maximum itself also.
And btw:
for x=1:size(data,1)
a(x)=max(data(x,:));
end
a=a'
clear x
can be replaced with
a=max(data,[],2);
It's not pretty but this is the only way I found so far of doing this kind of thing without a loop.
If loops are ok I would recommend Gunther Struyf answer as the most compact use of matlab's in-built array looping function, arrayfun.
Some of the transposition etc may be superfluous if you're wanting column mins instead of row...
[mx, imx] = max(data');
inds = repmat(1:size(data,2), [size(data,1),1]);
imx2 = repmat(imx', [1, size(data,2)]);
data2 = data;
data2(inds >= imx2) = inf;
min(data2');
NOTE: if data is not needed we can remove the additional data2 variable, and reduce the line count.
So to demonstrate what this does, (and see if I understood the question correctly):
for input
>> data = [1,3,-1; 5,2,1]
I get minima:
>> min(data2')
ans = [1, inf]
I.e. it only found the min values before the max values for each row, and anything else was set to inf.
In words:
For each row get index of maximum
Generate matrix of column indices
Use repmat to generate a matrix, same size as data where each row is index of maximum
Set data to infinity where column index > max_index matrix
find min as usual.