wsdzbm
wsdzbm

Reputation: 3670

average bins along a dimension of a nd array in matlab

To compute the mean of every bins along a dimension of a nd array in matlab, for example, average every 10 elements along dim 4 of a 4d array

x = reshape(1:30*30*20*300,30,30,20,300);    
n = 10; 
m = size(x,4)/10;
y = nan(30,30,20,m);
for ii = 1 : m
    y(:,:,:,ii) = mean(x(:,:,:,(1:n)+(ii-1)*n),4);
end

It looks a bit silly. I think there must be better ways to average the bins?

Besides, is it possible to make the script applicable to general cases, namely, arbitray ndims of array and along an arbitray dim to average?

Upvotes: 1

Views: 73

Answers (2)

beesleep
beesleep

Reputation: 1362

Here is a generic solution, using the accumarray function. I haven't tested how fast it is. There might be some room for improvement though.

Basically, accumarray groups the value in x following a matrix of customized index for your question

x = reshape(1:30*30*20*300,30,30,20,300); 
s = size(x);

% parameters for averaging
dimAv = 4; 
n = 10;

% get linear index
ix = (1:numel(x))';

% transform them to a matrix of index per dimension
% this is a customized version of ind2sub
pcum = [1 cumprod(s(1:end-1))];
sub = zeros(numel(ix),numel(s));
for i = numel(s):-1:1,
    ixtmp = rem(ix-1, pcum(i)) + 1;
    sub(:,i) = (ix - ixtmp)/pcum(i) + 1;
    ix = ixtmp;
end

% correct index for the given dimension
sub(:,dimAv) = floor((sub(:,dimAv)-1)/n)+1;

% run the accumarray to compute the average
sout = s;
sout(dimAv) = ceil(sout(dimAv)/n);
y = accumarray(sub,x(:), sout, @mean);

If you need a faster and memory efficient operation, you'll have to write your own mex function. It shouldn't be so difficult, I think !

Upvotes: 1

rahnema1
rahnema1

Reputation: 15867

For the second part of your question you can use this:

x = reshape(1:30*30*20*300,30,30,20,300);    
dim = 4;
n = 10; 
m = size(x,dim)/10;
y = nan(30,30,20,m);
idx1 = repmat({':'},1,ndims(x));
idx2 = repmat({':'},1,ndims(x));
for ii = 1 : m
    idx1{dim} = ii;
    idx2{dim} = (1:n)+(ii-1)*n;
    y(idx1{:}) = mean(x(idx2{:}),dim);
end

For the first part of the question here is an alternative using cumsum and diff, but it may not be better then the loop solution:

function y = slicedmean(x,slice_size,dim)
    s = cumsum(x,dim);
    idx1 = repmat({':'},1,ndims(x));
    idx2 = repmat({':'},1,ndims(x));
    idx1{dim} = slice_size;
    idx2{dim} = slice_size:slice_size:size(x,dim);
    y = cat(dim,s(idx1{:}),diff(s(idx2{:}),[],dim))/slice_size;
end

Upvotes: 1

Related Questions