De-nest elements of cell-matrix into a matrix - matlab

EDIT: It turns out this problem is not solved, as it fails to handle empty cells in the source data. i.e. k = {1 2 [] 4 5}; cat( 2, k{:} ) gives 1 2 4 5 not 1 2 NaN 4 5. So the subsequent reshape is now misaligned. Can anyone outline a strategy for handling this?
I have data of the form:
X = { ...
{ '014-03-01' [1.1] [1.2] [1.3] }; ...
{ '014-03-02' [2.1] [2.2] [2.3] }; ... %etc
}
I wish to replace [1.1] with 1.1 etc, and also replace the date with an integer using datenum
So I may as well use a standard 2D matrix to hold the result (as every element can be expressed as a Double).
But how to go out this repacking?
I was hoping to switch the dates in-place using X{:,1} = datenum( X{:,1} ) but this command fails.
I can do:
A = cat( 1, X{:} )
dateNums = datenum( cat( 1, A{:,1} ) )
values = reshape( cat( 1, A{:,2:end} ), size(X,1), [] )
final = [dateNums values]
Well, that works, but I don't feel at all comfortable with it.
>> u = A{:,1}
u =
014-03-01
>> cat(1,u)
ans =
014-03-01
This suggests only one value is output. But:
>> cat(1,A{:,1})
ans =
014-03-01
014-03-02
So A{:,1} must be emitting a sequential stream of values, and cat must be accepting varargs.
So now if I do A{:,2:end}, it is now spitting out that 2D subgrid again as a sequential stream of values...? And the only way to get at that grid is to cat -> reshape it. Is this a correct understanding?
I'm finding MATLAB's console output infuriatingly inconsistent.

The "sequential stream of values" is known as a comma-separated list. Doing A{:,1} in MATLAB in the console is equivalent to the following syntax:
>> A{1,1}, A{2,1}, A{3,1}, ..., A{end,1}
This is why you see a stream of values because it is literally typing out each row of the cell for the first column, separated by commas and showing that in the command prompt. This is probably the source of your infuriation as you're getting a verbose dump of all of the contents in the cell when you are unpacking them into a comma-separated list. In any case, this is why you use cat because doing cat(1, A{:,1}) is equivalent to doing:
cat(1, A{1,1}, A{2,1}, A{3,1}, ... A{end,1})
The end result is that it takes all elements in the 2D cell array of the first column and creates a new result concatenating all of these together. Similarly, doing A{:, 2:end} is equivalent to (note the column-major order):
>> A{1, 2}, A{2, 2}, A{3, 2}, ..., A{end, 2}, A{1, 3}, A{2, 3}, A{3, 3}..., A{end, 3}, ..., A{end, end}
This is why you need to perform a reshape because if you did cat with this by itself, it will only give you a single vector as a result. You probably want a 2D matrix, so the reshape is necessary to convert the vector into matrix form.
Comma-separated lists are very similar to Python's splat operator if you're familiar with Python. The splat operator is used for unpacking input arguments that are placed in a single list or iterator type... so for example, if you had the following in Python:
l = [1, 2, 3, 4]
func(*l)
This is equivalent to doing:
func(1, 2, 3, 4)
The above isn't really necessary to understand comma-separated lists, but I just wanted to show you that they're used in many programming languages, including Python.

There is a problem with empty cells: cat will skip them. Which means that a subsequent reshape will throw a 'dimension mismatch' error.
The following code simply removes rows containing empty cells (which is what I require) as a preprocessing step.
(It would only take a minor alteration to replace empty cells with NaNs).
A = cat( 1, X{:} );
% Remove any row containing empty cells
emptiesInRow = sum( cellfun('isempty',A), 2 );
A( emptiesInRow > 0, : ) = [];
% Date is first col
dateNums = datenum( cat( 1, A{:,1} ) );
% Get other cols
values = reshape( cat( 1, A{:,2:end} ), size(A,1), [] );
% Recombine into (double) array
grid = [dateNums values]; %#ok<NASGU>

Related

Debugging the error "Dimensions of arrays being concatenated are not consistent" in MATLAB

