简体   繁体   中英

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?

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

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 !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM