【问题标题】:I am looking for an elegant means of extracting nested data from a MATLAB data structure我正在寻找一种从 MATLAB 数据结构中提取嵌套数据的优雅方法
【发布时间】:2014-06-07 03:23:51
【问题描述】:

使用 MATLAB,除了使用嵌套 FOR 循环的蛮力技术之外,我很好奇是否有更优雅的方法从我在下面显示的示例数据结构中提取 X 和 Y 数据。我无法在 MATLAB 中使用 bsxfun、arrayfun 或 strucfun 设计出一种优雅的方式来执行此操作。

% Create an example of the input structure that I need to parse
for i =1:100
    setName = ['n' num2str(i)];
    for j = 1:randi(10,1)
        repName = ['n' num2str(j)];
        data.sets.(setName).replicates.(repName).X = i + randn();
        data.sets.(setName).replicates.(repName).Y = i + randn();
    end
end

clearvars -except data

% Brute force technique using nested FOR Loops to extract X & Y from this
% nested structure for easy plotting. Is there a better way to extract the
% X & Y values created above without using FOR loops?

n = 1;
setNames = fieldnames(data.sets);
for i =1:length(setNames)
    replicateNames = fieldnames(data.sets.(setNames{i}).replicates);
    for j = 1:length(replicateNames)
        X(n) = data.sets.(setNames{i}).replicates.(replicateNames{j}).X;
        Y(n) = data.sets.(setNames{i}).replicates.(replicateNames{j}).Y;
        n = n+1;
    end
end

scatter(X,Y);

【问题讨论】:

  • 这与“防御性编程”有什么关系?为什么它被标记为这样?

标签: arrays matlab struct nested-lists


【解决方案1】:

MATLAB 最适用于数组/矩阵(无论是数值数组、结构数组、元胞数组、对象数组等)。该语言提供了可以轻松地对数组进行切片和索引的构造。

因此,MATLAB 中的惯用方式是创建 non-scalar structure array,而不是 deeply nested structure

例如,让我们首先将嵌套结构转换为结构的二维数组,其中第一个维度表示“复制”,第二个维度表示“集合”:

ds = struct('X',[], 'Y',[]);
sets = fieldnames(data.sets);
for i=1:numel(sets)
    reps = fieldnames(data.sets.(sets{i}).replicates);
    for j=1:numel(reps)
        ds(j,i) = data.sets.(sets{i}).replicates.(reps{j});
    end
end

结果是一个 10×100 结构体数组,每个数组有两个字段 X 和 Y:

>> ds
ds = 
10x100 struct array with fields:
    X
    Y

在原始结构中访问data.sets.n99.replicates.n9 将等效于在新结构中访问ds(9,99)

>> data.sets.n99.replicates.n9
ans = 
    X: 100.3616
    Y: 98.8023

>> ds(9,99)
ans = 
    X: 100.3616
    Y: 98.8023

这个新结构的好处是可以使用数组索引符号和comma-separated lists 轻松访问它。所以我们可以像你做的那样提取 X 和 Y 向量:

XX = [ds.X];    % or XX = cat(2, ds.X)
YY = [ds.Y];
scatter(XX, YY, 1)

因此,如果您可以控制构建结构,我会按照上面描述的方式设计它。否则,代码中带有 dynamic field names 的双 for 循环是从中提取值的最佳方法。


您可能会编写一堆相互调用的structfun,但这不是最易读的代码。这是我想出的扁平化嵌套结构的方法:

D = structfun(@(n) ...
        structfun(@(nn) [nn.X nn.Y], n.replicates, 'UniformOutput',false), ...
        data.sets, 'UniformOutput',false);

可以使用更少的嵌套字段访问生成的结构:

>> D.n99.n9
ans =
  100.3616
   98.8023

比原来的稍微好一点,但如果没有一些 for 循环,仍然不容易遍历。

【讨论】:

【解决方案2】:

由于我们经常从我们无法控制的来源(其他业务部门、客户等)“给定”深度嵌套的结构,因此有时婴儿必须做婴儿必须做的事情。这是一个似乎可以完全压平嵌套结构的技巧。也发布到here,以防这些问题之一被删除。 .根据通常的 GPL-3 规则版权 Carl Witthoft。

%  struct2sims converter
function simout = struct2sims(structin)
fnam = fieldnames(structin);
for jf = 1:numel(fnam)
    subnam = [inputname(1),'_',fnam{jf}];
    if isstruct(structin.(fnam{jf}) ) ,
    % need to dive;  build a new variable that's not a substruct
     eval(sprintf('%s = structin.(fnam{jf});', fnam{jf}));
    eval(sprintf('simtmp = struct2sims(%s);',fnam{jf}) );
    % try removing the struct before getting any farther...
    simout.(subnam) = simtmp;
    else
    % at bottom, ok
    simout.(subnam) = structin.(fnam{jf});
    end

end
 %  need to unpack structs here, after each level of recursion
 % returns...
    subfnam = fieldnames(simout);
    for kf = 1:numel(subfnam)
         if isstruct(simout.(subfnam{kf}) ),  
             subsubnam = fieldnames(simout.(subfnam{kf}));
             for fk = 1:numel(subsubnam)
                 simout.([inputname(1),'_',subsubnam{fk}])...
                     = simout.(subfnam{kf}).(subsubnam{fk}) ;
             end
             simout = rmfield(simout,subfnam{kf});
         end
    end
 % if desired write to file with:
 % save('flattened','-struct','simout');
end

【讨论】:

  • 这并不能完全解决上面的问题,如果有人想像这样扁平化嵌套结构,它仍然可能很有用......但是您的代码效率不高,而且它使用eval ugh!这是一个更好的实现:pastebin.com/Dr8Kh3n7。将它应用到上面的例子d = struct2sims(data),你会得到一个完全扁平的结构,其中的字段形式为data_sets_n1_replicates_n1_X, ..., data_sets_n100_replicates_n10_Y
  • 请注意,MATLAB 的 maximum length for variables names 为 63,因此深度嵌套的结构在以这种方式展平时可能会被截断...也许您还可以添加一个选项来指定最大深度以限制递归级别(因此用户指定一个最大深度级别,之后结构输入按原样返回而不展平它们)。例如,depth=4 的输出将是字段data_sets_n?_replicates_n?,每个字段都是一个浅结构,只有XY
  • @Amro 感谢您的升级和警告。我会尝试激励 :-) 来实现“最大深度”选项和一些名称长度检查。碰巧的是,这段代码最初是为内部使用而编写的,我们的数据源永远不会超过三个深度。
  • 应该很容易实现深度选项。这个想法是添加第三个参数function s_out = struct2sims(s_in, name, depth),它默认为0 if nargin < 3, depth = 0; end,你可以在递归调用中增加它,比如s_tmp = struct2sims(val, subname, depth+1);。然后你可以测试这个当前深度是否小于指定的限制,如果超过它,你会停止递归并按原样返回输入结构s_out = struct(name,s_in); return;
  • @Amro 我想我会换一种方式:struct2sims(s_in,name, Max),然后递归调用 Max-1 并在达到零时停止。
猜你喜欢
  • 1970-01-01
  • 2020-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-30
  • 1970-01-01
  • 2015-05-14
  • 1970-01-01
相关资源
最近更新 更多