Clear Filters
Clear Filters

Averaging every nth element ignoring zeros into a matrix

3 views (last 30 days)
Dear all,
I am trying to analyse a dataset and struggling with a probably quite simple question and so far the research in the forum hasn't helped me much. I have a m*n matrix and want to "bin" (I know its the wrong word for it, but for lack of a better word..) it into a 10x10 matrix, thereby calculating the average of the values in each "bin". I have coordinates for cells with a specific activation value, which I have transformed into a matrix with the coordinates as row/column specs and the value as the cell-value. Now I want to create a heat map to visualize if there are specific areas with a higher activity than others. Since there are not always cells at all coordinates, the 0 in the matrix are not to be used for further calculations.
I have successfully used a very neat approach that a user posted in this forum a long time ago for summing up the values into a bin, which was perfect for my previous analysis, but now I need to average instead of summing. The code I have previously used ( Source ) would look like this:
IND = [0 0 0 0 0 0; 0 10 15 0 0 0; 0 0 0 0 0 7; 0 0 0 0 0 8];
[n,m] = size(IND);
ind = (1:n)';
IND2 = sparse(ceil(ind/2),ind,1)*IND;
ind = (1:m)';
IND3 = IND2*sparse(ind,ceil(ind/3),1);
Summing in 2 directions to end up with a matrix that is 2x2. Is there an easy way that I am missing to transform this code into averaging instead of summing? (I stumbled upon reshaping methods but couldn't quite get them to work to sum up in both directions) For the few attempts where I could average in one of the desired directions, I realized that it would calculate the average with all elements, including the zeros. Is there a way of excluding these in the calculation but maintain the order of the matrix? (if I use mean(IND(IND~=0)), it will give me the result as the first cell instead of the "bin" where the data is from). I would be very happy for help in any kind of way, thanks so much!

Answers (2)

Walter Roberson
Walter Roberson on 26 Sep 2016
Edited: Walter Roberson on 26 Sep 2016
See accumarray(), which allows you to specify a custom function. You can specify @mean . For example,
index = randi(50, 20, 1);
data = rand(20, 1);
accumarray(index, data, [], @mean)
  2 Comments
ghastliness
ghastliness on 27 Sep 2016
Edited: ghastliness on 27 Sep 2016
Thank you for the idea, in my exemplary data I managed to get it to work using accumarray and the nanmean function, however, since my matrix-size is not always divisible by 10, I couldn't get the accumarray to work. However, I managed using the blockproc function and replacing all zeros with NaN in my matrix:
heatmap = blockproc(IND, [size(IND,1)/n, size(IND,2)/n], @(theBlockStructure) nanmean(theBlockStructure.data(:)));
If there are easier and better ways I would still be happy to learn more about this problem, since this does take a bit to sum all of my image data. :-)
Walter Roberson
Walter Roberson on 27 Sep 2016
Transform your non-integer variables into integer values.
range_min = 0; range_max = 1;
number_of_bins = 10;
x = rand(1, 20) * (range_max - range_min) + range_min;
x_as_index = floor( double(x(:) - range_min)./(range_max - range_min)./(1+eps) * number_of_bins ) + 1;
y = rand(20, 1);
counts = accumarray(index, data, [], @nanmean);
The 1+eps divisor is to account for the possibility that some data is exactly equal to the upper end of the range, but you might want to get rid of it there depending on how you think about the values that are exactly on the bin boundaries. For example if you have values in the range 0 to 1 and you have 2 bins, then it is easy to say "Oh yes, make 1/2 the dividing point between the bins", but do you make the rule 0 < x <= 1/2 and 1/2 < x <= 1, or do you make the rule 0 <= x < 1/2, 1/2 <= x < 1, or do you make the rule 0 <= x < 1/2, 1/2 <= x <= 1 ? In the first of those, the value 1 exactly gets left out of the rule; in the second of those, the value 0 exactly gets left out of the rule; in the third of those, the second bin is larger than the first by one value. In binary floating point spaced equally apart at 1/2^53, then 0 <= x <= 1 involves an odd number of values so there is no one "right" answer.

Sign in to comment.


ghastliness
ghastliness on 27 Sep 2016
Edited: ghastliness on 27 Sep 2016
As an add-on, I found a nice summary for summarizing/averaging across several rows resp. columns including a speed test for all suggested solutions: http://stackoverflow.com/questions/9576643/how-should-i-average-groups-of-rows-in-a-matrix-to-produce-a-new-smaller-matrix altough most of them only work if the number of rows is divisible by the number of rows of the new matrix.

Categories

Find more on Creating and Concatenating Matrices in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!