Matlab: 2d-array, rows different lengths - matlab

In Matlab, I want to create a two-dimensional array. However, I cannot create a matrix, because the rows are all different lengths.
I am new to Matlab, and I would normally do this in C++ by creating an array of pointers, with each pointer pointing towards its own array.
How should I do this in Matlab? Thanks.

You can use cell arrays, which can contain data of varying types and sizes.
Like this:
data = {[1]; [2,2]; [3,3,3]};
Check out here for more examples.

You could use a cell array:
C = {[1,2,3];
[1,2,3,4,5];
[1,2]};
Or pad with NaN or 0 or Inf etc
N = [1, 2, 3, NaN, NaN;
1, 2, 3, 4, 5;
1, 2, NaN, NaN, NaN]
It really depends on what you will be doing with your data next

Use cell ararys as mentioned by others. Listing out some code and comments to explain it -
%%// Create a cell array to store data
Arr = {[1 3 4 6 8];
[1 8 3];
[4 6 3 2];
[6 3 6 2 6 8]}
%%// Access element (3,4)
element = Arr{3}(4)
Outputs
Arr =
[1x5 double]
[1x3 double]
[1x4 double]
[1x6 double]
element =
2

Related

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

Find intersection of two cell arrays in mATLAB

I have two cell arrays C & D , they contains numerical data (but some cells are empty) . the data that is inside each cell may be a 2D array, I want to find the intersection of each cell in C with each cell in D
How can I do such thing?
for example : if the size of C & D is 10-by10
C= [ {1 2 } ,{ 3 4},.... etc]
D = [ { 1 34 7} , {2 5},... etc]
Out = c intersect D
out= [ { 1} , {},.... etc]
>> C = {1 [2 3 4; 5 6 7] [] [] 5};
>> D = {1:2 3:5 6 7:9 []};
>> R = cellfun(#(c, d) intersect(c(:), d(:)), C, D, 'uniformoutput', 0);
>> R{:}
ans =
1
ans =
3
4
5
ans =
Empty matrix: 1-by-0
ans =
Empty matrix: 0-by-1
ans =
Empty matrix: 1-by-0
If your data is only numeric (each cell contains a numeric value and empties) I suggest you to change that to a numeric array and use the intersect function. It is easy to represent missing values as NaN.
To convert to double:
tmp = {1, 2, 3, 4, 5, []};
% // Getting rid of the empties
index_empties = cellfun(#isempty, tmp);
tmp(index_empties) = {NaN};
% // converting to double
tmp_double = cellfun(#double, tmp);
Cell values are there to easily create a vector with non-homogeneous data types (Strings and numbers, for example). It is quite common see people using cells to store numbers. While this might be valid in some cases, using cells to store homogeneous data will waste memory and will complicate some operations. For example, you cannot easily sum two cell-vectors with numeric data while summing two double-vectors is trivial.

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 ];

How can I group variables from a mat file by name and collect them in a structure?

I'm reading in a mat file which has variables with a counter e.g. a1, a2, a3, b, c1 and c2.
I want to put the data into a structure with field names "a", "b" and "c" where
a=[a1;a2;a3], b=b and c = [c1; c2].
The algorithm needs to be able to do this, without knowing what the maximum value of the counter of each variable is. How can I do this?
First, you would load your mat file data using the load function, placing it in a structure. Let's say that gave you the following sample data:
>> s = struct('a1',1:5,'b',[],'a2',1:5,'a3',1:5,'c1',1:3,'c2',3:5)
s =
a1: [1 2 3 4 5]
b: []
a2: [1 2 3 4 5]
a3: [1 2 3 4 5]
c1: [1 2 3]
c2: [3 4 5]
Then, we'll order the fields alphabetically using orderfields, find the field names using fieldnames, use regexprep to strip all trailing numbers off of the field names, then get the unique substrings using unique:
>> s = orderfields(s);
>> [strs, inds] = unique(regexprep(fieldnames(s), '\d*$', ''), 'last')
strs =
'a'
'b'
'c'
inds =
3
4
6
Using the indices returned by unique, we can calculate how many times each substring appears by doing diff([0; inds]). Then, we group the structure data into a cell array using these counts and struct2cell and mat2cell:
>> data = mat2cell(struct2cell(s), diff([0; inds]))
data =
{3x1 cell}
{1x1 cell}
{2x1 cell}
Notice that we have a cell array of cell arrays. If you know for sure that each set of entries for each substring will concatenate properly (as in our example), you can concatenate them using cellfun and cell2mat as follows:
>> data = cellfun(#cell2mat, data, 'UniformOutput', false)
data =
[3x5 double]
[]
[2x3 double]
Now we have a cell array of matrices, and a new structure can be made using cell2struct:
>> snew = cell2struct(data, strs)
snew =
a: [3x5 double]
b: []
c: [2x3 double]

How can I insert each row of a matrix into cells in Matlab?

Suppose A = [1 2 3;4 5 6;7 8 9]
I want to convert it to B = [{[1,2,3]};{[4,5,6]};{[7,8,9]}]
How can I do that in an easy way?
You can use mat2cell function.
From the documentation:
C = mat2cell(A,dim1Dist,...,dimNDist) divides array A into smaller
arrays within cell array C. Vectors dim1Dist,...dimNDist specify how
to divide the rows, columns, and (when applicable) higher dimensions
of A.
You can do it like this:
A = [1 2 3; 4 5 6; 7 8 9];
B = mat2cell(A, [1 1 1], 3);
will give you:
B={[1 2 3];[4 5 6];[7 8 9]}
Documentation also says:
C = mat2cell(A,rowDist) divides array A into an n-by-1 cell array C,
where n == numel(rowDist).
So, if you are always going to split your matrix to rows, but not to columns, you can do it without the second parameter.
B = mat2cell(A, [1 1 1]);
A better, generalized way would be:
mat2cell(A, ones(1, size(A, 1)), size(A, 2));
You can't have a "matrix of cells" like your notation for B implied.
A cell array allows you to store "any data type" in the individual cells. You can't store a cell as a data type in an array.
So let's assume you meant to say you wanted B = {[1,2,3], [4,5,6], [7,8,9]};
If that is the case, then
B = cell(1,3);
for ii=1:3
B(ii) = {A(ii, :)};
end
should do the trick.
Note - edited based on Hadi's comment.