How to store submatrix in array - arrays

I have a square matrix B and I want to extract its submatrix which has consecutive row numbers and column numbered 1 through k, with k every natural number no more than n (size of my matrix). It also needs to have non-zero main diagonal entries.
Furthermore, I want to store the submatrices in an array form, (the next step is to check if their determinants are positive, but I won't include that in this question. Here is the code I have built:
for i = 1:n
for j = 1:n-i+1
submat2{i,j} = B([j:j+i-1],[1:i]);
for k = 1:i
maindiag{i,j,k} = prod((submat2{i,j}(i,i) ~= 0));
end
matmaindiag = []
for l = 1:size(maindiag(i,j,:),3)
matmaindiag = [matmaindiag cell2mat(maindiag(i,j,l))]
if prod(matmaindiag ~= 0)
boundsub{end+1} = submat2{i,j};
end
end
end
end
Is there any better way to do this?
For example, if I have:
B =
6 7 8 9
11 12 13 14
0 17 18 19
0 0 23 24
then the submatrices I would like to extract are:
B([1],[1]), B([1],[2]), B([1,2],[1,2]), B([2,3],[1,2]), B([1,2,3],[1,2,3]), B([2,3,4],[1,2,3]), and B itself
since they:
consisted of entries from columns of B that numbered consecutively from 1 through k (the size of the submatrix itself) and
From the consecutively numbered rows of B
Don't have any zero-valued diagonal entries
Thanks for any help and thoughts :)

I have come up with this
n = size(B,1)
for i = 1:n
for j = 1:n-i+1
submat{i,j} = B([j:j+i-1],[1:i]);
end
end
bousub = []
for i = 1:n
for j = 1:n-i+1
dia = diag(submat{i,j});
if (prod(dia) ~= 0)
bousub{end+1} = submat{i,j};
end
end
end

Related

Updating a matrix from list of redundant indices

I have a matrix A, a list of indices is and js, and a list of values to add to A, ws. Originally I was simply iterating through A by a nested for loop:
for idx = 1:N
i = is(idx);
j = js(idx);
w = ws(idx);
A(i,j) = A(i,j) + w;
end
However, I would like to vectorize this to increase efficiency. I thought something simple like
A(is,js) = A(is,js) + ws
would work, and it does as long as the is and js don't repeat. Said differently, if I generate idx = sub2ind(size(A),is,js);, so long as idx has no repeat values, all is well. If it does, then only the last value is added, all previous values are left out. A concrete example:
A = zeros(3,3);
indices = [1,2,3,1];
additions = [5,5,5,5];
A(indices) = A(indices) + additions;
This results in the first column having values of 5, not 5,5,10.
This is a small example, but in my actual application the lists of indices are really long and filled with redundant values. I'm hoping to vectorize this to save time, so going through and eliminating redundancies isn't really an option. So my main question is, how do I add to a matrix from a given set of redundant indices? Alternatively, is there another way of working through this without any sort of iteration?
To emphasize a nice property of accumarray (accumarray actually works with two indices)
With the example from Luis Mendo:
is = [2 3 3 1 1 2].';
js = [1 3 3 2 2 4].';
ws = [10 20 30 40 50 60].';
A3 = accumarray([is js],ws);
%% A3 =
%% 0 90 0 0
%% 10 0 0 60
%% 0 0 50 0
If I understand correctly, you only need full(sparse(is, js, ws)). This works because sparse accumulates values at matching indices.
% Example data
is = [2 3 3 1 1 2];
js = [1 3 3 2 2 4];
ws = [10 20 30 40 50 60];
% With loop
N = numel(is);
A = zeros(max(is), max(js));
for idx = 1:N
i = is(idx);
j = js(idx);
w = ws(idx);
A(i,j) = A(i,j) + w;
end
% With `sparse`
A2 = full(sparse(is, js, ws));
% Check
isequal(A, A2)

