Reputation: 1271
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.):
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
Reputation: 112659
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
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
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