如何将 Structure 转换为 n 维矩阵

How can I turn Structure to n-dimensional matrix

我有 2 个矩阵。 第一个是名字。
Names={'a','b','c'};

第二个是数字。
a=[1 3]; b=[4]; c=[2 4 5];

然后我有结构名称,它是名称和数字的组合,它们等于一些行和列相等的随机矩阵。

对于这种情况,我有 6 个组合 (2*1*3),它看起来像 =

a1.b4.c2=[7 8 9; 10 11 14];
a1.b4.c4=[2 4 5; 3 4 7];
a1.b4.c5=[3 2 11; 4 7 8];
a3.b4.c2=[1 1 1; 3 5 12];
a3.b4.c4=[2 7 9 ; 10 11 12];
a3.b4.c5=[4 2 7 ; 5 6 8];

我想return把这个变成n维矩阵。在这种情况下,它是 5 维的,必须看起来像这样;

(:,:,1,4,2)=[7 8 9; 10 11 14];     %%%% for a=1 b=4 c=2
(:,:,1,4,4)=[2 4 5; 3 4 7];        %%%% for a=1 b=4 c=4
(:,:,1,4,5)=[3 2 11; 4 7 8];       %%%% for a=1 b=4 c=5
(:,:,3,4,2)=[1 1 1; 3 5 12];       %%%% for a=3 b=4 c=2
(:,:,3,4,4)=[2 7 9 ; 10 11 12];    %%%% for a=3 b=4 c=4
(:,:,3,4,5)=[4 2 7 ; 5 6 8];       %%%% for a=3 b=4 c=5

我想编写一个通用代码来帮助我为不同数量的姓名和号码完成这项工作,但我做不到。希望你能帮我!谢谢

要获取每个字段的可能数字的所有组合,请使用 ndgrid

[Numbers{1:3}] = ndgrid(a,b,c);

如您所描述的那样,将编号的结构放在工作区中会使以编程方式访问它们变得混乱,您应该尽可能避免这种情况;但是仍然可以使用 eval.

访问它们
evalPattern = strjoin(strcat(Names,'%d'), '.'); % 'a%d.b%d.c%d'
firstNumbers = cellfun(@(n) n(1), Numbers); % [1 4 2]
firstElement = eval(sprintf(evalPattern,firstNumbers)); % returns the value of a1.b4.c2

result = nan([size(firstElement) size(Numbers{1}]);
for ii = 1:numel(Numbers{1})
    iiNumbers = cellfun(@(n) n(ii), Numbers);
    result(:,:,ii) = eval(sprintf(evalPattern,iiNumbers));
end

好的,花费的时间比我预期的要长,但在满足以下要求的情况下,以下代码应该适用于任意数量的姓名和号码:

  • 目前,名称被认为是单个字符 - 可以通过 regexp 或类似的方式进行调整。
  • 您的所有 ax.bx.cx.... 都可以由您的应用程序(预先)存储在某个上级结构中。
  • 您的结构始终遵循 ax.bx.cx... 的呈现顺序,并且矩阵维数相等。

所以,脚本很长,而且 - 恐怕 - 需要一些解释。请只是问。基本思想是循环遍历结构,只要特定的 "children" 仍然是结构。这确保了任意 "depth" 结构,即名称和数字的数量。

我扩展了你的数据,所以你看,它也适用于 (a) 额外的名字,(b) 额外的数字,和 (c) 不同的矩阵大小。当然,它也适用于您的原始数据。

此外,一开始不需要 NamesNumbers,因为这些信息是从(必须有上级的)结构中自动提取的。

(注意:用 Octave 编写。我试图验证所有功能在 Matlab 中也可用。如果不是这种情况,请报告任何问题。然后我将重构代码。)

% Structs given.
a1.b4.c2.d3 = ones(4, 4);
a1.b4.c4.d3 = ones(4, 4) * 2;
a1.b4.c5.d3 = ones(4, 4) * 3;
a1.b6.c2.d3 = ones(4, 4) * 4;
a1.b6.c4.d3 = ones(4, 4) * 5;
a1.b6.c5.d3 = ones(4, 4) * 6;
a2.b4.c2.d3 = ones(4, 4) * 7;
a2.b4.c4.d3 = ones(4, 4) * 8;
a2.b4.c5.d3 = ones(4, 4) * 9;
a2.b6.c2.d3 = ones(4, 4) * 10;
a2.b6.c4.d3 = ones(4, 4) * 11;
a2.b6.c5.d3 = ones(4, 4) * 12;

% REQUIREMENT: Store your structs in some superordinated struct.
super.a1 = a1;
super.a2 = a2;

% Initialize combined struct for names and numbers.
NamesNumbers = struct();

% Initialize Names cell array.
Names = {};

% Extract names and numbers from superordinated struct.
totalNames = 0;
totalNumbers = 1;
current = super;
while (isstruct(current))
  fields = fieldnames(current);
  totalNames = totalNames + 1;
  totalNumbers = totalNumbers * numel(fields);
  for iField = 1:numel(fields)
    field = fields{iField};
    name = field(1);
    Names{totalNames} = name;
    number = field(2:end);
    if (isfield(NamesNumbers, name) == false)
      NamesNumbers.(name) = str2num(number);
    else
      NamesNumbers.(name) = [NamesNumbers.(name) str2num(number)];
    end
  end
  current = current.(fields{1});
  if (isstruct(current) == false)
    [nRows, nCols] = size(current);
  end
end

% Extract all values from superordinated struct.
level = struct2cell(super);
while (isstruct([level{:}]))
  level = struct2cell([level{:}]);
end
values = vertcat(level{:});

% Determine indices.
maxIdx = cellfun(@(x) max(x), struct2cell(NamesNumbers));
idx = zeros([totalNumbers, totalNames]);
factorProd = 1;
for iName = 1:totalNames
  numbers = NamesNumbers.(Names{iName});
  n = numel(numbers);
  factorProd = factorProd * n;
  inner = totalNumbers / factorProd;
  resh = totalNumbers * n / factorProd;
  outer = factorProd / n;
  column = repmat(reshape(repmat(numbers, inner, 1), resh, 1), outer, 1);
  START = (iName - 1) * totalNumbers + 1;
  STOP = iName * totalNumbers;
  idx(START:STOP) = column;
end

% Initialize output.
output = zeros([nRows nCols maxIdx']);

% Fill output with values.
for iIdx = 1:size(idx, 1)
  temp = num2cell(idx(iIdx, :));
  START = (iIdx - 1) * nRows + 1;
  STOP = iIdx * nRows;
  output(:, :, temp{:}) = values(START:STOP, :);
end