【问题标题】:summing fields in struct arrays that have the same inner structure [closed]对具有相同内部结构的结构数组中的字段求和
【发布时间】:2021-12-31 07:27:08
【问题描述】:

我有很多 mat 文件,每个文件都包含一个结构数组s,它具有相同的内部结构。这是您从加载单个文件中获得的s 结构数组之一的最小示例:

s(1).A.a=rand(3);
s(1).A.b=rand(4);
s(1).B  =1;

s(2).A.a=rand(3);
s(2).A.b=rand(4);
s(2).B  =10;

在实践中,结构数组有 100 个元素,以及数十个字段和子字段。请不要评论按原样保存文件的选择。它不在我的控制范围内,这里的问题是关于如何处理这些文件中的信息。

我最终希望对这些结构数组的每个子字段的所有信息进行平均,因此逻辑步骤是将它们相加(然后除以文件数)。

我目前的解决方案是这样的:

% initialize arrays of the same inner structure as `s`      

 sum_s_A_a=zeros(size(s(1).A.a,1),size(s(1).A.a,2),numel(s));
 sum_s_A_b=zeros(size(s(1).A.b,1),size(s(1).A.b,2),numel(s));
 sum_s_B=zeros(1,numel(s));

 for jj=1:100 % loop over all 100 files (just for the example) 
   
       %  load here each file that contains s
    
      for ii=1:numel(s) ; % loop each element in s and add it to sum_s
        sum_s_A_a(:,:,ii) = sum_s_A_a(:,:,ii)  + s(ii).A.a;
        sum_s_A_b(:,:,ii) = sum_s_A_b(:,:,ii)  + s(ii).A.b;
        sum_s_B(ii) =  sum_s_B(ii) + s(ii).B;
      end

  end

这非常不实用,因为s 中有几十个字段和子字段,但是如果您使用上面定义的s,上面的最小示例适用于“单个文件”情况

我想以与上述 for 循环类似的方式对所有这些文件的信息求和,但不写下所有字段和子字段的名称并将其硬编码为数组名称,并且如果可能的话不for 循环。

我不介意信息的最终容器是结构体、单元格还是数组。

【问题讨论】:

  • 结果的类型是什么?结构体还是数值数组?
  • 警告您正在使用动态变量名称,即bad, very bad。不要调用你的变量s1, s2, s3 等。如果可能的话,回到你创建所有这些变量的地方(一开始可能是一团糟)并重新考虑你的方法。可能一个取代的结构或单元会更可取。然后,您可以只对其进行索引,而不是试图深入研究动态变量命名的兔子洞。
  • 我其实不是,这只是我给出的例子。实际上,我正在阅读具有相同名称 s 的 mat 文件,我只想以问题中描述的方式汇总所有 100 个 mat 文件。
  • 我也不明白为什么这个问题被关闭了。有一个最小的例子,细节很清楚。问题是关于对具有相同内部结构的结构中的字段求和。
  • 我编辑了这个问题,最小的例子有效,我尽可能地简化和澄清了这个问题。

标签: matlab struct vectorization


【解决方案1】:

从您的示例开始,以便对所有 A 字段求和并在此处返回数字数组是一种不使用循环的方法:

function result = sumstruct (varargin)
  v = [varargin{:}];
  s = [v.A];
  result = sum([s.tot1], 2);
end

并将其称为:

result = sumstruct (s1, s2, s3);

编辑:

但是,如果您还想对其他字段及其子字段求和并将它们组合成一个结构,您需要使用循环或cellfun。这是一个递归减少嵌套结构的解决方案:

function result = reduce(fcn, varargin)
  fcns0 = {@(x)cat(3, x{:}), @(x)x};
  switcher0 = @(tf, s)fcns0{tf+1}(s);
  fcns = {@(s)fcn(s), @(s)reduce(fcn, s{:})};
  switcher = @(tf, s)fcns{tf+1}(s);
  c = cellfun(@(x){struct2cell(x)}, varargin);
  s0 = cat(3, c{:});
  s1 = reshape(s0, [], numel(varargin));
  s2 = cellfun(@(x){switcher0(isstruct(x{1}), x)}, num2cell(s1, 2));
  s3 = reshape(s2, size(c{1}));
  s4 = cellfun(@(c){switcher(iscell(c), c)}, s3);
  fnames = fieldnames(varargin{1});
  result = cell2struct(s4, fnames, 1);
end

第一个参数是用于归约的函数句柄,其余参数是结构数组。

使用循环加载所有文件并使用reduce:

c = cell (1, 100);
for i = 1:100
  c{i} = load('file');
end
result = reduce(@(x)sum(x, 3), c{:});
result = reduce(@(x)x ./ 100, result);

您也可以增量加载文件并执行reduce:

result = [];
for i = 1:100
  s = load('file');
  if i == 1
    result = s;
  else
    result = reduce(@(x)sum(x, 3), result, s);
  end
end  
result = reduce(@(x)x ./ 100, result);

注意这里的归约函数应该沿着数组的第三维执行,因此它被写为sum(x, 3)

【讨论】:

  • 谢谢你的回答,不幸的是它对我不起作用,因为它假定了一个特定的字段或子字段结构。我编辑了问题并添加了一个最小的示例,不幸的是您的代码不起作用。
  • 使用最小示例和您的答案如下:c={s,s}; result = sumstruct (c{:}); 产生错误(我的脚本是 structsum_script):Error using fieldnames Invalid input argument of type 'double'. Input must be a structure or a Java or COM object. Error in structsum_script>sumfields (line 48) fnames = fieldnames(s); Error in structsum_script>@(f){sumfields(v,f)} (line 42) c = cellfun(@(f){sumfields(v, f)}, fnames); Error in structsum_script>sumstruct (line 42) c = cellfun(@(f){sumfields(v, f)}, fnames); Error in structsum_script (line 37) result = sumstruct (c{:});
  • 我编辑了答案,我认为它应该适用于更一般的情况。
  • 再次感谢您编辑的答案,我非常感谢您的帮助,但我担心您错过了问题的要点之一,例如,在您的回答中,如果我有一个案例与我的问题中相同的输入一个接一个c{1}=sc{2}=s 在您的回答中result.B=6 您实际上还总结了s 的元素。所需的答案就是您在我的问题中看到的sum_s_B。这是一个 2 元素数组 sum_s_B = [ 2 4]
  • 答案已更新!它现在应该根据您的说明起作用。
猜你喜欢
  • 2013-09-09
  • 1970-01-01
  • 1970-01-01
  • 2021-06-07
  • 1970-01-01
  • 2016-02-23
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多