Я ищу элегантное средство извлечения вложенных данных из структуры данных MATLAB.

Используя 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);

person user3716890    schedule 07.06.2014    source источник
comment
Какое это имеет отношение к защитному программированию? Почему он помечен как таковой?   -  person Disillusioned    schedule 15.06.2014


Ответы (2)


MATLAB лучше всего работает с массивами/матрицами (будь то числовые массивы, массивы структур, массивы ячеек, массивы объектов и т. д.). Язык предлагает конструкции для простого разделения и индексации массивов.

Таким образом, идиоматическим способом в MATLAB было бы создание массив нескалярных структур, в отличие от глубоко вложенная структура.

Например, давайте сначала преобразуем вложенную структуру в двумерный массив структур, где первое измерение обозначает «реплицы», а второе измерение обозначает «наборы»:

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

Преимущество этой новой структуры в том, что к ней можно легко получить доступ, используя нотацию индексирования массива и списки, разделенные запятыми. Таким образом, мы можем извлечь векторы X и Y, как вы, просто следующим образом:

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

Итак, если бы вы могли контролировать построение структуры, я бы спроектировал ее, как описано выше, для начала. В противном случае двойной цикл 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.

person Amro    schedule 07.06.2014
comment
вот связанный пост, который может представлять интерес: stackoverflow.com/a/4169216/97160 - person Amro; 07.06.2014
comment
Смотрите мой комментарий в верхней части моего ответа... :-) - person Carl Witthoft; 09.08.2016

Поскольку нам часто «дают» глубоко вложенные структуры из источников, которые мы не можем контролировать (другие бизнес-подразделения, клиенты и т. д.), иногда ребенок должен делать то, что должен делать ребенок. Вот хак, который, кажется, работает, чтобы полностью сгладить вложенную структуру. Также опубликовано в здесь на случай, если один из этих вопросов будет удален. . Авторское право 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
person Carl Witthoft    schedule 09.08.2016
comment
это не совсем отвечает на вопрос выше, но все же может быть полезно, если кто-то хочет сгладить такие вложенные структуры... Однако ваш код не очень эффективен, плюс он использует eval тьфу! Вот лучшая реализация: pastebin.com/Dr8Kh3n7. Применив его на примере выше d = struct2sims(data), вы получите совершенно плоскую структуру с полями вида data_sets_n1_replicates_n1_X, ..., data_sets_n100_replicates_n10_Y - person Amro; 10.08.2016
comment
Предостережение, MATLAB имеет максимальную длину имен переменных, равную 63 , поэтому глубоко вложенные структуры могут быть усечены при сглаживании таким образом... Возможно, вы также можете добавить параметр для указания максимальной глубины, чтобы ограничить уровень рекурсии (чтобы пользователь указывал максимальный уровень глубины, после которого входные данные структуры возвращаются как- без их выравнивания). Например, с depth=4 на выходе будут поля data_sets_n?_replicates_n?, каждое из которых представляет собой неглубокую структуру только с X и Y. - person Amro; 10.08.2016
comment
@Amro спасибо за обновления и предупреждения. Я постараюсь мотивировать :-) реализовать опцию «максимальная глубина» и некоторые проверки длины имени. Как оказалось, этот код изначально был написан для внутреннего использования, когда наши источники данных никогда не превышают трех глубин. - person Carl Witthoft; 10.08.2016
comment
должно быть легко реализовать опцию глубины. Идея состоит в том, чтобы добавить третий аргумент 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;. - person Amro; 10.08.2016
comment
@Amro Я думаю, я бы сделал это по-другому: struct2sims(s_in,name, Max), затем рекурсивно вызывал с помощью Max-1 и останавливался, когда он достигал нуля. - person Carl Witthoft; 10.08.2016