Creating cells in Matlab - 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
>>>

Related

Find corresponding array in a cell

Suppose I have a cell of arrays of the same size, for example
arr = {[1 NaN 2 ], ...
[NaN 4 7 ], ...
[3 4 NaN] };
and I also have a vector, for example
vec = [1 2 2];
How do I find the corresponding cell entry that matches the vector vec. Matching means the entries in the same location are the same, except for NaNs?
For this particular vector vec I would like to have 1 returned, since it matches the first row.
Another vector [5 4 7] would return 2.
Vectors that don't match like [7 7 7] and vectors that match more than one entry like [3 4 7] should throw an error.
Note that the vector [3 7 4] does not match the second entry, because the order is important.
For each cell element, just check if
all(isnan(cellElement) | cellElement == vec)
is true, which means, you found a match. If you convert your cell to a matrix checkMatrix with multiple rows and each row corresponding to one cellElement, you can even do it without implementing a loop by repeating vec vertically and comparing the whole matrix in a single step. You will have to tell all() to check along dimension 2 rather than dimension 1 and have find() detect all the matches, like so:
find( all( ...
isnan(checkMatrix) | checkMatrix == repmat(vec,size(checkMatrix, 1),1) ...
, 2)); % all() along dimension 2
So I thought about it and came up with this:
matching_ind = #(x, arr) find(...
cellfun(#(y) max(abs(not(x-y==0).*not(isnan(x-y)))),...
arr) == 0);
inds = matching_ind(vec, arr);
if length(inds) ~= 1
error('42');
end
See if this bsxfun based approach works for you -
A = vertcat(arr{:});
matching_ind = find(all(bsxfun(#eq,A,vec(:).') | isnan(A),2)) %//'
if numel(matching_ind)~=1
error('Error ID : 42.')
else
out = matching_ind(1);
end

Apply cellfun to only one column in cell array

I know cellfun can be applied to an entire cell array and understand its syntax. However is it possible to apply cellfun only to one column in a cell array and not have it affect the other columns?
As user1543042 and It's magic said in the comments, you can apply the cell function to just one column using ':', but you want to add an assignment step. Also, as you want the cell function to return a cell array, you need to flag non-uniformoutput. So, you end up with:
C(:,i) = cellfun(#foo, C(:,i), 'UniformOutput', false)
To see an example in action:
>> C = {1,2,3;4 5 6};
>> C
C =
[1] [2] [3]
[4] [5] [6]
>> size(C)
ans =
2 3
>> cellfun(#(x)x.^2,C(:,1))
ans =
1
16
>> C(:,1) = cellfun(#(x)x.^2,C(:,1))
Conversion to cell from double is not possible.
>> C(:,1) = cellfun(#(x)x.^2,C(:,1),'UniformOutput',false)
C =
[ 1] [2] [3]
[16] [5] [6]
>>

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

Create a cell with values from multiple cells

I have P being a 1x23 cell. In each cell of P, there is a bunch of numbers in nx1 dimension. Cells in P do not have the same row n (e.g., P{1,1} could be 16x1 and P{1,2} could be 17x1. Now, I'd like to put all the values from all the cells in P (P{1,1}, P{1,2}...P{1,23}) into cell D with the dimension of mx1. m never exceeds 1080 so I can do D=cell(1080,1) then eliminate empty cells later. Right now I am having trouble inputting all the values of P into D. Could anyone help?
Thanks.
Is this what you want?
>> P = {[1 2].', [3 4 5].'}
>> D = vertcat(P{:})
D =
1
2
3
4
5
If you really need D in cell form:
>> D = mat2cell(D,ones(1,size(D,1)),1)
D =
[1]
[2]
[3]
[4]
[5]

Searching a cell array of vectors and returning indices

I have a 3000x1 cell array of vectors of different lengths and am looking for a way to search them all for a number and return the cell indices for the first and last occurrence of that number.
So my data looks like this:
[1]
[1 2]
[1 2]
[3]
[6 7 8 9]
etc
And I want to my results to look like this when I search for the number 1:
ans = 1 3
All the indices (e.g. [1 2 3] for 1) would also work, though the above would be better. So far I'm unable to solve either problem.
I've tried
cellfun(#(x) x==1, positions, 'UniformOutput', 0)
This returns a logical array, effectively putting me back at square 1. I've tried using find(cellfun...) but this gives the error undefined function 'find' for input arguments of type 'cell'. Most of the help I can find is for searching for strings within a cell array. Do I need to convert all my vectors to strings for this to work?
C = {[1]
[1 2]
[1 2]
[3]
[6 7 8 9]}; %// example data
N = 1; %// sought number
ind = cellfun(#(v) any(v==N), C); %// gives 1 for cells which contain N
first = find(ind,1);
last = find(ind,1,'last');
result = [ first last ];