How to recover the original locations of deleted elements? - matlab

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]) = [];

Related

Indexing rows of a table by comparing the values between two cells

I have a table like the above attachment. Column A and Column B contains some elements in terms of cell array. I want to create the third column (Level) as the resultant column; based on the following logic.
The row for which, value of cell A = value of cell B will be labeled1. (In the 3rd row, the value of column A=value of column B= 3, hence labeled 1).
Next, the preceding value will be removed from all
the cells of column A; and the step 1 will be repeated until all the
rows are labeled. (In the second step, 3 will be removed from all
the cells, hence both row 1 and row 2 will be labeled as 2;In the
final step, elements {1,2} will be further removed from the last row
resulting the level as 3 )
I am using cell2mat and setdiff functions to compare the values across the cells, but I am not able to frame the above 2 logical steps to run my code successfully. I have just started learning MATLAB, Any help will be highly appreciated.
Here's the simplest answer I could come up with, using a single while loop and assuming the cells of A and B contain row vectors:
Level = zeros(size(A));
index = cellfun(#isequal, A, B);
while any(index)
Level(index) = max(Level)+1;
A = cellfun(#(c) {setdiff(c, unique([A{index}]))}, A);
index = cellfun(#isequal, A, B);
end
The above code first initializes a matrix of zeroes Level the same size as A to store the level values. Then it finds a logical index index of where there are matching cell contents between A and B using cellfun and isequal. It will continue to loop as long as there are any matches indicated by index. The corresponding indices in Level are set to the current maximum value in Level plus one. All the matching cell contents from A are concatenated and the unique values found by unique([A{index}]). A set difference operation is then used (along with cellfun) to remove the matching values from each cell in A, overwriting A with the remaining values. A new index for matches is then computed and the loop restarts.
Given the following sample data from your question:
A = {[1 2 3]; [2 3]; 3; [1 2 3 4]};
B = {[1 2]; 2; 3; 4};
The code returns the expected level vector:
Level =
2
2
1
3
Not my best work, i think it is possible to get rid of the inner loop.
% your testdata
A = {[1 2 3]
[2 3]
3
[1,2,4]};
B = {[1 2]
2
3
4};
Level = NaN(numel(B),1);
temp = A; % copy of A that we are going to remove elements from
k = 0; % loop couter
while any(isnan(Level)) % do until each element of Level is not NaN
k = k+1; % increment counter by 1
% step 1
idx = find(cellfun(#isequal,temp,B)); % determine which cells are equal
Level(idx) = k; % set level of equal cells
% step 2
for k = 1:numel(idx) % for each cell that is equal
%remove values in B from A for each equal cell
temp = cellfun(#setdiff,temp,repmat(B(idx(k)),numel(B),1),'UniformOutput',0);
end
end

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).

Get even/odd indices of a matrix - 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.

Filtering a matrix by unique column elements

M is a matrix: M = [X Y Z] where X, Y and Z are column vectors.
What is the easiest way to filter M so that:
1- No element is repeated per column
2- The order of the rows remain (if an element appears twice in a column, then I want to delete the entire row where it appears the second time)
e.g:
M = [1 2 4;
1 3 5;
2 3 9]
would become
Mf = [1 2 4;
2 3 9]
I tried to use [u,~,ind] = unique(M,'rows') to have the elements for which one element in a column is repeated, but this function deals with the entire row (if only one element of the row is repeated, then the row is unique)
Here is a quick and dirty solution, should be fine as long as you M isn't too big. I've tested it on a few matrices and it seems to work as intended.
count=1;
for i=1:length(M(1,:))^2
[~,IA,~]=unique(M(:,count),'first');
if length(IA)~=length(M(:,1))
M=M(IA,:);
count=count-1;
end
count=count+1;
if count>length(M(:,1))
break
end
end
M

Sum every n rows of matrix

Is there any way that I can sum up columns values for each group of three rows in a matrix?
I can sum three rows up in a manual way.
For example
% matrix is the one I wanna store the new data.
% data is the original dataset.
matrix(1,1:end) = sum(data(1:3, 1:end))
matrix(2,1:end) = sum(data(4:6, 1:end))
...
But if the dataset is huge, this wouldn't work.
Is there any way to do this automatically without loops?
Here are four other ways:
The obligatory for-loop:
% for-loop over each three rows
matrix = zeros(size(data,1)/3, size(data,2));
counter = 1;
for i=1:3:size(data,1)
matrix(counter,:) = sum(data(i:i+3-1,:));
counter = counter + 1;
end
Using mat2cell for tiling:
% divide each three rows into a cell
matrix = mat2cell(data, ones(1,size(data,1)/3)*3);
% compute the sum of rows in each cell
matrix = cell2mat(cellfun(#sum, matrix, 'UniformOutput',false));
Using third dimension (based on this):
% put each three row into a separate 3rd dimension slice
matrix = permute(reshape(data', [], 3, size(data,1)/3), [2 1 3]);
% sum rows, and put back together
matrix = permute(sum(matrix), [3 2 1]);
Using accumarray:
% build array of group indices [1,1,1,2,2,2,3,3,3,...]
idx = floor(((1:size(data,1))' - 1)/3) + 1;
% use it to accumulate rows (appliead to each column separately)
matrix = cell2mat(arrayfun(#(i)accumarray(idx,data(:,i)), 1:size(data,2), ...
'UniformOutput',false));
Of course all the solution so far assume that the number of rows is evenly divisble by 3.
This one-liner reshapes so that all the values needed for a particular cell are in a column, does the sum, and then reshapes the back to the expected shape.
reshape(sum(reshape(data, 3, [])), [], size(data, 2))
The naked 3 could be changed if you want to sum a different number of rows together. It's on you to make sure the number of rows in each group divides evenly.
Slice the matrix into three pieces and add them together:
matrix = data(1:3:end, :) + data(2:3:end, :) + data(3:3:end, :);
This will give an error if size(data,1) is not a multiple of three, since the three pieces wouldn't be the same size. If appropriate to your data, you might work around that by truncating data, or appending some zeros to the end.
You could also do something fancy with reshape and 3D arrays. But I would prefer the above (unless you need to replace 3 with a variable...)
Prashant answered nicely before but I would have a simple amendment:
fl = filterLength;
A = yourVector (where mod(A,fl)==0)
sum(reshape(A,fl,[]),1).'/fl;
There is the ",1" that makes the line run even when fl==1 (original values).
I discovered this while running it in a for loop like so:
... read A ...
% Plot data
hold on;
averageFactors = [1 3 10 30 100 300 1000];
colors = hsv(length(averageFactors));
clear legendTxt;
for i=1:length(averageFactors)
% ------ FILTERING ----------
clear Atrunc;
clear ttrunc;
clear B;
fl = averageFactors(i); % filter length
Atrunc = A(1:L-mod(L,fl),:);
ttrunc = t(1:L-mod(L,fl),:);
B = sum(reshape(Atrunc,fl,[]),1).'/fl;
tB = sum(reshape(ttrunc,fl,[]),1).'/fl;
length(B)
plot(tB,B,'color',colors(i,:) )
%kbhit ()
endfor