Group cell-array row by an ID in another row - matlab

I have got this cell-array:
QueueArr = ...
{ [1] [5] [1] [2] [1] [ 1] [ 5] [ 1] [ 2] ;
[6] [8] [7] [9] [5] [10] [18] [17] [19] }
Now I want to group the second row depending on the first one. My result cell-array should look like this:
loopCell = ...
{ [ 1] [ 2] [ 5] ;
[6 7 5 10 17] [ 9 19] [ 8 18] }
I solved this problem with this code:
%// convert from cell to a matrix
loopMatrix = cell2mat(QueueArr);
%// get the unique elements from the first row
loopMatrixUnique = unique(loopMatrix(1,:));
%// create the result cell
loopCell = cell(2,size(loopMatrixUnique,2));
%// iterate through the unique indexes
for i = 1:size(loopMatrixUnique,2)
%// saving the first row
loopCell{1,i} = loopMatrixUnique(i);
%// calculating the grouped elements
loopCell{2,i} = loopMatrix(2,loopMatrix(1,:) == loopMatrixUnique(i));
end
My question now is whether there is an easier or more ideal solution to my problem.

As you were told in comments, the 3rd output of unique is very usefull for your case.
Once you have that, cellfun can also be used to rebuild your cell array quickly:
b = cell2mat(QueueArr(2,:)) ; %// convert bottom line to array for convenience
[C,~,ic]= unique( cell2mat(QueueArr(1,:)) ) ;
R = [ num2cell(C) ; ... %// top row
cellfun( #(x) b(ic==x) , num2cell(1:length(C)) , 'uni',0) ] %// bottom row

I solved it myself with accumarray.
thx to #Dan for the hint.
%// save the second row
sections = [QueueArr{2,:}];
%// get unique chapters and subs
[chapters, ~, subs] = unique([QueueArr{1,:}]);
%// create the grouped cell-array
groups = accumarray(subs, sections, [], #(x) {x});
%// create the result cell-array
loopCell = [num2cell(chatpers); groups.'];

Related

Creating cells in Matlab

I want to create cells in matlab like the following:
Q{1,1,1}=1;
Q{1,1,2}=1;
Q{2,2,1}=1;
Q{2,1,2}=1;
However, I do not want to create this manually. In my application I have some vectors, one of which can be: x=[1 2 3 4]
And with this vector x I want to create
P{1,2,3,4}=1
So the vector x kind of dictates the coordinates of the cell (sorry for bad english).
Since I dont know the length of the vector (it can change from case to case) I cannot do this:
P{x(1,1),x(1,2),x(1,3),x(1,4)}=1;
What can I do here?
EDIT: I put the cells content with number "one" just for an example. The content of cell its gonna be linear matrix variable generated by the function sdpvar from the yalmip toolbox.
First, if you only have numeric content perhaps a matrix is better then a cell.
To populate the spaces within a cell with a certain input you could do the following:
x = [1 2 3 4];
P(x) = {1}
P =
[1] [1] [1] [1]
This also works when a index is skipped
x = [1 2 4 5]
P(x) = {1}
P =
[1] [1] [] [1] [1]
To create your Q cell you should preallocate it to get the correct size, then you could use sub2ind to point out correct indexes
Q = cell(2,2,2)
% To populate all with 1
Q(:) = {1}
Q(:,:,1) =
[1] [1]
[1] [1]
Q(:,:,2) =
[1] [1]
[1] [1]
% To populate only a certain indexes
idx = sub2ind( size(Q), [1 1 2 2], [1 1 2 1], [1 2 1 2]);
Q(idx) = {1}
Q(:,:,1) =
[1] []
[] [1]
Q(:,:,2) =
[1] []
[1] []
I am not sure you can do that without resorting to eval:
>>> x=[1,2,3,4];
>>> value=1 % or whatever you need here
>>> cmd=sprintf('%s%s%s','P{', strjoin(arrayfun(#(a) num2str(a),x,'UniformOutput',false),','), '}=value')
cmd = P{1,2,3,4}=1
>>> eval(cmd)
P = {1x2x3x4 Cell Array}
>>> P{1,2,3,4}
ans = 1
>>>

Merging elements of different cells

Suppose, we have a cell array consisting of ids and one attribute, e.g.
A{1,1}=[1 2;2 4]
A{1,2}=[2 3 5;8 5 6]
Now, I'd like to have a final output consisting of unique ids of two cells (first row values) and corresponding columns have attribute value of each cell separately.
i.e.
C =
[1] [ 2]
[2] [1x2 double] % 4 in first cell and 8 in second cell
[3] [ 5]
[5] [ 6]
it seems that it's not possible to use something like C=[unique(A{1,:}(1,:)')]. Any help is greatly appreciated.
Assuming that each cell has two rows and a variable amount of columns where the first row is the ID and the second row is an attribute, I'd consolidate all of the cells into a single 2D matrix and use accumarray. accumarray is very suitable here because you want to group values that belong to the same ID together and apply a function to it. In our case, our function will simply place the values in a cell array and we'll make sure that the values are sorted because the values that are grouped by accumarray per ID come into the function in random order.
Use cell2mat to convert the cells into a 2D matrix, transpose it so that it's compatible for accumarray, and use it. One thing I'll need to note is that should any IDs be missing, accumarray will make this slot empty. What I meant by missing is that in your example, the ID 4 is missing as there is a gap between 3 and 5 and also the ID 6 between 5 and 7 (I added the example in your comment to me). Because the largest ID in your data is 7, accumarray works by assigning outputs from ID 1 up to ID 7 in increments of 1. The last thing we would need to tackle is to eliminate any empty cells from the output of accumarray to complete the grouping.
BTW, I'm going to assume that your cell array consists of a single row of cells like your example.... so:
%// Setup
A{1,1}=[1 2;2 4];
A{1,2}=[2 3 5;8 5 6];
A{1,3}=[7;8];
%// Convert row of cell arrays to a single 2D matrix, then transpose for accumarray
B = cell2mat(A).';
%// Group IDs together and ensure they're sorted
out = accumarray(B(:,1), B(:,2), [], #(x) {sort(x)});
%// Add a column of IDs and concatenate with the previous output
IDs = num2cell((1:numel(out)).');
out = [IDs out];
%// Any cells from the grouping that are empty, eliminate
ind = cellfun(#isempty, out(:,2));
out(ind,:) = [];
We get:
out =
[1] [ 2]
[2] [2x1 double]
[3] [ 5]
[5] [ 6]
[7] [ 8]
>> celldisp(out(2,:))
ans{1} =
2
ans{2} =
4
8
If you'd like this done on a 2D cell array, where each row of this cell array represents a separate instance of the same problem, one suggestion I have is to perhaps loop over each row. Something like this, given your example in the comments:
%// Setup
A{1,1}=[1 2;2 4];
A{1,2}=[2 3 5;8 5 6];
A{1,3}=[7;8];
A{2,1}=[1 2;2 4];
A{2,2}=[1;7];
%// Make a cell array that will contain the output per row
out = cell(size(A,1),1);
for idx = 1 : size(A,1)
%// Convert row of cell arrays to a single 2D matrix, then transpose for accumarray
B = cell2mat(A(idx,:)).';
%// Group IDs together and ensure they're sorted
out{idx} = accumarray(B(:,1), B(:,2), [], #(x) {sort(x)});
%// Add a column of IDs and concatenate with the previous output
IDs = num2cell((1:numel(out{idx})).');
out{idx} = [IDs out{idx}];
%// Any cells from the grouping that are empty, eliminate
ind = cellfun(#isempty, out{idx}(:,2));
out{idx}(ind,:) = [];
end
We get:
>> out{1}
ans =
[1] [ 2]
[2] [2x1 double]
[3] [ 5]
[5] [ 6]
[7] [ 8]
>> out{2}
ans =
[1] [2x1 double]
[2] [ 4]
>> celldisp(out{1}(2,:))
ans{1} =
2
ans{2} =
4
8
>> celldisp(out{2}(1,:))
ans{1} =
1
ans{2} =
2
7

In Matlab, How to eliminate empty columns from the cell array?

So in 3 X 18 cell array, 7 columns are empty and I need a new cell array that's 3 X 11. Any suggestions without going for looping ?
Let's consider the following cell array. Its second column consists only of [], so it should be removed.
>> c = {1 , [], 'a'; 2, [], []; 3, [], 'bc'}
c =
[1] [] 'a'
[2] [] []
[3] [] 'bc'
You can compute a logical index to tell which columns should be kept and then use it to obtain the result:
>> keep = any(~cellfun('isempty',c), 1); %// keep columns that don't only contain []
keep =
1 0 1 %// column 2 should be removed
>> result = c(:,keep)
result =
[1] 'a'
[2] []
[3] 'bc'
How it works:
cellfun('isempty' ,c) is a matrix the same size as c. It contains 1 at entry (m,n) if and only if c{m,n} is empty.
~cellfun('isempty' ,c) is the logical negation of the above, so it contains 1 where c is not empty.
any(~cellfun('isempty' ,c), 1) applies any to each column of the above. So it's a row vector such that its m-th entry equals 1 if any of the cells of c in that column are non-empty, and 0 otherwise.
The above is used as a logical index to select the desired columns of c.
Use cellfun to detect elements, then from that find columns with empty elements and delete those:
cellarray(:, any(cellfun(#isempty, cellarray), 1)) = [];
If instead you'd like to keep columns with at least one non-empty element, use all instead of any.
For example:
>> cellarray = {1 2 ,[], 4;[], 5, [], 3}
[1] [2] [] [4]
[] [5] [] [3]
>> cellarray(:,any(cellfun(#isempty, cellarray), 1))=[]
cellarray =
[2] [4]
[5] [3]

Change a cell array into a matrix in matlab

everyone, I have a cell array in MATLAB:
'AA->AA' [ 9] [1.8036]
'AA->AC' [ 6] [1.2024]
'AA->AG' [13] [2.6052]
'AA->AT' [ 9] [1.8036]
I want to change it into a matrix with the row names are 'AA->AA','AA->AC'.....ect,I tried cell2mat and it reminded me: All contents of the input cell array must be of the same data type. so anyone give me an idea.
Thanks.
Use containers.Map:
myData = containers.Map();
for ii=1:size( myCell, 1 )
myData( myCell{ii,1} ) = [myCell{ii,:}];
end

How can I group adjacent not-empty cells?

I have a 4x4 cell array C, which
C= {
[1] [3] [6] [ ];
[2] [ ] [ ] [8];
[ ] [4] [ ] [9];
[ ] [5] [7] [ ]}
I want to generate a new cell array D which give me
D = {[1;2], [3], [4;5],[6],[7],[8;9]}
basically I want to 1. combine the adjacent non empty cell in each column vertically and 2. output the new cell array D contains the result.
You can use this. I've used bwlabel from the imaging toolkit:
C= { ...
[1] [3] [6] [ ]; ...
[2] [ ] [ ] [8]; ...
[ ] [4] [ ] [9]; ...
[ ] [5] [7] [ ]};
lenf = #(X)~isempty(X);
lens = cellfun(lenf, C);
lens is now a logical array indicating if any slot in C is empty or not. Now we can construct D by treating each column in lens as a 1 x whatever binary image, and seek regions using bwlabel(). Finally we put the regions into D.
sum = 0;
for k = 1:size(lens,2)
[L,num] = bwlabel(lens(:,k), 4);
for idx = 1:num
D{idx+sum} = cat(1, C{L==idx, k});
end
sum = sum + num;
end
Without depending on another toolbox, you could use this code
nextGroup = diff([true(1, size(C, 2)); cellfun(#isempty, C)]) < 0;
index = reshape(cumsum(nextGroup(:)), size(nextGroup));
result = arrayfun(#(x) horzcat(C{index==x}), 1:index(end,end), ...
'UniformOutput', false);
It works in Octave, so I hope, it works in Matlab, too.
Replace the empty cells with NaN values and make it a matrix with cell2mat and get a logical matrix with all numbers. In a for loop you could easily get all connected values with bwconncomp (this will require the image toolbox).
To avoid the for loop, we can turn into a long vector with a nan separating the end of each column (by adding a row of nans before turning it into a vector).
C(cellfun (#isempty, C)) = {nan};
C = cell2mat (C);
C(end+1, :) = nan;
mask = false (size (C));
mask(~isnan (C)) = true;
list = regionprops (bwconncomp (mask(:)), C(:), 'PixelValues')
list is a struct array so you get a cs-list when you try to access it. You can place all the values into a cell array with:
D = {list(:).Pixelvalues}