How to get average values ​​of specific regions in each slice in 3d matrix using MATLAB boolean indexing

In Matlab,

If I have a 3D matrix like below, I want to know the average of the areas with values ​​greater than 5 in each slice. How can I use a boolean pointer to do this, without any loops?

I would like to get a 3-by-1 array, with each element indicating the mean of the regions in their respective slice.

m3d = randi(10,[3,3,3])

m3d (:,:, 1) =

 7     7     8
 1     9     8
 9     7     6

      

m3d (:,:, 2) =

10    10     5
 9     7     8
 5     3     3

      

m3d (:,:, 3) =

 9     7     5
 4     1     9
 5     9     1

      

Getting the index

3d_index = m3d > 5;

      

My last

result = mean(m3d(3d_index));

      

in which I do not want to have an average for all regions

+3


source to share


3 answers


One approach -

%// 3d mask of elements greater than 5
mask = m3d>5

%// Sum of all elements greater than 5 in each slice
sumvals = sum(reshape(m3d.*mask,[],size(m3d,3)))

%// Count of elements great than 5 in each slice
counts = sum(reshape(mask,[],size(m3d,3)))

%// Final output of mean values for the regions with >5 only
out = sumvals./counts

      

Benchmarking

Here are some runtime tests to see where all the posted approaches are. For tests, we took a random 3D array 1500 x 1500 x 100

with values ​​in the range [1,255]

. Following is the comparative code -



m3d = randi(255,1500,1500,100); %// Input 3D array

%// Warm up tic/toc.
for k = 1:50000
    tic(); elapsed = toc();
end

disp('------------------------ With SUMMING and COUNTING ')
tic
%// .... Proposed approach in this solution
toc, clear out counts sumvals mask

disp('------------------------ With FOR-LOOP ')
tic
N   = size(m3d, 3);
out = zeros(N, 1);
for k = 1:size(m3d,3)
        val    = m3d(:,:,k);
        lix    = val>5;
        out(k) = mean(val(lix));
end;
toc, clear out lix val k N

disp('----------------------- With ACCUMARRAY')
tic
ind = m3d>5;
result = accumarray(ceil(find(ind)/size(m3d,1)/size(m3d,2)), m3d(ind), [], @mean);
toc, clear ind result

disp('----------------------- With NANMEAN')
tic
m3d(m3d<5) = NaN; %// Please note: This is a bad practice to change input
out = nanmean(nanmean(m3d,1),2);
toc

      

Runtimes

------------------------ With SUMMING and COUNTING 
Elapsed time is 0.904139 seconds.
------------------------ With FOR-LOOP 
Elapsed time is 2.321151 seconds.
----------------------- With ACCUMARRAY
Elapsed time is 4.350005 seconds.
----------------------- With NANMEAN
Elapsed time is 1.827613 seconds.

      

+2


source


You can use an array of masks to do it like this:



m3d = randi(10,[3,3,3]);

%Set values less than 5 to NaN
m3d(m3d<5) = NaN
nanmean(nanmean(m3d,1),2)

      

+1


source


This can be done very easily with accumarray

:

ind = m3d>5;
result = accumarray(ceil(find(ind)/size(m3d,1)/size(m3d,2)), m3d(ind), [], @mean);

      

0


source







All Articles