Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Average every N rows by column

I have four matrices that are 240x30. I need to calculate an average for every 15 rows and I need to do this by column. So, in the end I should have 30 columns with 16 values.

So, for instance:

myMatrix = randi(240,30)

And this is what I have so far:

averageBins = 15;
meanByBinsMyMatrix = arrayfun(@(i) mean(myMatrix (i:i+averageBins-1)),1:averageBins:length(myMatrix )-averageBins+1)'; % the averaged vectormean()

This seems to be working but I think it only does the job for the first column. How can I extend this to work over each column?

like image 548
Glu Avatar asked Dec 11 '22 01:12

Glu


1 Answers

You can reshape() your first dimension a to 15-by-n matrix, leaving the columns as third dimension. Then take the mean over the first dimensions, i.e. your blocks of n rows. Since this will result in a 1x16x30 matrix in your case, squeeze() out the last dimension.

n = 15;
A = rand(240,30);
B = reshape(A,[n, size(A,1)/n, size(A,2)]); %crashes for size(A,1)/n != integer
C = squeeze(mean(B,1)); % Calculate the mean over the first dimension

A short introduction to reshape() in multiple dimensions.

Create a small matrix, say magic(4), for visualisation:

A = magic(4)
A =
    16     2     3    13
     5    11    10     8
     9     7     6    12
     4    14    15     1

This is a four column matrix. Now, if we reshape it to a 2x2x4 matrix, where do our dimensions go? Let's try it:

B = reshape(A,[2,2,4])
B(:,:,1) =
    16     9
     5     4
B(:,:,2) =
     2     7
    11    14
B(:,:,3) =
     3     6
    10    15
B(:,:,4) =
    13    12
     8     1

What happened? The first column of A contains [16;5;9;4]. The first "page", which is the first entry on the third dimension, of B contains exactly these numbers!

reshape() works column-wise, meaning that it "walks" through the initial matrix going (1,1)->(2,1)->(3,1) etc, until the end of the first column is reached. Then it starts reading at (1,2)->(2,2)->(3,2) etc.
As to the storing part: MATLAB stores them in the same order, thus column wise. When it has filled up a dimension, it will increase the next dimension index by 1 and keep on filling. Thus, the 1x4 first column of A gets stored as [16;5] first, then the second column is filled, making the first page of B(:,:,1) = [16 9;5 4], i.e. it started filling the second column of the first page. Now that the first page is full, the second column of A gets put into the second page of B in the same way, the third column goes to the third page and so on and so forth, until all elements in A are copied.

Note: this is why the "size" parameter of reshape is so important, it tells the program when to start in the next dimension. The product of your sizes, i.e. the number of elements, cannot change during this operation!

After doing this in the original case, you end up with a 15-by-16-by-30 matrix. Meaning that you have 30 pages, your original columns, which 16 columns each, the number of blocks, of 15 rows, being the number of elements in each group.
Then mean(B,1) tells mean to take the mean over the first dimension, which are the elements in each of your blocks, and that across all blocks and pages.

The only minor thing left is that MATLAB doesn't strip off non-trailing singleton dimensions by default, so you end up with a 1-by-16-by-30 matrix. squeeze(), finally, strips out all the singleton dimensions, leaving you with a 16-by-30 matrix, containing the average of each block of 15 elements in the corresponding row/column location.

like image 98
Adriaan Avatar answered Dec 17 '22 17:12

Adriaan