How do a create a list of indexes based on the value and a new array from that? MATLAB

I would like to eliminate all the columns in which the third row contain zero values in my dataset.
As an example:
original_data = [1 2 3 4 5; 1 2 3 4 5; 0 0 0 1 2]
For the first three columns (with zeros on third line), I would like to create a new array in which the colums with zeros in third line are deleted to get the result:
new_data = [ 4 5; 4 5; 1 2]
I would also like an array of the column indices of the non-zero values in the original array.
For example:
original_indices = [4, 5]
I tried:
dados_teste = dados_out_15;
dados_p6 = [];
[m,n] = size(dados_teste)
for i = 1:n
if dados_teste(3:i) == 0;
dados_p6 = dados_teste(:,i)
else
dados_p6 = dados_teste(:,n)
end
end
But it clearly does not work...
I would apply the find() function to find all the non-zero indices, then apply matrix indexing to generate a new array that only contains the columns corresponding to the non-zero indices in the third row.
Sample_Array = [20 30 40 50; 30 20 70 90; 0 2 1 2];
%Grabbing the third row of the matrix%
Third_Row = Sample_Array(3,:);
%Finding all the non-zero indices%
[Non_Zero_Indices] = find(Third_Row);
%Using matrix indices to generate a new array based on the non-zero
%indicies%
New_Matrix = Sample_Array(:,Non_Zero_Indices);
%Printing matrices%
Sample_Array
New_Matrix
Non_Zero_Indices

alternating and shifting sections of an array