I have a function VanderPol() which is supposed to give a vector output, but it doesn't seem to work. It is just three lines of code but I cannot seem to find the bug.
The function is
function [output] = VanderPol(y, i)
output = [y(2,i); (1-y(1,i)^2)*y(2,i) -y(1,i)];
end
and it is called as
z = [1 2 3;
4 5 6];
VanderPol(z,1)
I recieve an error message stating that VanderPol(z,1) is faulty, but no hint why. The exact error message is shown below. Can anyone spot the bug?
Error using vertcat
Dimensions of arrays being concatenated are not consistent.
This is a bit of an edge case: You can construct arrays in MATLAB by separating elements either by a comma , or a space . Thus, the following ways both work and give the same result:
a = [1, 2, 3]
b = [1 2 3]
When building matrices, this works similarly, and rows are separated by a semicolon or a new line, i.e. we have the following equivalent possibilities:
A = [1, 2, 3; 4, 5, 6]
B = [1 2 3; 4 5 6]
C = [1, 2, 3
4, 5, 6]
D = [1 2 3
4 5 6]
Now to your example: your array is the following:
[y(2,i); (1-y(1,i)^2)*y(2,i) -y(1,i)]
The first row contains one element y(2,i). The second row, however, is interpreted as two elements: (1-y(1,i)^2)*y(2,i) and -y(1,i), due to the space between these parts. I.e. MATLAB thinks you are using a space to separate two parts of an array like in b above. It interprets the input like the following:
[y(2,i); (1-y(1,i)^2)*y(2,i), -y(1,i)]
If you paste the code into MATLAB, you will thus get an error complaining that it is not possible to have an array with 1 element in the first and 2 elements in the second row:
>> [y(2,i); (1-y(1,i)^2)*y(2,i) -y(1,i)]
Error using vertcat
Dimensions of arrays being concatenated are not consistent.
To solve the problem you have to tell MATLAB that there is only one element in the second row, given by the subtraction (1-y(1,i)^2)*y(2,i) -y(1,i). Here are some ways to do that:
output = [y(2,i); (1-y(1,i)^2)*y(2,i) - y(1,i)]; % spaces on both sides of -
output = [y(2,i); (1-y(1,i)^2)*y(2,i)-y(1,i)]; % no spaces around -
output = [y(2,i); ((1-y(1,i)^2)*y(2,i) -y(1,i))]; % parentheses around everything

MATLAB: Using cellfun with multi level cell array

