Moving rows from a matrix to a cell array using matlab: Trouble indexing cells - matlab

Using matlab, I am attempting to move rows from one matrix (RF) to a cell array using equivalent values in the matrix and the array. For each value i in the first column of matrix RF I would like to find which cell contains the same value. I would then like to move the row containing i (i,:) to the first row of zeros in that cell.
At the end I will delete all remaining rows of zeros in M.
I currently have the following code:
RF = [1 7; 4 8; 3 9; 7 10]
M = cell(3,1);
for k = 1:3
M{k} = zeros(10,2);
end
matsize2 = length((RF(:)));
halfmatsize2 = 0.5 * matsize2;
for i = 1:halfmatsize2 % Values in the first column of RF
J = RF(i);
JK = RF(i,:);
L = find(cell2mat(M)==J); % I wanted this line to give me the index of the cell containing J
H = find(M{L}==0, 1, 'first'); % This returns an error
M{L}(H,:)= JK; % Puts JK into the first row of zeros (my goal)
end
This outputs the error "index exceeds matrix dimensions" for the line H = find(M{L}==0, 1, 'first'); This is because while I wanted L to return the index of the cell containing J, it is giving me the location with all cells combined into one. What's the best way to find the cell containing J and then move JK to the first row of zeros in the aforementioned cell?
For example, I have the matrix RF = [1 7; 4 8; 3 9; 7 10] and the cell array M = cell(3,1), where M{1}=[1 4; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0], M{2} = [2 5; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0] and M{3} = [3 6; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0; 0 0].
I would like to end with M{1} = [1 4; 1 7; 4 8; 7 10], M{2} = [2 5] and M{3} = [3 6; 3 9].`
The overarching goal here is to automate the process for taking a matrix containing parent-offspring relationships for species and turn it into a phylogeny. The example provided is the simple one I've been using to test other aspects of the larger code.
Help would be much appreciated! After hours of Google searching and trial/error I remain stuck. I would also appreciate advice on how to streamline my rather clumsy code.
Thanks in advance!

Based on what I have understood, I think this would work for you and should be more efficient too -
%// Get matches between each element of col1 of RF against each in its col2
RF12 = bsxfun(#eq,RF(:,1),RF(:,2)'); %//'
%// Cut each cell of M into cells that have only non-zero rows
M_cut = arrayfun(#(n) M{n}(any(M{n},2),:),1:numel(M),'Uni',0)
M_out = cell(size(M)); %// storage for output
for n = 1:numel(M) %// we need to iterate through each cell
%// Matches for all elements in col1 of RF against all elements in M
M_RF1 = any(bsxfun(#eq,M{n}(:),RF(:,1)')); %//'
%// Matching row indices for all elements in col1 of RF against all
%// matches in its col2 and all elements in M
idx = any(bsxfun(#and,RF12,M_RF1),2) | M_RF1(:); %//'
%// Logically index into RF for the matches and vertically concatenate
%// with the M_cut cells to give us the desired output in each cell
M_out{n} = [M_cut{n} ; RF(idx,:)];
end
celldisp(M_out) %// display output
Output -
M_out{1} =
1 4
1 7
4 8
7 10
M_out{2} =
2 5
M_out{3} =
3 6
3 9

Related

Generating a Matrix from a given vector

Im trying to generate a specific type o matrix in Matlab.
I need to modify it for specific types of data with the following rules:
First I have to choose a grade g (max 6 let's say) then I have to choose the number of elements per row n (max 18) ;
These numbers are the powers of a specific polynomial of grade g ;
The sum per row in the matrix is not allowed to be bigger than the chosen g grade ;
The biggest element per row is the chosen g.
For g = 2, n = 2 the matrix will look like this:
A = [0 0;
0 1;
1 0;
0 2;
2 0;
1 1]
For g = 2, n = 3 the matrix will look like this:
A = [0 0 0;
0 0 1;
0 0 2;
0 1 0;
0 2 0;
1 0 0;
2 0 0;
0 1 1;
1 0 1;
1 1 0]
How can I generate all possible combinations of an array elements?
Ex : given v = [0 1 2];
Result = [0 0 0;
0 0 1;
0 1 0;
0 1 1;
1 0 0;
1 0 1;
1 1 0;
1 1 1;
0 0 2;
0 2 0;
2 0 0;
2 0 1;
2 1 1;
2 1 2;
...]
and so on...
I've tried this with perms, nchoosek, repelem, repmat, for-loops, unique, matrix concatenations, everything but I couldn't be able to find and algorithm.
You can first generate all n permutations of [0..g] with repetition and rearrangement, then select allowed combinations:
% all n permutations of [0..g] (with repetition and rearrangement)
powers = unique(nchoosek(repmat(0:g, 1, n), n), 'row');
% allowed set of powers
powers = powers(sum(powers, 2) <= g, :);
As I already said in comments, the above code is extremely time and memory inefficient. For example when I run it for g=6 and n=9, MATLAB gives the following error:
Error using zeros Requested 23667689815x9 (1587.0GB) array exceeds
maximum array size preference.
...
To reduce memory consumption, you can do the following:
% all n permutations of [0..g] (with repetition)
gPerms = nmultichoosek(0:g, n);
% allowed set of powers
allowed = gPerms(sum(gPerms, 2) <= g, :);
% all permutations of [1..n] (no repetition)
nPerms = perms(1:n);
% all n permutations of [0..g] (with repetition and rearrangement)
arranges = arrayfun(#(i) allowed(:, nPerms(i, :)), ...
1:size(nPerms, 1), 'UniformOutput', false)';
powers = cell2mat(arranges);
% unique set of permutations
powers = unique(powers, 'rows');
In the above code, first n permutations with repetition of g is generated using #knedlsepp's implementation. The filtered to keep only combinations that their sums is less than or equal to g. In next step all rearrangements of these combinations are calculated. It still takes more than 13 seconds here to find 5005 combinations for the g=6 and n=9 case.

Extract unique elements from each row of a matrix (Matlab)

I would like to apply the function unique to each row of a given matrix, without involving any for loop. Suppose I have the following 4-by-5 matrix
full(A) = [0 1 0 0 1
2 1 0 3 0
1 2 0 0 2
0 3 1 0 0]
where A is the corresponding sparse matrix. As an example using a for loop, I can do
uniq = cell(4,1);
for i = 1:4
uniq{i} = unique(A(i,:));
end
and I would obtain the cell structure uniq given by
uniq{1} = {1}
uniq{2} = {[1 2 3]}
uniq{3} = {[1 2]}
uniq{4} = {[1 3]}
Is there a quicker way to vectorize this and avoid for loops?
I need to apply this to matrices M-by-5 with M large.
Note that I'm not interested in the number of unique elements per row (I know there are around answers for such a problem).
You can use accumarray with a custom function:
A = sparse([0 1 0 0 1; 2 1 0 3 0; 1 2 0 0 2; 0 3 1 0 0]); % data
[ii, ~, vv] = find(A);
uniq = accumarray(ii(:), vv(:), [], #(x){unique(x.')});
This gives:
>> celldisp(uniq)
uniq{1} =
1
uniq{2} =
1 2 3
uniq{3} =
1 2
uniq{4} =
1 3
you can use num2cell(A,2) to convert each row into cell and then cellfun with unique to get cell array of unique values from each row:
% generate large nX5 matrix
n = 5000;
A = randi(5,n,5);
% convert each row into cell
C = num2cell(A,2);
% take unique values from each cell
U = cellfun(#unique,C,'UniformOutput',0);

Randomly permute matrix rows keeping same-valued rows adjacent

How can I randomly permute row keeping those with the same value adjacent to each other? I know that I can use randperm on the rows to randomly permute all rows, but I do not know how to keep the same-valued rows adjacent. Thanks.
A = [0 0 0;
0 0 0;
1 1 1;
1 1 1;
1 1 1;
2 2 2;
2 2 2];
permute_A = [0 0 0;
0 0 0;
2 2 2;
2 2 2;
1 1 1;
1 1 1;
1 1 1];
You have to identify the clusters and then permute them:
Finding the clusters can be done by finding the changes in their differences:
diffA = diff(A);
clusters_start = [1 ; find(any(diffA,2)~=0)+1];
Then the cluster ends are easily found by:
clusters_end = [clusters_start(2:end)-1 ;size(A,1)];
clusters_length = clusters_end-clusters_start+1;
now you know the number of clusters and you can permute them:
Nclusters = numel(clusters_start);
perm_idx = randperm(Nclusters );
clusters_start = clusters_start(perm_idx);
clusters_end = clusters_end(perm_idx);
clusters_length = clusters_length(perm_idx);
and put them in a new matrix:
newA = NaN(size(A));
for ii=1:Nclusters
newA(sum(clusters_length(1:ii-1))+(1:clusters_length(ii)),:) = A(clusters_start(ii):clusters_end(ii),:);
end

Randomly replace percentage of elements in matrix per existing values

is there a sensible way to replace x% of each value in matrix/vector with a new value, and have the the element(s) to be changed be selected randomly? That is, in A, if I wanted to change 20% of the values (1 element per existing value) to the value 5, how do I make sure that each of the 5 elements per existing value in A has an equal probability of changing to the new value (e.g. 5)? I would appreciate some guidance on a method to complete the task described above.
Thank you kindly.
% Example Matrix
% M = 5;
% N = 5;
% A = zeros(M, N);
A = [0 0 0 0 0;
1 1 1 1 1;
2 2 2 2 2;
3 3 3 3 3;
4 4 4 4 4];
% Example Matrix with 20% of elements per value replaced with the value '5'
A = [0 0 5 0 0;
1 5 1 1 1;
2 5 2 2 2;
3 3 3 3 5;
4 4 5 4 4];
Try using logical arrays and a random number generated, like this:
vals_to_change=rand(size(A,1),size(A,2))<p;
A(vals_to_change)=rand(sum(vals_to_change),1);
Using information from here and here I was able to achieve my objective. The code below will replace x% of each value in matrix with a new value and then randomize its location within that value in the matrix.
M = 5;
N = 5;
A = zeros(M, N);
PC = 20; % percent to change
nCells = round(100/PC); % # of cells to replace with new value
A = [0 0 0 0 0;
1 1 1 1 1;
2 2 2 2 2;
3 3 3 3 3;
4 4 4 4 4];
A2 = A+1; % Pad the cell values for calculations (bc of zero)
newvalue = 6;
a=hist(A2(:),5);% determine qty of each value
for i=1:5
% find 1st instance of each value and convert to newvalue
A2(find(A2==i,round(a(i)/nCells)))=newvalue;
end;
out = A2-1; % remove padding
[~,idx] = sort(rand(M,N),2); % convert column indices into linear indices
idx = (idx-1)*M + ndgrid(1:M,1:N); %rearrange each newvalue to be random
A = out;
A(:) = A(idx);

Matlab, Integer vector to binary matrix without loop [duplicate]

This question already has answers here:
Creating Indicator Matrix
(6 answers)
Closed 7 years ago.
I have a vector with N elements, all integers 1-M. I want to convert this to a NxM matrix with each row containing only zeros except for the i:th element set to one, i being the integer in the vector.
For example:
[1 1 3] => [1 0 0; 1 0 0; 0 0 1]
I currently do this in a loop, like this:
y_vec = zeros(m, num_labels);
for i = 1:m
y_vec(i, y(i)) = 1;
end
Is there a way to do this without a loop?
Yes, there is:
y = [1 1 3];
m = length(y);
num_labels = max(y);
%# initialize y_vec
y_vec = zeros(m,num_labels);
%# create a linear index from {row,y}
idx = sub2ind(size(y_vec),1:m,y);
%# set the proper elements of y_vec to 1
y_vec(idx) = 1;
If you have access to Statistics Toolbox, the command dummyvar does exactly this.
>> dummyvar([1 1 3])
ans =
1 0 0
1 0 0
0 0 1
(This has been asked in Creating Indicator Matrix and Matlab/Octave 1-of-K representation.)
My favorite answer is woodchips' sparse(1:n,labels,1,n,m);.