I have a n x m array (could be any size array but it will not be a 1 x m) and I want to rotate / shift each square loop individually no matter the array size.
How can I alternate the rotation / shift each square loop no matter the size of the array.
Please note: I'm not trying to calculate the values in the array but shift the values.
My thought process was to get the values of each "square loop" and place them into one row and do a circshift then place them back into another array.
I ran into problems trying to get the values back into the original n x m array size and I wasn't sure how I could loop through the process for different n x m arrays.
The pink highlighted section, left of the arrows is the starting position of the array and it's "loops" and the green highlighted section, right of the arrows is the type of rotation / shift of the values that I'm trying to create. The array could have more than 3 "loops" this is just an example.
Code below:
I=[1:5;6:10;11:15;16:20;21:25;26:30]
[rw,col] = size(I);
outer_1=[I(1,:),I(2:end-1,end).',I(end,end:-1:1),I(end-1:-1:2,1).'] %get values in one row (so I can shift values)
outer_1_shift=circshift(outer_1,[0 1]) %shift values
new_array=zeros(rw,col);
Ps: I'm using Octave 4.2.2 Ubuntu 18.04
Edit: The circshift function was changed for Octave 5.0, the last edit made it compatible with previous versions
1;
function r = rndtrip (n, m, v)
rv = #(x) x - 2 * (v - 1);
r = [v * ones(1,rv(m)-1) v:n-v+1 (n-v+1)*ones(1,rv(m)-2)];
if (rv(m) > 1)
r = [r n-v+1:-1:v+1];
endif
endfunction
function idx = ring (n, m , v)
if (2*(v-1) > min (n, m))
r = [];
else
r = rndtrip (n, m, v);
c = circshift (rndtrip (m, n, v)(:), - n + 2 * v - 1).';
idx = sub2ind ([n m], r, c);
endif
endfunction
# your I
I = reshape (1:30, 5, 6).';
# positive is clockwise, negative ccw
r = [1 -1 1];
for k = 1:numel(r)
idx = ring (rows(I), columns(I), k);
I(idx) = I(circshift(idx(:), r(k)));
endfor
I
gives
I =
6 1 2 3 4
11 8 9 14 5
16 7 18 19 10
21 12 13 24 15
26 17 22 23 20
27 28 29 30 25
run it on tio
So, I had the same idea as in Andy's comment. Nevertheless, since I was already preparing some code, here is my suggestion:
% Input.
I = reshape(1:30, 5, 6).'
[m, n] = size(I);
% Determine number of loops.
nLoops = min(ceil([m, n] / 2));
% Iterate loops.
for iLoop = 1:nLoops
% Determine number of repetitions per row / column.
row = n - 2 * (iLoop - 1);
col = m - 2 * (iLoop - 1);
% Initialize indices.
idx = [];
% Add top row indices.
idx = [idx, [repelem(iLoop, row).']; iLoop:(n-(iLoop-1))];
% Add right column indices.
idx = [idx, [[iLoop+1:(m-(iLoop-1))]; repelem(n-(iLoop-1), col-1).']];
if (iLoop != m-(iLoop-1))
% Add bottom row indices.
idx = [idx, [repelem(m-(iLoop-1), row-1).'; (n-(iLoop-1)-1:-1:iLoop)]]
end
if (iLoop != n-(iLoop-1))
% Add left column indices.
idx = [idx, [[(m-(iLoop-1))-1:-1:iLoop+1]; repelem(iLoop, col-2).']]
end
% Convert subscript indices to linear indices.
idx = sub2ind(size(I), idx(1, :), idx(2, :));
% Determine direction for circular shift operation.
if (mod(iLoop, 2) == 1)
direction = [0 1];
else
direction = [0 -1];
end
% Replace values in I.
I(idx) = circshift(I(idx), direction);
end
% Output.
I
Unfortunately, I couldn't think of a smarter way to generate the indices, since you need to maintain the right order and avoid double indices. As you can see, I obtain subscript indices with respect to I, since this can be done quite easy using the matrix dimensions and number of loops. Nevertheless, for the circshift operation and later replacing of the values in I, linear indices are more handy, so that's why the sub2ind operation.
Input and output look like this:
I =
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25
26 27 28 29 30
I =
6 1 2 3 4
11 8 9 14 5
16 7 18 19 10
21 12 13 24 15
26 17 22 23 20
27 28 29 30 25
I was right, that the "shift direction" changes with every loop?
Hope that helps!
Caution: I haven't tested for generality, yet. So, please report any errors you might come across.

what is the matlab way without loop to multiply a matrix with a vector (extending to the 3rd dim)?

A simple example to illustrate all elements of a matrix multiplying each element of a vector to generate a 3D array.
M = reshape(1:12,4,3);
V = 1:2;
n = length(V);
A = nan([size(M),n]);
for ii = 1 : n
A(:,:,ii) = M * V(ii);
end
then
A(:,:,1) =
1 5 9
2 6 10
3 7 11
4 8 12
A(:,:,2) =
2 10 18
4 12 20
6 14 22
8 16 24
Or by repmat both M and V to the size of [4,3,2],
A = repmat(M,1,1,n) * reshape(V(ones(size(M)),:),[size(M),n])
It creates two 3D array by repmat besides the resulting 3d array A.
How to make it efficiently WITHOUT for loop and save the memory use?
According to the answer by #Lincoln,
A = bsxfun(#times, repmat(M,1,1,n), reshape(1:n, 1, 1, n));
repmat the vector V to 3d is not necessary.
Is it possible to create NO 3d array if the final result wanted is 2d, the sum of A along the 3rd dim? By for loop, the code would be
M = reshape(1:12,4,3);
V = 1:2;
n = length(V);
A = 0;
for ii = 1 : n
A = A + M * V(ii);
end
Try replacing the for.. loop with:
M_rep = repmat(M,1,1,n) %//repeat M in the 3rd dimension n times
v = reshape(1:n, 1, 1, n) %//create a vector [1 2 .. n] pointing in the third dimension
A = bsxfun(#times, M_rep, v) %//vector multiply them in the third dimension
In your above example, n=2.
EDIT (to your added question): To sum without allocating A:
B = sum(bsxfun(#times, M_rep, v),3);

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.

Resources