Selecting data from a dynamically named ND array

1 view (last 30 days)
I have a structure with fields of various dimensions - typically between 3 and 6. Each of the fields has one dimension that represents a sampling instance, but in some fields it is dimension 2, in others dimension 3 or 4. I need to extract a subset of the data from each of the fields by selecting samples along the relevant dimension, so if all of the fields were, say, 4D and the sampling dimension was 2 with ix as the selection, I could use:
flds = fieldnames(S.(inst))
for i = 1:numel(flds)
fld = flds{i};
S.(inst).(fld) = S.(inst).(fld)(:,ix,:,:);
clear fld
end; clear i
I can easily check the number of dimensions of the field and I can work out which dimension of the field I need to sample along, but I then end up with something like:
flds = fieldnames(S.(inst))
for i = 1:numel(flds)
fld = flds{i};
ndims = numel(size(S.(inst).(fld));
idir = find(size(S.(inst).(fld)) == Nsamples); % where Nsamples is the number of samples
if ndims == 3
if idir == 1
S.(inst).(fld) = S.(inst).(fld)(ix,:,:);
elseif idir == 2
S.(inst).(fld) = S.(inst).(fld)(:,ix,:);
else
S.(inst).(fld) = S.(inst).(fld)(:,:,ix;
end
elseif ndims == 4
if idir == 1
S.(inst).(fld) = S.(inst).(fld)(ix,:,:,:)
elseif idir == 2
% etc
% etc
end
clear fld
end; clear i
I wondered whether anyone could suggest a more elegant solution? I was thinking that it would be possible to dynamically generate the data selection (:,ix,:,:) as a text string and use it with eval, but I'm not sure that works with the dynamic naming of variables.
Any thoughts?

Accepted Answer

Stephen23
Stephen23 on 18 May 2017
Edited: Stephen23 on 6 Nov 2018
For a start you might like to read this (if you want to write better code, then it can help you):
And then to solve your problem you do not need to write lots of if-s or use eval, in fact you can do solve your quite simply by creating a cell array of indices and using that to generate a comma-separated list:
>> X = randi(9,1,2,3,4);
>> C = {':',':',2,':'};
>> X(C{:})
ans(:,:,1,1) =
1 1
ans(:,:,1,2) =
8 2
ans(:,:,1,3) =
3 8
ans(:,:,1,4) =
2 4
Another alternative is to call subsref directly. The function subsref is what MATLAB uses to index into variables, but you can also call it directly. All you need to do is create a suitable structure input, it is not that hard but you should play around with it to understand how it works. For example:
>> subsref(X,substruct('()',C))
ans(:,:,1,1) =
1 1
ans(:,:,1,2) =
8 2
ans(:,:,1,3) =
3 8
ans(:,:,1,4) =
2 4
Clearly it is pretty trivial to construct that cell array: something like this (untested):
C = repmat({':'},1,totdim);
C{ixdim} = ix;
  2 Comments
Brian Scannell
Brian Scannell on 18 May 2017
Ah, that looks ideal. I have found that there is nearly always a way to avoid using eval - your answer proves the point!
Many thanks, Brian
Stephen23
Stephen23 on 18 May 2017
@Brian: I am glad to help, and I hope that it works for you.

Sign in to comment.

More Answers (0)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!