Matlab: Assemble submatrices whose #cols and #rows are stored in a vector - arrays

I have two vectors, R and C, which have the number of rows and columns, respectively, of submatrices that I need to assemble in a ones matrix I (40x20). There's 12 submatrices total.
R = [4 2 4 4 2 4];
C = [4 16 16 4];
Moreover, all the elements of each submatrix have its value stored in vector k:
k = [3 2 3 3 2 3 2 1 2 2 1 2 2 1 2 2 1 2 3 2 3 3 2 3 ]; % 24 elements
Thus for instance, submatrix M(1:4,1:4) has 4 rows, and 4 columns and value equal to k(1) = 1.
QUESTION: How can I assemble matrix M with all submatrices?
Any ideas?
Thanks!
EDIT:
The matrix M should look like this:
and the submatrices:
and the values of k:

Here is a vectorized solution:
R1 = repelem(1:numel(R), R);
C1 = repelem(1:numel(C), C);
[CC RR] = meshgrid(C1, R1);
idx = sub2ind([numel(R), numel(C)], RR, CC);
result = k(idx);
Instead you can use cell array, fill it with sub matrices and then convert the cell array to a matrix.
carr = cell(numel(R), numel(C));
k1 = reshape(k,numel(R),numel(C));
for ii = 1:numel(R)
for jj = 1:numel(C)
carr(ii,jj)=repmat(K1(ii,jj), R(ii), C(jj));
end
end
result = cell2mat(carr)

Related

Sort a matrix according to ordering in another matrix

I am trying to sort an array based on another array. I tried the sort method with index return, but it is somehow behaving strangely.
y = [1 2 3; 2 3 4]
x = [1 3 4; 2 2 3]
[yy, ii] = sort(y,'descend');
yy =
2 3 4
1 2 3
ii =
2 2 2
1 1 1
But my x(ii) is not the matrix sorted based on y.
x(ii) =
2 2 2
1 1 1
I am expecting the matrix to be:
x(ii) =
2 2 3
1 3 4
How can I sort the matrix x according to another matrix y?
ii are row subscripts but are being inputted by you as linear indices.
You need to convert them to relevant linear indices before proceeding i.e.
>> szx = size(x);
>> x(sub2ind(szx, ii, repmat(1:szx(2),szx(1),1)))
ans =
2 2 3
1 3 4

Reduce 3d to 2d array by using an index vector for one dimension

