我正在寻找一种从MATLAB数据结构中提取嵌套数据的优雅方法

时间:2014-06-07 03:23:51

标签: arrays matlab struct nested-lists

使用MATLAB,除了使用嵌套FOR循环的强力技术之外,我很好奇是否有更优雅的方法来提取X&来自我在下面显示的示例数据结构的Y数据。我无法使用bsxfun,arrayfun或strucfun在MATLAB中设计一种优雅的方法。

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

2 个答案:

答案 0 :(得分:1)

MATLAB最适用于数组/矩阵(无论是数值数组,结构数组,单元数组,对象数组等)。该语言提供了轻松切片和索引到数组的构造。

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

例如,让我们首先将嵌套结构转换为2D结构数组,其中第一个维度表示"复制",第二个维度表示"设置":

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循环仍然不能轻易遍历。

答案 1 :(得分:-1)

因为我们经常被给予"来自我们无法控制的来源(其他业务部门,客户等)的深层嵌套结构,有时婴儿必须做婴儿必须做的事情。这是一个似乎可以完全展平嵌套结构的黑客攻击。如果其中一个问题被删除,也会发布到here。 。版权所有Carl Witthoft遵循通常的GPL-3规则。

%  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