AnnaSchumann
AnnaSchumann

Reputation: 1271

Conversion from 3D cell array to a set of 2D matrices

I have a 3D-cell array designated as A{s,i,h}, serving as a store for large amounts of numerical data during a nested-loop portion of my script. Some of the cell entries will be blank [ ], whilst the rest consist of numbers - either singular or in arrays (1 x 10 double etc.):

enter image description here

I want to convert this cell array to a set of 2D matrices.

Specifically, one separate matrix for each value of h (h is always equal 1:3) and one column in each matrix for every value of s. Each column will contain all the numerical data combined - it does not need to be separated by i.

How can I go about this? I ordinarily deal with 3D-cell arrays in this form to produce separate matrices (one for each value of h) using something like this:

lens = sum(cellfun('length',reshape(A,[],size(A,3))),1);
max_length = max(lens);
mat = zeros(max_length,numel(lens));
mask = bsxfun(@le,[1:max_length]',lens);
mat(mask) = [A{:}];
mat(mat==0) = NaN;
mat = sort(mat*100);
Matrix1 = mat(~isnan(mat(:,1)),1);
Matrix2 = mat(~isnan(mat(:,2)),2);
Matrix3 = mat(~isnan(mat(:,3)),3);

However in this instance, each matrix had only a single column. I'm have trouble adding multiple columns to each output matrix.

Upvotes: 4

Views: 919

Answers (2)

Luis Mendo
Luis Mendo

Reputation: 112659

1. Result in the form of a cell array of matrices (as requested)

Here's one possible approach. I had to use one for loop. However, the loop can be easily avoided if you accept a 3D-array result instead of a cell array of 2D-arrays. See second part of the answer.

If you follow the comments in the code and inspect the result of each step, it's straightforward to see how it works.

%// Example data
A(:,:,1) = { 1:2, 3:5, 6:9; 10 11:12 13:15 };
A(:,:,2) = { 16:18, 19:22, 23; 24:28, [], 29:30 };

%// Let's go
[S, I, H] = size(A);
B = permute(A, [2 1 3]); %// permute rows and columns
B = squeeze(mat2cell(B, I, ones(1, S), ones(1, H))); %// group each col of B into a cell...
B = cellfun(@(x) [x{:}], B, 'uniformoutput', false); %// ...containing a single vector
t = cellfun(@numel, B); %// lengths of all columns of result
result = cell(1,H); %// preallocate
for h = 1:H
    mask = bsxfun(@le, (1:max(t(:,h))), t(:,h)).'; %'// values of result{h} to be used
    result{h} = NaN(size(mask)); %// unused values will be NaN
    result{h}(mask) = [B{:,h}]; %// fill values for matrix result{h}
end

Result in this example:

A{1,1,1} =
     1     2
A{2,1,1} =
    10
A{1,2,1} =
     3     4     5
A{2,2,1} =
    11    12
A{1,3,1} =
     6     7     8     9
A{2,3,1} =
    13    14    15
A{1,1,2} =
    16    17    18
A{2,1,2} =
    24    25    26    27    28
A{1,2,2} =
    19    20    21    22
A{2,2,2} =
     []
A{1,3,2} =
    23
A{2,3,2} =
    29    30

result{1} =
     1    10
     2    11
     3    12
     4    13
     5    14
     6    15
     7   NaN
     8   NaN
     9   NaN
result{2} =
    16    24
    17    25
    18    26
    19    27
    20    28
    21    29
    22    30
    23   NaN

2. Result in the form of 3D array

As indicated above, using a 3D array to store the result permits avoiding loops. In the code below, the last three lines replace the loop used in the first part of the answer. The rest of the code is the same.

%// Example data
A(:,:,1) = { 1:2, 3:5, 6:9; 10 11:12 13:15 };
A(:,:,2) = { 16:18, 19:22, 23; 24:28, [], 29:30 };

%// Let's go
[S, I, H] = size(A);
B = permute(A, [2 1 3]); %// permute rows and columns
B = squeeze(mat2cell(B, I, ones(1, S), ones(1, H))); %// group each col of B into a cell...
B = cellfun(@(x) [x{:}], B, 'uniformoutput', false); %// ...containing a single vector
t = cellfun(@numel, B); %// lengths of all columns of result
mask = bsxfun(@le, (1:max(t(:))).', permute(t, [3 1 2])); %'// values of result to be used
result = NaN(size(mask)); %// unused values will be NaN
result(mask) = [B{:}]; %// fill values

This gives (compare with result of the first part):

>> result
result(:,:,1) =
     1    10
     2    11
     3    12
     4    13
     5    14
     6    15
     7   NaN
     8   NaN
     9   NaN
result(:,:,2) =
    16    24
    17    25
    18    26
    19    27
    20    28
    21    29
    22    30
    23   NaN
   NaN   NaN

Upvotes: 2

Matt
Matt

Reputation: 2802

Brute force approach:

[num_s, num_i, num_h] = size(A);
cellofmat = cell(num_h,1);
for matrix = 1:num_h
    sizemat = max(cellfun(@numel, A(:,1,matrix)));
    cellofmat{matrix} = nan(sizemat, num_s);
    for column = 1:num_s
        lengthcol = length(A{column, 1, matrix});
        cellofmat{matrix}(1:lengthcol, column) = A{column, 1,matrix};
    end
end
Matrix1 = cellofmat{1};
Matrix2 = cellofmat{2};
Matrix3 = cellofmat{3};

I don't know what your actual structure looks like but this works for A that is setup using the following steps.

A = cell(20,1,3);
for x = 1:3
    for y = 1:20
        len = ceil(rand(1,1) * 10);
        A{y,1,x} = rand(len, 1);
    end
end

Upvotes: 0

Related Questions