MATLAB: how to pass in the diagonal of a matrix as an argument in another matrix? - arrays

Let c be 2D array, and x and y be 1D arrays of the same length (for instance, let's have x=1:7 and y=3:9).
I need to find a way to pass in arguments from x and y in the way I will describe below.
If I put simply c(x,y) it will give a 7 by 7 matrix. I don't want that.
Instead, I want to pass in the diagonal of the [x y] matrix: ((x(1), y(1)), (x(2), y(2))...(x(7), y(7)). Is there a way to do this without a for loop or any iterative statement?

You are looking for sub2ind function
res = c( sub2ind(size(c), x, y ) )

There's an easier way. If you're looking for a diagonal, use diag. If you have a matrix c:
c =
5 8 4 2 9 1 6 1 1
9 8 7 5 9 3 2 7 5
2 3 9 10 2 1 4 2 2
3 2 9 2 4 4 7 2 4
3 9 10 8 7 5 2 1 8
5 6 3 7 6 1 10 5 2
6 1 7 3 10 8 2 4 2
you can find the main diagonal by using diag with no extra arguments:
>> diag(c)
ans =
5
8
9
2
7
1
2
The second argument, though, indicates which diagonal you want as an offset from the main diagonal. So the default diagonal is equal to 0. If you want the diagonal starting at c(1,3), that's 2 above the main diagonal, so
>> diag(c,2)
ans =
4
5
2
4
2
5
2
Similarly, if you want the diagonal starting at c(4,1), the offset is -3:
>> diag(c,-3)
ans =
3
9
3
3

Related

MATLAB: Remove specific elements from array

Question 1: I have a 1x15 array, comprising of positive integers and negative integers. I wish to implement a MATLAB code which keeps all positive integers and skips the cells with negative contents.
I have tried the following:
X = [1 2 3 4 5 -10 1 -5 4 6 8 9 2 4 -2];
[r c] = size(X);
for i=1:r
for j=1:c
if X(i,j)<0
X(i,j)=X(i,j+1)
end
end
end
The output should be:
X_new = [1 2 3 4 5 1 4 6 8 9 2 4]
How do I do this?
Question 2:
X = [1 2 3 4 5 -10 1 -5 4 6 8 9 2 4 -2]
Y = [5 3 8 9 4 5 6 7 4 7 9 5 2 1 4]
From Question 1,
X_new = [1 2 3 4 5 1 4 6 8 9 2 4]
I need to delete the corresponding values in Y so that:
Y_new = [5 3 8 9 4 6 4 7 9 5 2 1]
How do I perform this?
In MATLAB, manipulating arrays and matrices can be done much easier than for-loop solutions,
in your task, can do find and delete negative value in the array, simply, as follows:
Idx_neg = X < 0; % finding X indices corresponding to negative elements
X ( Idx_neg ) = []; % removing elements using [] operator
Y ( Idx_neg ) = []; % removing corresponding elements in Y array

Matrix transformation in MATLAB

For example, I have a matrix A (Figure 1). When the variable n = 2, I want it to be transformed to the matrix B. The red rectangle shows the transformation rule of every column. According to this rule, when the n = 3, it can become the matrix C.
I have written a script using a for loop method, but it is a waste of time when the matrix A is very large (e.g. 11688* 140000). Is there an efficient way to solve this problem?
Figure 1:
Here is a way using reshape and implicit expansion:
result = reshape(A((1:size(A,1)-n+1) + (0:n-1).', :), n, []);
For example assume that n = 3. Implicit expansion is used to extract indices of rows:
row_ind = (1:size(A,1)-n+1) + (0:n-1).';
The following matrix is created:
1 2
2 3
3 4
Extract the desired rows of A:
A_expanded = A(row_ind, :)
When the matrix row_ind is used as an index it behaves like a vector:
1
2
1 2 3
2 3 -> 2
3 4 3
4
A_expanded =
3 5 7
6 8 9
2 6 3
6 8 9
2 6 3
1 2 1
Now A_expanded can be reshaped to the desired size:
result = reshape(A_expanded, n, []);
>>result =
3 6 5 8 7 9
6 2 8 6 9 3
2 1 6 2 3 1
If you have the Image Processing Toolbox you can use im2col as follows:
result = im2col(A, [n 1], 'sliding');

Efficient "window-select" array blocks?

Suppose I have the following array:
x = [a b
c d
e f
g h
i j];
I want to "swipe a window of two rows" progressively (one row at a time) along the array to generate the following array:
y = [a b c d e f g h
c d e f g h i j];
What is the most efficient way to do this? I don't want to use cellfun or arrayfun or for loops.
im2col is going to be your best bet here if you have the Image Processing Toolbox.
x = [1 2
3 4
5 6
7 8];
im2col(x.', [1 2])
% 1 2 3 4 5 6
% 3 4 5 6 7 8
If you don't have the Image Processing Toolbox, you can also easily do this with built-ins.
reshape(permute(cat(3, x(1:end-1,:), x(2:end,:)), [3 2 1]), 2, [])
% 1 2 3 4 5 6
% 3 4 5 6 7 8
This combines the all rows with the next row by concatenating a row-shifted version along the third dimension. Then we use permute to shift the dimensions around and then we reshape it to be the desired size.
If you don't have the Image Processing Toolbox, you can do this using simple indexing:
x =
1 2
3 4
5 6
7 8
9 10
y = x.'; %% Transpose it, for simplicity
z = [y(1:end-2); y(3:end)] %% Take elements 1:end-2 and 3:end and concatenate them
z =
1 2 3 4 5 6 7 8
3 4 5 6 7 8 9 10
You can do the transposing and reshaping in a simple step (see Suever's edit), but the above might be easier to read, understand and debug for beginners.
Here's an approach to solve it for a generic case of selecting L rows per window -
[m,n] = size(x) % Store size
% Extend rows by indexing into them with a progressive array of indices
x_ext = x(bsxfun(#plus,(1:L)',0:m-L),:);
% Split the first dim at L into two dims, out of which "push" back the
% second dim thus created as the last dim. This would bring in the columns
% as the second dimension. Then, using linear indexing reshape into the
% desired shape of L rows for output.
out = reshape(permute(reshape(x_ext,L,[],n),[1,3,2]),L,[])
Sample run -
x = % Input array
9 1
3 1
7 5
7 8
4 9
6 2
L = % Window length
3
out =
9 1 3 1 7 5 7 8
3 1 7 5 7 8 4 9
7 5 7 8 4 9 6 2

Extend a 2D-matrix to a 3D-matrix by multiplying with a vector [duplicate]

This question already has an answer here:
Multiply 2D Matrix with vector to span third dimension - MATLAB
(1 answer)
Closed 7 years ago.
Here what I would like to achieve:
I have a matrix C
C=[1 2 3; 4 5 6; 7 8 9];
And a vector a
a=[1 2];
I would like to make such an operation, that each element of the a vector is multiplied with C ( scalar multiplication ) and out comes a 3-dimensional array D:
(:,:,1) =
1 2 3
4 5 6
7 8 9
(:,:,2) =
2 4 6
8 10 12
14 16 18
It would certainly work with a loop, but, since I'll need this operation on multiple occasions, a oneliner would be a great saver.
This is a beautiful example for the use of bsxfun and reshape. While #thewaywewalks proposes first calling bsxfun and reshaping the result, I'd suggest the opposite. This makes one of the key concepts of bsxfun - the singleton dimension expansion - more clear:
out = bsxfun(#times,C,reshape(a,1,1,[]))
ans(:,:,1) =
1 2 3
4 5 6
7 8 9
ans(:,:,2) =
2 4 6
8 10 12
14 16 18
With reshape(a,1,1,[]), you make a be in the third dimension. If you now apply bsxfun, it will multiply the matrix C with each element of a.
Some reshape'ing and some bsxfun will do:
out = reshape(bsxfun(#mtimes, C(:), a(:).'), [size(C),numel(a)] )
As suggested in hbaderts answer one could also use bsxfun's capability of dimension expansion, and provide a permuted vector of factors:
out = bsxfun(#mtimes,C,permute(a,[3,1,2]))
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18
I have another method for the benchmark compare...
IMO it's the neatest way, at least for the syntax/readability term:
out = reshape(kron(a,C),[size(C),numel(a)]);
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18
Another possibility is to use matrix multiplication of C as a column vector times a as a row vector (this gives all element-wise products), and then reshape the result:
out = reshape(C(:)*a, size(C,1), size(C,2), numel(a));
EDIT (BENCHMARKING): Since several solutions (including mine below) have been suggested, here is some rough benchmarking to compare the different solutions, using larger arrays:
a=1:10;
N=1000; timers=zeros(N,6);
for ii=1:N; C=rand(400);
tic; out = repmat(C,[1,1,numel(a)]).*reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)]); timers(ii,1)=toc;
tic; out = bsxfun(#times,C,reshape(a,1,1,[])); timers(ii,2)=toc;
tic; out = reshape(C(:)*a, size(C,1), size(C,2), numel(a)); timers(ii,3)=toc;
tic; out = bsxfun(#mtimes,C,permute(a,[3,1,2])); timers(ii,4)=toc;
tic; out = reshape(bsxfun(#mtimes, C(:), a(:).'), [size(C),numel(a)] ); timers(ii,5)=toc;
tic; out = reshape(kron(a,C),[size(C),numel(a)]); timers(ii,6)=toc;
end;
mean(timers)
ans =
0.0080863 0.0032406 0.0041718 0.015166 0.0074462 0.0033051
... suggesting that #hbaderts solution is fastest, then #Adiel's, then #Luis Mendo's, then #thewaywewalk's (1), then mine, then #thewaywewalk's (2).
My solution:
Another option, using repmat and reshape (no bsxfun):
out = repmat(C,[1,1,numel(a)]).*reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)])
out(:,:,1) =
1 2 3
4 5 6
7 8 9
out(:,:,2) =
2 4 6
8 10 12
14 16 18
This is the element-wise multiplication of two arrays. The first is your original matrix C repeated numel(a) times in the third dimension:
repmat(C,[1,1,numel(a)])
ans(:,:,1) =
1 2 3
4 5 6
7 8 9
ans(:,:,2) =
1 2 3
4 5 6
7 8 9
The second is the same size as the first, with each slice containing the corresponding element of a:
reshape(repelem(a,size(C,1),size(C,2)),[size(C),numel(a)])
ans(:,:,1) =
1 1 1
1 1 1
1 1 1
ans(:,:,2) =
2 2 2
2 2 2
2 2 2

Matlab- moving numbers to new row if condition is met

I have a variable like this that is all one row:
1 2 3 4 5 6 7 8 9 2 4 5 6 5
I want to write a for loop that will find where a number is less than the previous one and put the rest of the numbers in a new row, like this
1 2 3 4 5 6 7 8 9
2 4 5 6
5
I have tried this:
test = [1 2 3 4 5 6 7 8 9 2 4 5 6 5];
m = zeros(size(test));
for i=1:numel(test)-1;
for rows=1:size(m,1)
if test(i) > test(i+1);
m(i+1, rows+1) = test(i+1:end)
end % for rows
end % for
But it's clearly not right and just hangs.
Let x be your data vector. What you want can be done quite simply as follows:
ind = [find(diff(x)<0) numel(x)]; %// find ends of increasing subsequences
ind(2:end) = diff(ind); %// compute lengths of those subsequences
y = mat2cell(x, 1, ind); %// split data vector according to those lenghts
This produces the desired result in cell array y. A cell array is used so that each "row" can have a different number of columns.
Example:
x = [1 2 3 4 5 6 7 8 9 2 4 5 6 5];
gives
y{1} =
1 2 3 4 5 6 7 8 9
y{2} =
2 4 5 6
y{3} =
5
If you are looking for a numeric array output, you would need to fill the "gaps" with something and filling with zeros seem like a good option as you seem to be doing in your code as well.
So, here's a bsxfun based approach to achieve the same -
test = [1 2 3 4 5 6 7 8 9 2 4 5 6 5] %// Input
idx = [find(diff(test)<0) numel(test)] %// positions of row shifts
lens = [idx(1) diff(idx)] %// lengths of each row in the proposed output
m = zeros(max(lens),numel(lens)) %// setup output matrix
m(bsxfun(#le,[1:max(lens)]',lens)) = test; %//'# put values from input array
m = m.' %//'# Output that is a transposed version after putting the values
Output -
m =
1 2 3 4 5 6 7 8 9
2 4 5 6 0 0 0 0 0
5 0 0 0 0 0 0 0 0

Resources