I have in Matlab a cell named: elem
[36 29]
[]
[30 29]
[30 18]
[]
[31 29]
[]
[]
[8 9]
[32 30]
and a matrix named: conn
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
and i want to make 2 new matrices which will contain only the elements that correspond to non-empty cells of elem without using a for loop.
For example the correct result would be:
29 36
29 30
18 30
29 31
8 9
30 32
and:
1 2
1 4
1 5
2 4
3 5
4 5
Any help would be greatly appreciated.
inds = ~cellfun('isempty', elem); %// NOTE: faster than anonymous function
conn = conn(inds,:);
elem = elem(inds); %// (preservative)
or
inds = cellfun('isempty', elem); %// NOTE: faster than anonymous function
conn(inds,:) = [];
elem(inds ) = []; %// (destructive)
or
inds = cellfun(#(x)isequal(x,[]), elem) %// NOTE: stricter; evaluates to false
conn = conn(inds,:); %// when the 'empties' are '' or {}
elem = elem(inds); %// or struct([])
or
inds = cellfun(#(x)isequal(x,[]), elem) %// "
conn(inds,:) = [];
elem(inds ) = [];
or
inds = cellfun(#numel, elem)==2 %// NOTE: even stricter; only evaluates to
conn = conn(inds,:); %// true when there are exactly 2 elements
elem = elem(inds); %// in the entry
or
inds = cellfun(#numel, elem)==2 %// "
conn(inds,:) = [];
elem(inds ) = [];
or (if you're just interested in elem)
elem = cell2mat(elem)
or
elem = cat(1,elem{:}) %// NOTE: probably the fastest of them all
Your first output can be obtained by:
cellfun(#fliplr, elem(~cellfun(#isempty, elem)), 'UniformOutput', 0);
Note that I included #fliplr, assuming that the element order reversal in your question was intentional
Your second output can be obtained by:
conn(~cellfun(#isempty, elem), :);
conn = [1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5];
elem = { [36 29]
[]
[30 29]
[30 18]
[]
[31 29]
[]
[8 9]
[32 30]};
conn(~cellfun(#isempty, elem), :)
you can use the #isempty function for cellfun() together with logical operators like this:
EmptyCells=cellfun(#isempty,elem); %Detect empty cells
elem(EmptyCells)=[]; %Remove empty cells
conn=conn(~EmptyCells); %Remove elements corresponding the empty cells in elem
Hope that works!
Related
I have a problem I am trying to solve that creates an Nx1 cell where the data stored inside it are always N number of 2x2 matrices.
Example:
N = 2
mycell = cell(N,1);
for i =1:N;
mycell{i} = randi([0, 10], 2);
end
newmatrix = zeros (N+1);
So say mycell{1} looks like:
[3 5
2 1]
and mycell{2} looks like:
[6 9;
3 2]
My new matrix of zeros looks like:
[0 0 0
0 0 0
0 0 0]
I want to get it too look like this (joining the last element of the first cell with the first element of the next cell in this sort of diagonal setup):
[3 5 0
2 7 9
0 3 2]
Is there a simple way to do this or any built in Matlab functions that may help?
Thank you.
Here's a solution based on accumarray. It doesn't use loops, and it works for generic sizes N (number of matrices), R (number of rows of each matrix) and C (number of columns of each matrix):
Generate example data (using a generalization of the code in the question):
N = 3; % number of matrices
R = 2; % number of rows of each matrix
C = 3; % number of columns of each matrix
mycell = cell(N,1);
for i =1:N;
mycell{i} = randi([0, 10], [R C]);
end
The following steps are used:
Build row and column indices with the appropriate staggering;
Concatenate the cell array and linearize, so that all data is in a column vector;
Apply accumarray to build the result matrix, summing values with the same indices.
Code:
indCol = repmat((0:N-1)*(R-1)+(1:R).', C, 1);
indRow = repelem((0:N-1)*(C-1)+(1:C).', R, 1);
newmatrix = accumarray([indCol(:) indRow(:)], reshape(cat(3, mycell{:}), 1, []));
Example result:
>> celldisp(mycell)
mycell{1} =
3 1 2
5 6 7
mycell{2} =
7 4 2
8 0 10
mycell{3} =
1 5 0
9 10 4
>> newmatrix
newmatrix =
3 1 2 0 0 0 0
5 6 14 4 2 0 0
0 0 8 0 11 5 0
0 0 0 0 9 10 4
We can do the following:
N = 2
mycell = cell(N,1);
newmatrix = zeros (N+1);
for i =1:N;
mycell{i} = randi([0, 10], 2);
newmatrix(i:i+1, i:i+1) = newmatrix(i:i+1, i:i+1) + mycell{i}
end
newmatrix
mycell
This produces
newmatrix =
6 4 0
8 10 9
0 3 4
mycell =
{
[1,1] =
6 4
8 0
[2,1] =
10 9
3 4
}
I have two different matrices A and B:
A =
[7 8 9;
4 5 6]
B =
[22 32 12;
9 8 10]
sortB =
[12 22 32;
8 9 10]
sortindex_B=[3 1 2;2 1 3];, i.e., 12 is in the third position of matrix B, 22 in first, and 32 in second position; similarly for the second row.
Now I want to sort A depending on Sortindex_B (i.e., in matrix A I want 7 as the third element, 8 as the first, and 9 as the second element of the first row; and similarly for the second row: 4 at the second, 5 at the first, and 6 as the third element). Hence the result should look like:
A_final =
[8 9 7;
5 4 6]
How can I achieve this?
You can ask for the sorting index matrix from sort command, when sorting B, and thereafter operate on A as a cell, in so making cellfun available:
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
[sortB, I] = sort(B,2);
Icell = mat2cell(I,ones(1, size(I,1)),size(I,2));
Acell = mat2cell(A,ones(1, size(I,1)),size(I,2));
sortA = cell2mat(...
cellfun(#(x,y) y(x), Icell, Acell, 'UniformOutput', false))
Output (you state first row output as 8 9 7, but did you really mean 9 7 8?)
sortA =
9 7 8
5 4 6
For sorting as specifically specified in your question; re-map index matrix I:
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
[sortB, I] = sort(B,2);
%// re-map I
for i = 1:size(I,1)
Itmp = I(i,:);
for j = 1:size(I,2)
I(i,Itmp(j)) = j;
end
end
Icell = mat2cell(I,ones(1, size(I,1)),size(I,2));
ImapCell = mat2cell(Imap,ones(1, size(I,1)),size(I,2));
Acell = mat2cell(A,ones(1, size(I,1)),size(I,2));
sortA = cell2mat(...
cellfun(#(x,y) y(x), Icell, Acell, 'UniformOutput', false))
Output
sortA =
8 9 7
5 4 6
Use a combination of sort as in dfri's answer and sub2ind:
A = [7 8 9;
4 5 6];
B = [22 32 12;
9 8 10];
[sortB, sortindex_B] = sort(B,2);
[~, colIdx] = sort(sortindex_B,2);
rowIdx = ndgrid(1:size(B,1),1:size(B,2));
idx = sub2ind(size(B),rowIdx,colIdx);
sortA = A(idx)
ans =
8 9 7
5 4 6
you will have to do this row by row using the index values that the sort function returns.
Something like this should do the trick and is expandable to any number of rows that your matrix A and B may have. This does also validate that A and B are the same size before it continues.
B= [22 32 12; 9 8 10]
A = [7 8 9; 4 5 6];
assert(all(size(A) == size(B)));
sortB = zeros(size(B));
finalA= zeros(size(A));
for i = 1:size(B,1)
[sorted,idx] = sort(B(i,:));
sortB(i,:) = sorted;
tempA = A(i,:);
tempA = tempA(idx);
finalA(i,:) = tempA;
end
There are many clever ways to do this, including this for loop. I hope the comments will explain the logic.
clear; %// input the sample data
A = [7 8 9; 4 5 6];
B = [22 32 12; 9 8 10];
sortB = [12 22 32; 8 9 10];
%// loop through every element in B
[R C]=size(B);
for i=1:C
for j=1:R
%// Where does A(j,i) need to go in Afinal?
%// It needs to go in the j-th row, and in
%// whatever column of B(j,:) equals sortB(j,i).
Afinal( j , find( B(j,:) ==sortB(j,i)) ) = A(j,i);
end
end
And the result:
>> Afinal
Afinal =
8 9 7
5 4 6
So, I have this cell array contains n x 2 matrix in each cell. Here is the sample data :
[16 17;17 17]
<6x2 double>
<52x2 double>
[17 17;17 18]
[17 18;17 17]
What I am going to do is eliminate the duplicated matrix (matrices with same values or reversed values). in this case is [17 18; 17 17] (5th row), because we already have [17 17; 17 18] (4th row)
I tried using unique function but it says that the function just worked for strings. I also tried to find it with cellfun like this
cellfun(#(x) x==whatToSearch, lineTraced, 'UniformOutput', false)
but it says 'Matrix dimension must agree'
Thanks in advance.
Here is a solution. Given a m x 1 column cell array C of matrices, this code deletes the equivalent duplicates. (Here it will delete the 4th and 5th matrices, which are equivalent to the 1st).
C{1} = magic(3);
C{2} = magic(4);
C{3} = magic(5);
C{4} = C{1};
C{5} = flipud(C{1});
myEq = #(A,B) isequal(A,B) | isequal(A,flipud(B)); %// equivalence operator tests for same or up-down flipped matrix
C = C(:); %// ensure the cell array is a column
Crep = repmat(C,1,size(C,1)); %// repeat cell array along rows to get a square
comp = cellfun(myEq,Crep,Crep'); %'//get a comparison matrix by comparing with transpose
comp = tril(comp) - eye(size(comp)); %// ignore upper triangle and diagonal
idx = find( sum(comp,2)==0 ); %// get index of matrices we want to keep
result = C(idx); %// get result
The output is deletes the 4th and 5th matrices, leaving the first three magic matrices:
>> result
result =
[3x3 double] [4x4 double] [5x5 double]
>> result{1}, result{2}, result{3}
ans =
8 1 6
3 5 7
4 9 2
ans =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
ans =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
Here's code that does what you want.
mydup = rand(5,2);
mycell = {mydup;mydup;rand(7,2);rand(3,2);rand(5,2)}
myNewCell = trimCell(mycell)
Where trimCell is the function below:
function myNewCell = trimCell(myCell)
exclude = false(size(myCell));
for iter = 1:size(myCell,1)
toCompare = myCell{iter};
comparisons = cellfun(#(x) all(size(x)==size(toCompare)) && myEquals(x, toCompare),myCell);
% comparisons has at least 1 true in it, because toCompare==toCompare
exclude(iter) = sum(comparisons)>1;
end
myNewCell = myCell(~exclude);
end
function boolValue = myEquals(x,y)
assert(all(size(x)==size(y)));
boolValue = true;
for iter = 1:size(x,1)
thisRow = all(sort(x(iter,:))==sort(y(iter,:)));
boolValue = boolValue && thisRow;
if ~boolValue
return
end
end
end
Basically, what this function does, is, for each matrix in the cell it checks first if the sizes are equal. If the sizes aren't equal, then we know the matrices aren't equal, even if we reorder the rows.
If they are the same size, then we need to check if they're equal after row reordering. That's accomplished by the function myEquals which goes through row by row and sorts the rows before comparing them. It exits with false on the first row it finds that is not equal after reordering.
Hope this helps,
Andrew
just lets make it simple, assume that I have a 10x3 matrix in matlab. The numbers in the first two columns in each row represent the x and y (position) and the number in 3rd columns show the corresponding value. For instance, [1 4 12] shows that the value of function in x=1 and y=4 is equal to 12. I also have same x, and y in different rows, and I want to average the values with same x,y. and replace all of them with averaged one.
For example :
A = [1 4 12
1 4 14
1 4 10
1 5 5
1 5 7];
I want to have
B = [1 4 12
1 5 6]
I really appreciate your help
Thanks
Ali
Like this?
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7];
[x,y] = consolidator(A(:,1:2),A(:,3),#mean);
B = [x,y]
B =
1 4 12
1 5 6
Consolidator is on the File Exchange.
Using built-in functions:
sparsemean = accumarray(A(:,1:2), A(:,3).', [], #mean, 0, true);
[i,j,v] = find(sparsemean);
B = [i.' j.' v.'];
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7]; %your example data
B = unique(A(:, 1:2), 'rows'); %find the unique xy pairs
C = nan(length(B), 1);
% calculate means
for ii = 1:length(B)
C(ii) = mean(A(A(:, 1) == B(ii, 1) & A(:, 2) == B(ii, 2), 3));
end
C =
12
6
The step inside the for loop uses logical indexing to find the mean of rows that match the current xy pair in the loop.
Use unique to get the unique rows and use the returned indexing array to find the ones that should be averaged and ask accumarray to do the averaging part:
[C,~,J]=unique(A(:,1:2), 'rows');
B=[C, accumarray(J,A(:,3),[],#mean)];
For your example
>> [C,~,J]=unique(A(:,1:2), 'rows')
C =
1 4
1 5
J =
1
1
1
2
2
C contains the unique rows and J shows which rows in the original matrix correspond to the rows in C then
>> accumarray(J,A(:,3),[],#mean)
ans =
12
6
returns the desired averages and
>> B=[C, accumarray(J,A(:,3),[],#mean)]
B =
1 4 12
1 5 6
is the answer.
I have two vectors in MATLAB, say:
x = [1 20 3 7 10]
and
y = [2 51 1 9 18]
How can I plot y vs K where x has sorted value order (1 3 7 10 20) with their respective y values like the following?
x = [1 3 7 10 20]
y = [2 1 9 18 51]
Call sort with a second output argument.
x = [1 20 3 7 10]
y = [2 51 1 9 18]
[xsorted, I] = sort(x)
ysorted = y(I)
XY = sortrows([x ; y]');
plot(XY(:,1), XY(:,2));
Concatenate the matrices, transpose them and then you can use sortrows to order by X