Clear Filters
Clear Filters

how to access cells of n-dimensional array without colon operator?

1 view (last 30 days)
I need to develop a function that can accept any n-dimensional arrays. It should access cells of the arrays in the following way:
mat(1:end-1, :, :, ... :);
mat( :, 1:end-1, :, ... :);
mat( :, :, 1:end-1, ... :);
mat( :, :, :, ... 1:end-1);
And also by other similar combinations for more then one dimension, e.g.:
mat(1:end-1, :, 1:end-1, ... :);
How can it be done in a smarter way, so I do not need to consider all cases manually, and it can be universal for any number of dimensions?
I was looking for something as:
new_mat = function(mat, (vector with dimensions to be used), (vectors used to access cells, e.g., 1:end-1));
For example it could be:
new_mat = function(mat, [1 3], [1:end-1; 2:end]);
which is equivalent to:
new_mat = mat(1:end-1, :, 1:end-1, ... :);
GREAT THANKS!

Accepted Answer

Sean de Wolski
Sean de Wolski on 17 Sep 2013
Edited: Sean de Wolski on 17 Sep 2013
That was a fun post-lunch challenge, thanks!
extractFromND(magic(3),[1 2],[2 3;3 3])
It does not accept end you have to pass in the calculation yourself, e.g.
size(x,4)-1 %equivalen to end-1 in dim 4
The function
function Mextract = extractFromND(M,dim,datarange)
% EXTRACTFROMND extract data range in dim from M
%
%Usage:
% -Mextract = extractFromND(M,dim,datarange);
%
%Inputs:
% -M: nd matrix
% -dim: dimensions that the rows of datarange use
% -datarange: [dim x 2] where each row is the range in low/high in dim
%
%Outputs:
% -MExtract: M not including data outside of datarange in dim of M
%
%
%Examples:
%
% x = ones(3,3,3,3,3);
% x2 = extractFromND(x,[1 5],[2 3;1 2]);
% size(x2);
%
%See Also: colon
%
%
%TODO: make datarange nx3 with middle column the stride
%
%
% Copyright 2013 The MathWorks, Inc.
% SCd 735494
%
%Rudimentary error checking
assert(nargin==3,'Exactly three inputs expected');
ndM = ndims(M);
assert(max(dim)<=ndM,'Can''t pull from more dimensons than we have');
ned = numel(dim); %numel extract dimensions
assert(size(datarange,1)==ned,'datarange should have the same number of rows as dim');
szM = size(M);
assert(all(szM(dim).'>=datarange(:,2)),'out of range datarange');
%Engine:
%Build index vector
idx = repmat({':'},1,ndM);
for ii = 1:ned
idx{dim(ii)} = datarange(ii,1):datarange(ii,2);
end
%Extract from M:
Mextract = M(idx{:});
end

More Answers (4)

Jim Hokanson
Jim Hokanson on 17 Sep 2013
Unfortunately I don't know of any way of doing 'end' as a variable, but the basic gist is something like the following:
indices = cell(1,ndims(mat));
indices(:) = {':'} %This is a bit of magic ...
%At this point you have specified you want all values from all dimensions
%Now limit the variables obtained in specific dimensions
dims_change = [1 3];
grab_range = [1 1; 2 0]; %i.e. now mat(1:end-1,:,2:end-0,... etc)
for iDim = 1:length(dims_change)
cur_dim = dims_change(iDim);
cur_start = grab_range(iDim,1);
samples_remove_end = grab_range(iDim,2);
dim_size = size(mat,cur_dim);
indices(cur_dim) = {cur_start:(dim_size-samples_remove_end)};
end
%Now, use cell array expansion where each element gets treated as a different input
new_mat = mat(indices{:});

Matt J
Matt J on 17 Sep 2013
Edited: Matt J on 18 Sep 2013
Here is an object-oriented approach (see the classdef below). It might be less intuitive than what you had in mind. However, it lets you use indexing expressions with all normal flexibility (including "end"), but omitting the colons as you wished. It also avoids the drawbacks of my earlier Answer, which required EVAL.
Example:
Given A=rand(5,4,3,6), the following
obj=myclass(A);
B=obj{[1,4]}(2:end-1,3:end-2);
is equivalent to B=A(2:end-1,:,:,3:end-2).
classdef myclass
properties
data;
subset;
end
methods
function obj=myclass(A,subset)
if nargin<2, subset=1:ndims(A); end
obj.data=A;
obj.subset=subset;
end
function out=end(obj,k,~)
out=size(obj.data,obj.subset(k));
end
function B=subsref(obj,S)
if isequal(S(1).type,'{}')
obj.subset=S(1).subs{1};
S(1)=[];
end
if isempty(S), B=obj; return; end
nn=max([ndims(obj.data),obj.subset]);
idx=repmat({':'},1,nn);
idx(obj.subset)=S.subs;
B=obj.data(idx{:});
end
function N=numel(obj,varargin)
N=1;
end
end
end

Matt J
Matt J on 17 Sep 2013
Edited: Matt J on 17 Sep 2013
function B=myaccess(A,subset,varargin)
%Example:
%
% B=myaccess(A,[1,2],'2:end-1','3'),
%
%is equivalent to B=A(2:end-1,3,:)
m=length(subset);
n=ndims(A);
sz=size(A);
idx=repmat({':'},1,n);
for ii=1:m
jj=subset(ii);
r=1:sz(jj);
idx{jj}=eval(['r(' varargin{ii} ')']);
end
B=A(idx{:});
  3 Comments
Sean de Wolski
Sean de Wolski on 17 Sep 2013
If I was going to use this, I would say that the eval is not worth it, pass in the size(x,dim)-x calculation.
Matt J
Matt J on 17 Sep 2013
Not sure what "size(x,dim)-x" corresponds too. The result of that expression would be an array of size(x).
I think you mean you want to resolve all "end" expressions prior to the function call using size(x,dim), but this would require a loop over dim that is almost as much code as the functions we've all proposed.
Also, both you and Jim proposed a solution that would work for contiguous ranges a:b, but note that the OP never specified that the ranges to be indexed were contiguous. A modification of my code that might be more appealing is
for ii=1:m
jj=subset(ii);
r=1:sz(jj);
if ischar(varargin{ii}) %support "end" expressions
idx{jj}=eval(['r(' varargin{ii} ')']);
elseif isnumeric(varargin{ii})
idx{jj}=varargin{ii};
end
end

Sign in to comment.


Tomasz
Tomasz on 18 Sep 2013
Edited: Tomasz on 18 Sep 2013
Thank you for extremely fast response and good solutions! I have used this forum for the first time, and I am very positively surprised. Tomasz

Categories

Find more on Operators and Elementary Operations in Help Center and File Exchange

Products

Community Treasure Hunt

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

Start Hunting!