I have a M x N x O matrix and I would like to reduce it to a MxN matrix in MATLAB using a vector b of size M that contains the index of the element in the third dimension that is to be kept.
What it does then is build a 2d array with its entries selected from various pages of the original 3d array.
I have this loop but I am interested in a loopless solution.
for i = 1:M
for j = 1:N
tmp(i, j) = P(i, j, b(i));
end
end
The easiest way may just be to remove the j loop in your code:
for ii = 1:M
tmp(ii, :) = P(ii, :, b(ii));
end
But for the sake of comparison, here's a solution without a loop.
Given a 3d array P:
M = 7;
N = 5;
O = 6;
P = ones(M, N, O) .* permute(1:O, [3 1 2]);
(in this case I've used a 3d array where each element is equal to its O index)
and b, of size Mx1 with values from 1..O:
b = randi(O, M, 1)
you can construct the subscripts of all of the elements of P(:,:,1) and use b to select which plane to use:
[rr, cc] = ndgrid(1:M, 1:N);
inds = sub2ind(size(P), rr(:), cc(:), b(rr(:)));
tmp = reshape(P(inds), M, N)
For:
b.' = 5 4 1 5 3 1 3
we get:
tmp =
5 5 5 5 5
4 4 4 4 4
1 1 1 1 1
5 5 5 5 5
3 3 3 3 3
1 1 1 1 1
3 3 3 3 3
The elements of each row corresponds to the element in b as expected.

Get elements of a matrix that are greater than sum of their two indices in row major order

I'm Writing a function called large_elements that takes input an array named X that is a matrix or a vector. The function identifies those elements of X that are greater than the sum of their two indexes.
For example, if the element X(2,3) is 6, then that element would be identified because 6 > (2 + 3). The output of the function gives the indexes(row and column sub) of such elements found in row-major order. It is a matrix with exactly two columns. The first column contains the row indexes, while the second column contains the corresponding column indexes.
Here is an example, the statement
indexes = large_elements([1 4; 5 2; 6 0])
should give the output like this:
[1 2; 2 1; 3 1]
If no such element exists,
the function returns an
empty array.
I have came up with the following code
function indexes = large_elements(A)
[r c] = size(A);
ind = 1;
for ii = 1:r
for jj = 1:c
if A(ii,jj) > ii + jj
indexes(ind,:) = [ii jj];
ind = ind + 1;
else
indexes = [];
end
end
end
end
But the results are not as expected. Any help would be appreciated.
One vectorised approch using bsxfun, find and ind2sub
A = randi(8,5); %// Your matrix
%// finding sum of the indexes for all elements
indSum = bsxfun(#plus, (1:size(A,1)).', 1:size(A,2));
%// generating a mask of which elements satisfies the given condition (i.e A > indSum)
%// Transposing the mask and finding corresponding indexes
[c,r] = find(bsxfun(#gt, A, indSum).') ;
%// getting the matrix by appending row subs and col subs
out = [r,c]
Results:
Input A:
>> A
A =
4 4 7 2 2
1 3 4 8 3
8 8 2 8 7
8 3 4 5 1
4 1 1 1 1
Output in row-major order:
out =
1 1
1 2
1 3
2 4
3 1
3 2
3 4
4 1
Note: Getting subs in row-major order is tricky here
Also here is your correct loopy approach
[r, c] = size(A);
ind = 0;
indexes = [];
for ii = 1:r
for jj = 1:c
if A(ii,jj) > ii + jj
ind = ind + 1;
indexes(ind,:) = [ii jj];
end
end
end
That is because whenever you encounter an element which is smaller than the sum of its indices you are reinitializing the array to null. So the output is coming out to be null. You should not initialize it to null on the else condition.

adding values to diagonals of matrix using element-wise addition in matlab

I am writing a script that operates on matrices, and I have run into the problem of needing to add the sum of the diagonals of a previous matrix to the diagonal elements of a new matrix. The code I have so far for this particular function (described in more detail below) is:
t = 1;
for k = (m-1):-1:-(m-1)
C = bsxfun(#plus, diag(B, k), d);
g(t) = sum(diag(B, k));
t = t + 1;
end
where d is a 1x3 array, and C is supposed to be a 3x3 array; however, C is being output as a 1x3 array in such a way that the first diagonal is being summed and added to d, then the main diagonal is being summed and added to d, and the final diagonal is being summed and added to d.
Is there a way I can get the values of C to be such that the first diagonal is the sum of it's individual elements added to the last element of d, the main diagonal's individual elements added to the middle element of d, and the bottom diagonal's elements added to the first element of d? (while still working for any array size?)
Here is a picture that describes what I'm trying to achieve:
Thanks!
You can use toeplitz to generate a matrix containing the values that need to be added to your original matrix:
M = [5 5 5; 7 7 7; 9 9 9]; %// data matrix
v = [1 11 4 3 2]; %// data vector
S = toeplitz(v);
S = S(1:(numel(v)+1)/2, (numel(v)+1)/2:end);
result = M+S;
Or, as noted by #thewaywewalk, you can do this more directly as follows:
M = [5 5 5; 7 7 7; 9 9 9]; %// data matrix
v = [1 11 4 3 2]; %// data vector
result = M + toeplitz(v(size(M,1):-1:1), v(size(M,2):end));
Assuming B to be a square shaped matrix, listed in this post would be one bsxfun based vectorized approach. Here's the implementation -
N = size(B,1) %// Store size of B for later usage
%// Find a 2D grid of all indices with kth column representing kth diagonal of B
idx = bsxfun(#plus,[N-numel(B)+1:N+1:N]',[0:2*N-2]*N) %//'
%// Mask of all valid indices as we would see many from the 2D grid
%// going out of bounds of 2D array, B
mask = idx>numel(B) | idx<1
%// Set all out-of-bounds indices to one, so that in next step
%// we could index into B in a vectorized manner and sum those up with d
idx(mask)=1
sum1 = bsxfun(#plus,B(idx),d(:).') %//'
%// Store the summations at proper places in B with masking again
B(idx(~mask)) = sum1(~mask)
Sample run -
B =
1 9 0
7 9 4
6 8 7
d =
4 9 5 8 2
B =
6 17 2
16 14 12
10 17 12
Code:
The following code adds the sums of the diagonals of A to the corresponding diagonals in the matrix B. The code works for matrices A, B of equal size, not necessarily square.
A = magic(4);
B = magic(4);
D = bsxfun(#minus, size(A,2)+(1:size(A,1)).', 1:size(A,2)); %'
sumsDiagsA = accumarray(D(:), A(:)); %// Compute sums of diagonals (your 'd')
B = B + sumsDiagsA(D); %// Add them to the matrix
Explanation:
First we build a matrix that numbers all diagonals beginning from the rightmost diagonal:
>> D = bsxfun(#minus, size(A,2)+(1:size(A,1)).', 1:size(A,2))
D =
4 3 2 1
5 4 3 2
6 5 4 3
7 6 5 4
Then we compute sumsDiagsA as the sum of the diagonals via accumarray:
sumsDiagsA = accumarray(D(:), A(:));
The variable sumsDiagsA is what you refer to as d in your code.
Now we use indexing to the vector containing the sums and add them to the matrix B:
C = B + sumsDiagsA(D);
Assuming you have already computed your vector d, you don't need the accumarray-step and all you need to do is:
D = bsxfun(#minus, size(B,2)+(1:size(B,1)).', 1:size(B,2)); %'
C = B + d(D);

Matlab reshape horizontal cat

Hi I want to reshape a matrix but the reshape command doesn't order the elements the way I want it.
I have matrix with elements:
A B
C D
E F
G H
I K
L M
and want to reshape it to:
A B E F I K
C D G H L M
So I know how many rows I want to have (in this case 2) and all "groups" of 2 rows should get appended horizontally. Can this be done without a for loop?
You can do it with two reshape and one permute. Let n denote the number of rows per group:
y = reshape(permute(reshape(x.',size(x,2),n,[]),[2 1 3]),n,[]);
Example with 3 columns, n=2:
>> x = [1 2 3; 4 5 6; 7 8 9; 10 11 12]
x =
1 2 3
4 5 6
7 8 9
10 11 12
>> y = reshape(permute(reshape(x.',size(x,2),n,[]),[2 1 3]),n,[])
y =
1 2 3 7 8 9
4 5 6 10 11 12
Cell array approach -
mat1 = rand(6,2) %// Input matrix
nrows = 3; %// Number of rows in the output
[m,n] = size(mat1);
%// Create a cell array each cell of which is a (nrows x n) block from the input
cell_array1 = mat2cell(mat1,nrows.*ones(1,m/nrows),n);
%// Horizontally concatenate the double arrays obtained from each cell
out = horzcat(cell_array1{:})
Output on code run -
mat1 =
0.5133 0.2916
0.6188 0.6829
0.5651 0.2413
0.2083 0.7860
0.8576 0.3032
0.1489 0.4494
out =
0.5133 0.2916 0.5651 0.2413 0.8576 0.3032
0.6188 0.6829 0.2083 0.7860 0.1489 0.4494

Resources