如何将一个由{1xN cell}数组组成的{Mx1}单元格数组转换为由{Mx1 cell}数组组成的{1xN}单元格数组?

10
假设 C 是形状为 M × 1 的单元数组(即,size(C) 返回 [M 1]),并且每个 C 中的元素反过来又是一个形状为 1 × N 的单元数组。
我通常希望将这样的单元数组转换为一个新的单元数组 D,其形状为 1 × N,其中元素是形状为 M × 1 的单元数组,且对于所有的 0 < i ≤ M 和 0 < j ≤ NC{i}{j} 等于 D{j}{i}
我使用以下代码实现此功能。
D = arrayfun(@(j) arrayfun(@(i) C{i}{j}, (1:M)', 'un', 0), 1:N, 'un', 0);

尽管需要进行这种操作的频率相当高(毕竟它有点像“单元数组转置”),但我认为我应该问一下:

是否有更标准的方法来执行此操作?

请注意,所需的 D 与其他情况下不同。

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

或者等价地说,

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

E是一个二维(M × N)的单元格数组,而CD都是由一维单元格数组构成的一维单元格数组。当然,将E转换回CD也是另一个经常需要进行的操作(这种事情在MATLAB中永远不会停止),但我会在另一篇文章中讨论它。


这个问题背后的动机远不止上述问题。事实证明,我的MATLAB代码中有很大一部分,甚至更大的部分我的MATLAB编程时间和精力,都被用于这种基本上无用的数据格式转换工作。当然,在进行任何类型的计算工作时,格式转换是不可避免的,但是我发现在MATLAB中,我做得更多,或者说要花费更多的精力去处理,比起在其他系统(例如Mathematica或Python/NumPy)中工作时要多得多。我希望通过建立我的MATLAB“格式转换技巧”库,能够将我必须专注于格式转换的MATLAB编程时间的占比降至更为合理的水平。


P.S.以下代码构造了一个像上面描述的C,对于M = 5和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}

+1 对于良好的答案和可重现的代码。我认为双“arrayfun”方法并不是一个可怕的东西。 - Luis Mendo
@LuisMendo:想象一下,如果每次需要矩阵转置时都要写类似的东西,那该有多可怕。对我来说,这是一个巨大的难题,因为我经常需要这个操作。当然,我可能能够编写一个函数来简化事情,但在此之前,我想问一下是否已经有适当的内置替代方案。 - kjo
@kjo:这只是一个中间步骤,你的实际问题是如何将单元数组C转置为一个包含五个2x1单元格的1x5单元格吗?也许需要使用不同的数据结构。也许可以考虑使用struct - horchler
@horchler:正如我在小字里说的那样,这是一个经常出现的情况,它在许多不同的上下文中出现。在早些时候,你提到的转置问题可能是其中之一,但现在不是。 - kjo
1
@kjo 我同意你的看法。但是,一个单元数组中嵌套了单元数组,这是一种有点复杂的结构,所以我认为需要这样一个复杂的表达式来“转置”它是很正常的。这不是转置,更像是把它翻过来。如果你有一个二维单元数组而不是嵌套的单元数组,你可以简单地使用transpose。 - Luis Mendo
1个回答

3
这里有一个小技巧,使用num2cell来处理单元数组输入 - 关键是首先将C扩展为5x2的单元数组(等同于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)])

更简单地说
D = num2cell(cat(1,C{:}),1)

其中D {:}返回:

ans = 

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


ans = 

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

D返回C的反向操作可以通过以下方式完成:

% 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)])

或者

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

因此,您可能会创建以下类似的函数,可以在任何方向上工作:
transpose_cells = @(C)num2cell(cat(isrow(C)+1,C{:}),isrow(C)+1);
isequal(transpose_cells(transpose_cells(C)),C)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接