Get even/odd indices of a matrix - MATLAB - matlab

I have following problem:
I have a given matrix of let's say 4x4.
How can I get the indices of the following combinations:
row odd and column odd
row odd and column even
row even and column odd
row even and column even
For example if I have the matrix:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
'row odd and column odd' would be the indices of 1, 3, 9, 11...
'row odd and column even' would be the indices of 2, 4, 10, 12...
'row even and column odd' would be the indices of 5, 7, 13, 15...
and 'row even and column even' would be indices of 6, 8, 14, 16...
Also, is it possible to combine those operations so e.g. I get the indices for 'row odd and column odd' and 'row even and column even'?
Thank you!

It's pretty easy to do with indexing:
Odd rows and odd columns
B = A(1:2:end, 1:2:end);
Odd rows and even columns
B = A(1:2:end, 2:2:end);
Even rows and odd columns
B = A(2:2:end, 1:2:end);
Even rows and even columns
B = A(2:2:end, 2:2:end);
The above assumes that you want the actual matrix values themselves. It's a bit confusing as your matrix elements are the same as linear indexing values themselves. If you want to determine the actual column major indices to access the matrix, you can generate a vector from 1 to N where N is the total number of elements in your matrix, then reshape this matrix into the desired size that you want. After, use the same logic above to get the actual linear indices:
N = numel(A);
B = reshape(1:N, size(A,1), size(A,2));
ind = B(1:2:end, 1:2:end); %// For odd rows, odd columns
%// Repeat for the other ones...
Now, given your comment, you want to create a new matrix that will store only these extracted matrix values while making all of the other elements zero. If you want to do this, simply pre-allocate a matrix of zeroes, then copy over those values to extract using the computed indices into the new matrix. In other words:
N = numel(A);
B = reshape(1:N, size(A,1), size(A,2));
ind = B(1:2:end, 1:2:end); %// For odd rows, odd columns - Change to suit your tastes
out = zeros(size(A));
out(ind(:)) = A(ind(:));
If you want to combine the indices like having odd row - odd column, and even row - even column, just compute two sets of indices, concatenate them into a single vector and do the same syntax like before. Therefore:
N = numel(A);
B = reshape(1:N, size(A,1), size(A,2));
ind = B(1:2:end, 1:2:end); %// For odd rows, odd columns
ind2 = B(2:2:end, 2:2:end); %// For even rows, even columns
ind = [ind(:); ind2(:)];
out = zeros(size(A));
out(ind) = A(ind);