I have a multi-level cell array. The individual levels could be of different size. I am wondering how I can apply cellfunto the lowest level. Imagine the following mulit-level cell array:
a = {randi(10,5,1), randi(5,5,1)}
b = randi(100,5,1,10)
f = {a,b}
Now, I would like to drill down as much as possible and apply cellfun to the deepest level possible of f. At the and of each level, there is a 2D/3D matrix. Let's say, I simply want to add 5 to each value. What's the most efficient way?
Here's the result I am looking for.
[a_nRows, a_nCols, a_nPages] = size(a)
x = cellfun(#plus, f{1}, repmat({5}, a_nRows, a_nCols, a_nPages), 'UniformOutput', false)
y = cellfun(#plus, f(2), {5}, 'UniformOutput', false)
You can use recursion for this.
Firstly, define a function which does one of two things
If the input is a numeric matrix, apply some operation.
If the input is a cell, call this same function with the cell's contents as inputs.
The function would look something like this (defined locally to another function or in its own m file):
function out = myfunc( in, op )
if iscell( in )
out = cellfun( #(x) myfunc(x, op), in, 'UniformOutput', false );
elseif isnumeric( in )
out = op( in );
else
error( 'Cell contents must be numeric or cell!' )
end
end
Then you can call myfunc on your cell. Here is an example similar to yours:
a = {[1 2; 3 4], {eye(2), 10}}; % Nested cell arrays with numeric contents
op = #(M) M + 5; % Some operation to apply to all numeric contents
myfunc( a, op )
% >> ans =
% { [6 7; 8 9], {[6 5; 5 6], 15}}
Directly using your example, the output of myfunc(f, #(M)M+5) is the same as your {x, y{1}} - i.e. the operation op is applied to every cell and nested cell with the result structured the same as the input.

Extract data from a Cell Array using a vector and converting into an array

I have a cell array [5x1] which all cells are column vectors such as:
exInt =
[46x1 double]
[54x1 double]
[40x1 double]
[51x1 double]
[ 9x1 double]
I need to have a vector (vec) containing the cells in extInt I need to extract and then I have to convert these into a single column array. Such as:
vec = [1,3];
Output = cell2mat(extInt{vec})
Output should become something an array [86x1 double].
The way I have coded I get:
Error using cell2mat
Too many input arguments.
If possible, I would like to have a solution not using a loop.
The best approach here is to use cat along with a comma-separted list created by {} indexing to yield the expected column vector. We specify the first dimension as the first argument since you have all column vectors and we want the output to also be a column vector.
out = cat(1, extInt{vec})
Given your input, cell2mat attempts to concatenate along the second dimension which will fail for your data since all of the data have different number of rows. This is why (in your example) you had to transpose the data prior to calling cell2mat.
Update
Here is a benchmark to compare execution times between the cat and cell2mat approaches.
function benchit()
nRows = linspace(10, 1000, 100);
[times1, times2] = deal(zeros(size(nRows)));
for k = 1:numel(nRows)
rows = nRows(k);
data = arrayfun(#(x)rand(randi([10, 50], 1), 1), 1:rows, 'uni', 0);
vec = 1:2:numel(data);
times1(k) = timeit(#()cat_method(data, vec));
data = arrayfun(#(x)rand(randi([10, 50], 1), 1), 1:rows, 'uni', 0);
vec = 1:2:numel(data);
times2(k) = timeit(#()cell2mat_method(data, vec));
end
figure
hplot(1) = plot(nRows, times1 * 1000, 'DisplayName', 'cat');
hold on
hplot(2) = plot(nRows, times2 * 1000, 'DisplayName', 'cell2mat');
ylabel('Execution Times (ms)')
xlabel('# of Cell Array Elements')
legend(hplot)
end
function out = cat_method(data, vec)
out = cat(1, data{vec});
end
function out = cell2mat_method(data, vec)
out = cell2mat(data(vec)');
end
The reason for the constant offset between the two is that cell2mat calls cat internally but adds some additional logic on top of it. If you just use cat directly, you circumvent that additional overhead.
You have a small error in your code
Change
Output = cell2mat(extInt{vec});
to
Output = cell2mat(extInt(vec));
For cells, both brackets and parentheses can be used to get information. You can read some more about it here, but to summarize:
Use curly braces {} for setting or getting the contents of cell arrays.
Use parentheses () for indexing into a cell array to collect a subset of cells together in another cell array.
In your example, using brackets with index vector vec will produce 2 separate outputs (I've made a shorter version of extInt below)
extInt = {[1],[2 3],[4 5 6]};
extInt{vec}
ans =
1
ans =
4 5 6
As this is 2 separate outputs, it will also be 2 separate input to the function cell2mat. As this function only takes one input you get an error.
One alternative is in your own solution. Take the two outputs and place them inside a new (unnamed) cell
{extInt{vec}}
ans =
[1] [1x3 double]
Now, this (single) result goes into cell2mat without a problem.
(Note though that you might need to transpose your result before depending on if you have column or row vectors in your cell. The size vector (or matrix) to combine need to match/align.)
Another way as to use parentheses (as above in my solution). Here a subset of the original cell is return. Therefore it goes directly into the cell2matfunction.
extInt(vec)
ans =
[1] [1x3 double]
I have been messing around and I got this working by converting this entry into a new cell array and transposing it so the dimensions remained equivalent for the concatenating process
Output = cell2mat({extInt{vec}}')
use
Output = cell2mat(extInt(vec))
Since you want to address the cells in extInt not the content of the cells
extInt(vec)
extInt{vec}
try those to see whats going on

Print the value of a multidimensional array with the output as compatible matlab code

For matrices with dimensions equal or less then 2 the command is:
For instance:
>> mat2str(ones(2,2))
ans =
[1 1;1 1]
However, as the help states, this does not work for higher dimensions:
>> mat2str(rand(2,2,2))
Error using mat2str (line 49)
Input matrix must be 2-D.
How to output matrices with higher dimensions than 2 with that is code compatible, without resorting to custom made for loops?
This isn't directly possible because there is no built-in character to represent concatenation in the third dimension (an analog to the comma and semicolon in 2D). One potential workaround for this would be to perform mat2str on all "slices" in the third dimension and wrap them in a call to cat which, when executed, would concatenate all of the 2D matrices in the third dimension to recreate your input matrix.
M = reshape(1:8, [2 2 2]);
arrays = arrayfun(#(k)mat2str(M(:,:,k)), 1:size(M, 3), 'uni', 0);
result = ['cat(3', sprintf(', %s', arrays{:}), ')'];
result =
'cat(3, [1 3;2 4], [5 7;6 8])'
isequal(eval(result), M)
1
UPDATE
After thinking about this some more, a more elegant solution is to flatten the input matrix, run mat2str on that, and then in the string used to recreate the data, we utilize reshape combined with the original dimensions to provide a command which will recreate the data. This will work for any dimension of data.
result = sprintf('reshape(%s, %s);', mat2str(M(:)), mat2str(size(M)));
So for the following 4D input
M = randi([0 9], 1, 2, 3, 4);
result = sprintf('reshape(%s, %s);', mat2str(M(:)), mat2str(size(M)));
'reshape([6;9;4;6;5;2;6;1;7;2;1;7;2;1;6;2;2;8;3;1;1;3;8;5], [1 2 3 4]);'
Now if we reconstruct the data using this generated string, we can ensure that we get the correct data back.
Mnew = eval(result);
size(Mnew)
1 2 3 4
isequal(Mnew, M)
1
By specifying both the class and precision inputs to mat2str, we can even better approximate the input data including floating point numbers.
M = rand(1,2,3,4,5);
result = sprintf('reshape(%s, %s);', mat2str(M(:),64,'class'), mat2str(size(M)));
isequal(eval(result), M)
1

Matlab: Get Fields of Structures Inside Cells, 2D Strucutre Array

I am hoping to have a multidimensional array of structures, but I can't seem to get at the field of the contained elements. or in code:
mySample = struct('a', zeros(numA),'b', zeros(numB));
Data = cells(height,width);
disp(Data(1,1).a);
The bottom line fails with an error such as
"Improper index matrix reference."
How is a 2D array of structures done in Matlab?
There are a couple of ways to create an array of structures ("structure array" or "struct array"). Note that in a struct array, each element must have the same fields. For example, if s(1) has fields "a" and "b", then s(2)..s(n) must have fields "a" and "b".
% num rows
n = 10;
% num cols
m = 50;
% method 1, which will repeat a structure
s = struct('field1', 10, 'field2', 20);
sArray = repmat(s, n, m);
% method 2, which initializes each field to empty []
sArray(n,m) = struct('field1', [], 'field2', []);
You can expand on that to go beyond the second dimension eaisly:
sArray(n,m,p) = struct('field1', [], 'field2', []);
You could also preallocate the array and use a for-loop to set the value of each field. Additionally:
help deal
help structfun
You could also create a cell array of structures, which provides more flexibility: each structure in the cell array may have different fields.
c = cell(1,2);
c{1} = struct('a', 1, 'b', 2);
c{2} = struct('z', 0, 'q', 5);
2D array of structures can be done in 2 ways:
Cell array of structs - Heterogenic container. This means that each struct can be different.
x = {struct('a',1,'b',2), struct('c',3) ; struct() ; struct('aa',[5 6])};
disp(x{1,2});
Arrays of structs - Homegenic container. This means that all strucs must be the same - type safety.
x = struct('a',{1 2 3 ; 1 2 3},'b', {4 5 6; 7 8 9 });
disp(x(1,2));