Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert an {Mx1} cell array of {1xN cell} arrays into a {1xN} cell array of {Mx1 cell} arrays?

Suppose that C is a cell array with shape M × 1 (i.e., size(C) returns [M 1]), and that each element of C is in turn a cell array with shape 1 × N.

I often want to convert such a cell array to a new cell array D having shape 1 × N, with elements being cell arrays with shape M × 1, and such that C{i}{j} equals D{j}{i} for all 0 < i &leq; M, and 0 < j &leq; N.

I use the following monstrosity for this

D = arrayfun(@(j) arrayfun(@(i) C{i}{j}, (1:M)', 'un', 0), 1:N, 'un', 0);

but the need for this operation arises often enough (after all, it's sort of a "cell array transpose") that I thought I'd ask:

is there a more standard way to do this operation?

Note that the desired D is different from

E = cat(2, C{:});

or, equivalently,

E = cat(1, D{:});

The E above is a two-dimensional (M × N) cell array, whereas both C and D are one-dimensional cell arrays of one-dimensional cell arrays. Of course, conversion of E back to either C or D is also another often needed operation (this sort of thing is never-ending with MATLAB), but I'll leave it for another post.


The motivation behind this question goes far beyond the problem described above. It turns out that a huge fraction of my MATLAB code, and an even larger fraction of my MATLAB programming time and effort, are devoted to this essentially unproductive chore of converting data from one format to another. Of course, format conversion is unavoidable when doing any kind of computational work, but with MATLAB I find myself doing it a lot more, or at least having to work a lot harder at it, than when I work in other systems (e.g., Mathematica or Python/NumPy). My hope is that by building up my repertoire of MATLAB "format conversion tricks" I will be able to bring down to a more reasonable level the fraction of my MATLAB programming time that I have to devote to format conversion.


P.S. The following code constructs a C like the one described above, for M = 5 and N = 2.

uc = ['A':'Z'];
randstr = @() uc(randi(26, [1 4]));
M = 5;
rng(0);  % keep example reproducible
C = arrayfun(@(i) {randstr() i}, 1:M, 'un', 0)';

% C = 
%     {1x2 cell}
%     {1x2 cell}
%     {1x2 cell}
%     {1x2 cell}
%     {1x2 cell}
% >> cat(1, C{:});
% ans = 
%     'VXDX'    [1]
%     'QCHO'    [2]
%     'YZEZ'    [3]
%     'YMUD'    [4]
%     'KXUY'    [5]
%     

N = 2;
D = arrayfun(@(j) arrayfun(@(i) C{i}{j}, (1:M)', 'un', 0), 1:N, 'un', 0);

% D = 
%     {5x1 cell}    {5x1 cell}
like image 266
kjo Avatar asked Feb 17 '14 17:02

kjo


1 Answers

Here's a little trick using num2cell, which actually works with cell array inputs – the key is to first expand C into a 5-by-2 cell array (equivalent to cell(5,2)).

% Your example to produce C
uc = ['A':'Z'];
randstr = @() uc(randi(26, [1 4]));
M = 5;
rng(0);  % Keep example reproducible
C = arrayfun(@(i) {randstr() i}, 1:M, 'un', 0)';

% D = num2cell(reshape([C{:}],[N M]).',[1 M])
D = num2cell(reshape([C{:}],[size(C{1},2) size(C,1)]).',[1 size(C,1)])

or more simply

D = num2cell(cat(1,C{:}),1)

where D{:} returns:

ans = 

    'VXDX'
    'QCHO'
    'YZEZ'
    'YMUD'
    'KXUY'


ans = 

    [1]
    [2]
    [3]
    [4]
    [5]

The inverse operation to go from D back to C can be accomplished via:

% C = num2cell(reshape([D{:}],[N M]),[M N])
C = num2cell(reshape([D{:}],[size(D{1},1) size(D,2)]),[size(D,2) size(D{1},1)])

or

C = num2cell(cat(2,D{:}),2)

Thus you might be able to create a function like the following that would work in either direction:

transpose_cells = @(C)num2cell(cat(isrow(C)+1,C{:}),isrow(C)+1);
isequal(transpose_cells(transpose_cells(C)),C)
like image 70
horchler Avatar answered Nov 16 '22 04:11

horchler