Code
N = size(A,1); %// Get size of input matrix A
case1_ind = bsxfun(#plus,[1:2:N]',(0:N/2-1)*2*N)
case2_ind = case1_ind + N
case3_ind = case1_ind + 1
case4_ind = case3_ind + N
Note: These outputs are indices. So, to get the actual outputs, use these as indices.
To combine indices for case 1 and case 4, just concatenate -
case14comb_ind = [case1_ind ; case4_ind]
Edit :
%// To copy onto some other matrix of the same size as A, do this for case 1
new_matrix = zeros(size(A))
new_matrix(case1_ind(:)) = A(case1_ind(:))
Repeat this for the other cases too.

Related

Index a vector by a matrix of conditions to obtain multiple selections of the target?

I have a vector T of length n and m other vectors of the same length with 0 or 1 used as condition to select elements of T. The condition vectors are combined into a matrix I of size n x m.
Is there a one liner to extract a matrix M of values from Tsuch that the i-th column of M are those elements in T that are selected by the condition elements of the i-th column in I?
Example:
T = (1:10)'
I = mod(T,2) == 0
T(I)'
yields
2 4 6 8 10
However
I = mod(T,2:4) == 0
T(I)'
yields an error in the last statement. I see that the columns might select a different number of elements which results in vectors of different lengths (as in the example). However, even this example doesn't work:
I = zeros(10,2)
I(:,1) = mod(T,2)==0
I(:,2) = mod(T,2)==1
Is there any way to achieve the solution in a one liner?
The easiest way I can think of to do something like this is to take advantage of the element-wise multiplication operator .* with your matrix I. Take this as an example:
% these lines are just setup of your problem
m = 10;
n = 10;
T = [1:m]';
I = randi([0 1], m, n);
% 1 liner to create M
M = repmat(T, 1, n) .* I;
What this does is expand T to be the same size as I using repmat and then multiplies all the elements together using .*.
Here is a one linear solution
mat2cell(T(nonzeros(bsxfun(#times,I,(1:numel(T)).'))),sum(I))
First logical index should be converted to numeric index for it we multiply T by each column of I
idx = bsxfun(#times,I,(1:numel(T)).');
But that index contain zeros we should extract those values that correspond to 1s in matrix I:
idx = nonzeros(idx);
Then we extract repeated elements of T :
T2 = T(idx);
so we need to split T2 to 3 parts size of each part is equal to sum of elements of corresponding column of I and mat2cell is very helpful
result = mat2cell(T2,sum(I));
result
ans =
{
[1,1] =
2
4
6
8
10
[2,1] =
3
6
9
[3,1] =
4
8
}
One line solution using cellfun and mat2cell
nColumns = size(I,2); nRows = size(T,1); % Take the liberty of a line to write cleaner code
cellfun(#(i)T(i),mat2cell(I,nRows,ones(nColumns,1)),'uni',0)
What is going on:
#(i)T(i) % defines a function handle that takes a logical index and returns elements from T for those indexes
mat2cell(I,nRows,ones(nColumns,1)) % Split I such that every column is a cell
'uni',0 % Tell cellfun that the function returns non uniform output

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

Shifting repeating rows to a new column in a matrix

I am working with a n x 1 matrix, A, that has repeating values inside it:
A = [0;1;2;3;4; 0;1;2;3;4; 0;1;2;3;4; 0;1;2;3;4]
which correspond to an n x 1 matrix of B values:
B = [2;4;6;8;10; 3;5;7;9;11; 4;6;8;10;12; 5;7;9;11;13]
I am attempting to produce a generalised code to place each repetition into a separate column and store it into Aa and Bb, e.g.:
Aa = [0 0 0 0 Bb = [2 3 4 5
1 1 1 1 4 5 6 7
2 2 2 2 6 7 8 9
3 3 3 3 8 9 10 11
4 4 4 4] 10 11 12 13]
Essentially, each repetition from A and B needs to be copied into the next column and then deleted from the first column
So far I have managed to identify how many repetitions there are and copy the entire column over to the next column and then the next for the amount of repetitions there are but my method doesn't shift the matrix rows to columns as such.
clc;clf;close all
A = [0;1;2;3;4;0;1;2;3;4;0;1;2;3;4;0;1;2;3;4];
B = [2;4;6;8;10;3;5;7;9;11;4;6;8;10;12;5;7;9;11;13];
desiredCol = 1; %next column to go to
destinationCol = 0; %column to start on
n = length(A);
for i = 2:1:n-1
if A == 0;
A = [ A(:, 1:destinationCol)...
A(:, desiredCol+1:destinationCol)...
A(:, desiredCol)...
A(:, destinationCol+1:end) ];
end
end
A = [...] retrieved from Move a set of N-rows to another column in MATLAB
Any hints would be much appreciated. If you need further explanation, let me know!
Thanks!
Given our discussion in the comments, all you need is to use reshape which converts a matrix of known dimensions into an output matrix with specified dimensions provided that the number of elements match. You wish to transform a vector which has a set amount of repeating patterns into a matrix where each column has one of these repeating instances. reshape creates a matrix in column-major order where values are sampled column-wise and the matrix is populated this way. This is perfect for your situation.
Assuming that you already know how many "repeats" you're expecting, we call this An, you simply need to reshape your vector so that it has T = n / An rows where n is the length of the vector. Something like this will work.
n = numel(A); T = n / An;
Aa = reshape(A, T, []);
Bb = reshape(B, T, []);
The third parameter has empty braces and this tells MATLAB to infer how many columns there will be given that there are T rows. Technically, this would simply be An columns but it's nice to show you how flexible MATLAB can be.
If you say you already know the repeated subvector, and the number of times it repeats then it is relatively straight forward:
First make your new A matrix with the repmat function.
Then remap your B vector to the same size as you new A matrix
% Given that you already have the repeated subvector Asub, and the number
% of times it repeats; An:
Asub = [0;1;2;3;4];
An = 4;
lengthAsub = length(Asub);
Anew = repmat(Asub, [1,An]);
% If you can assume that the number of elements in B is equal to the number
% of elements in A:
numberColumns = size(Anew, 2);
newB = zeros(size(Anew));
for i = 1:numberColumns
indexStart = (i-1) * lengthAsub + 1;
indexEnd = indexStart + An;
newB(:,i) = B(indexStart:indexEnd);
end
If you don't know what is in your original A vector, but you do know it is repetitive, if you assume that the pattern has no repeats you can use the find function to find when the first element is repeated:
lengthAsub = find(A(2:end) == A(1), 1);
Asub = A(1:lengthAsub);
An = length(A) / lengthAsub
Hopefully this fits in with your data: the only reason it would not is if your subvector within A is a pattern which does not have unique numbers, such as:
A = [0;1;2;3;2;1;0; 0;1;2;3;2;1;0; 0;1;2;3;2;1;0; 0;1;2;3;2;1;0;]
It is worth noting that from the above intuitively you would have lengthAsub = find(A(2:end) == A(1), 1) - 1;, But this is not necessary because you are already effectively taking the one off by only looking in the matrix A(2:end).

Using elements of a vector to set elements of a matrix

I have a vector whose elements identify the indices (per column) that I need to set in a different matrix. Specifically, I have:
A = 7
1
2
and I need to create a matrix B with some number of rows of zeros, except for the elements identified by A. In other words, I want B:
B = zeros(10, 3); % number of rows is known; num columns = size(A)
B(A(1), 1) = 1
B(A(2), 2) = 1
B(A(3), 3) = 1
I would like to do this without having to write a loop.
Any pointers would be appreciated.
Thanks.
Use linear indexing:
B = zeros(10, 3);
B(A(:).'+ (0:numel(A)-1)*size(B,1)) = 1;
The second line can be written equivalently with sub2ind (may be a little slower):
B(sub2ind(size(B), A(:).', 1:numel(A))) = 1;

How to recover the original locations of deleted elements?

I have a data matrix A, the size of which is 4*20 (4 rows, 20 columns). The matrix A is generated by A = randn (4, 20).
In the first iteration, I delete columns [2,3] of matrix A. Then matrix A becomes matrix A1, the size of which is 4*18.
In the second iteration, I delete columns [4 8 10] of matrix A1. Then matrix A1 becomes matrix A2, the size of which is 4*15.
In the third iteration, I delete columns [1 3 6 9 10] of matrix A2. Then matrix A2 becomes matrix A3, the size of which is 4*10.
The deleted elements are put into a matrix B. My question is how to figure out the x and y coordinates in original matrix A of every deleted element in B. Anyone can give me a help? Thank you so much!
I would personally keep another vector that ranges between 1 to 20 and put your removal of columns within a loop. Let's call this vector column_choice. At each iteration, use randperm to randomly select from column_choice those columns you want to remove, then append these to your matrix B. Once you select these columns, remove these elements from column_choice and continue with your code. Also, those columns from column_choice we will add to another vector... call it, final_columns. This vector will tell you which vectors you ultimately removed in the end, and you can reference these columns in the original matrix.
To make things efficient, create an array where each element contains the total number of columns you want to remove at each iteration. Therefore, do something like:
cols_to_remove = [2 3 5];
The first element means you want to remove 2 columns in the first iteration, the second element means you want to remove 3 columns in the second iteration, and 5 columns in the last iteration. Because you're looping, it's a good idea to pre-allocate your matrix. In total, you're going to have 10 columns removed and populated in B, and since your random matrix has 4 rows, you should do this:
B = zeros(4,sum(cols_to_remove));
We are summing over cols_to_remove as this tells us how many columns we will ultimately be removing all together. One thing I'd like to mention is that you should make a copy of A before we start removing columns. That way, you're able to reference back into the original matrix.
Finally, without further ado, here's the code that I would write to tackle this problem:
column_choice = 1 : 20;
cols_to_remove = [2 3 5];
B = zeros(4,sum(cols_to_remove));
final_columns = zeros(1,sum(cols_to_remove));
A = randn(4,20); %// From your post
Acopy = A; %// Make a copy of the matrix
%// Keep track of which column we need to start populating
%// B at
counter = 1;
%// For each amount of columns we want to remove...
for cols = cols_to_remove
%// Randomly choose the columns we want to remove
to_remove = randperm(numel(column_choice), cols);
%// Remove from the A matrix and store into B
removed_cols = Acopy(:,to_remove);
Acopy(:,to_remove) = [];
B(:,counter : counter + cols - 1) = removed_cols;
%// Also add columns removed to final_columns
final_columns(counter : counter + cols - 1) = column_choice(to_remove);
%// Increment counter for the next spot to place columns
counter = counter + cols;
%// Also remove from column_choice
column_choice(to_remove) = [];
%// Continue your code here to process A and/or B
%//...
%//...
end
%// Remove copy to save memory
clear Acopy;
Therefore, final_columns will give you which columns were removed from the original matrix, and you can refer back to A to locate where these are. B will contain those removed columns from A and are all concatenated together.
Edit
As per your comments, you want to remove certain rows from each intermediate result. As such, you would specify which columns you want to remove in the second dimension of each matrix, then set it equal to []. Make sure you copy over each result into a new matrix before removing the columns. Also, you'll need to keep track of which indices from the original matrix you removed, so make that column_choice and final_columns vector again and repeat the saving logic that we have talked about before.
Therefore:
column_choice = 1:20;
final_columns = zeros(1,10);
A1 = A;
A1(:,[2 3]) = [];
final_columns(1:2) = column_choice([2 3]);
column_choice([2 3]) = [];
A2 = A1;
A2(:,[4 9 11]) = [];
final_columns(3:5) = column_choice([4 9 11]);
column_choice([4 9 11]) = [];
A3 = A2;
A3(:,[1 2 5 8 12]);
final_columns(6:10) = column_choice([1 2 5 8 12]);
column_choice([1 2 5 8 12]) = [];