# Average Windowing of Signal - noticeable delay as time increases

5 views (last 30 days)
Sarah on 12 Apr 2013
Hi everyone, I have a weird issue with the results of my code. What I am trying to do is calculate averages of a signal based on a sliding window. For example, if I have a signal with 100 samples, and I set my window size to 10, my code will calculate the average for every 10 samples in the signal and store it in an array. Here is the following code:
for I = 1:(length(Sig)-1)/Win.Size
Avg(1,I) = mean(Sig(1,Win.Start:Win.End),2);
Win.Start = Win.Start + Win.Size;
Win.End = Win.End + Win.Size;
Error(1,I) = (Avg(1,I) - TrueVal)/TrueVal;
if Win.End > length(Sig)
Win.End = 1091046;
end
end
Sig is a 1x1091046 signal.
Win.Size (Window Size) is 1000.
Avg contains the average for each 1000 samples of Sig for each for loop iteration until the end of Sig.
Error calculates the error between the average and the true value.
For some reason, I feel like each iteration slightly skews the results. Is there anything wrong with my code that causes a lag?

Cedric on 12 Apr 2013
Edited: Cedric on 12 Apr 2013
It seems to me that you are not really computing an average on windows around each value, but an average by block. If this TrueVal that you are using is the value that corresponds to the beginning of each block, you will have a shift of half the window size. To illustrate: if your signal is made of 10 values 1 to 10 and you want a moving average window of 3, you should have something like
values : 1 2 3 4 5 6 7 8 9 10
step 1 : - - mean = (1+2)/2 = 1.5 (*)
step 2 : - - - mean = (1+2+3)/3 = 2
step 3 : - - - mean = (2+3+4)/3 = 3
... ...
step 9 : - - - mean = (8+9+10)/3 = 9
step 10: - - mean = (9+10)/2 = 9.5 (*)
Note (*): the behavior here can be configured.
Here, you can see that you have as many averages as data points, and that they are matching quite well except at boundaries.
It seems that you are doing something more like:
values : 1 2 3 4 5 6 7 8 9 10
step 1 : - - - mean = (1+2+3)/3 = 2
step 2 : - - - mean = (4+5+6)/3 = 5
step 3 : - - - mean = (7+8+9)/3 = 8
Here, if you compare 1,4,7 to 2,5,8, you see the shift.
I am not sure at this point what it is that you want to achieve, but I would recommend using CONV or CONV2.
Example:
t = 0:0.1:10 ;
values = 2*sin(t) + rand(1, numel(t)) ; % Some values: noisy SIN.
windowSize = 9 ;
kernel = ones(1, windowSize)/windowSize ;
movingAverage = conv(values, kernel, 'same') ;
plot(t, [values.', movingAverage.']) ;
Sarah on 12 Apr 2013
You are correct. I am trying to compute the average by block, I am not technically trying to compute the moving average. However, whenever I compute the average by block...I notice that there is some lag or shift. How can I correct that?
Also, I want to say thank you for the elaborate answer. I really appreciate your time.
Cedric on 12 Apr 2013
Edited: Cedric on 12 Apr 2013
You're welcome!
I think that if you are really comparing block averages with values corresponding to beginnings of blocks, the only way to "synchronize" both is to change the code so you compare block averages with values corresponding to the middle of blocks. For that you can either keep the same blocks and choose shifted values, of keep same values but shift blocks.
Applied to my example, the first case would be to compare block averages not with 1,4,7 that are values at beginnings of blocks, but with 2,5,8 that are values corresponding to the middle of blocks. The second case would mean building other blocks (around values locations), e.g.
values : 1 2 3 4 5 6 7 8 9 10
step 1 : - mean = 1/1 = 1
step 2 : - - - mean = (3+4+5)/3 = 4
step 3 : - - - mean = (6+7+8)/3 = 7
step 4 : - mean = 10/1 = 10
In this case, blocks are build around indices of values and are truncated at boundaries.
As I don't really know what you want to compute, I can't be more specific. One point about the implementation though. Here is an alternate way for building a block average that should be more efficient than your loop. You would have to adapt it to your needs though:
valuesSize = 20 ; % Example with 20 random
blockSize = 4 ; % values and a block size
values = randi(10, 1, valuesSize) % of 4.
% Build a vector of block IDs.
blockStarts = 1 : blockSize : (valuesSize-blockSize+1) ;
blockIds = zeros(1, valuesSize) ;
blockIds(blockStarts) = 1 ;
blockIds = cumsum(blockIds)
% Compute averages - concise but slow for large arrays.
% blockAverages = accumarray(blockIds.', values, [max(blockIds),1], @mean) ;
% Compute averages - less concise but faster for large arrays.
blockSums = accumarray(blockIds.', values, [max(blockIds),1]) ;
blockCounts = accumarray(blockIds.', ones(1, valuesSize), [max(blockIds),1]) ;
blockAverages = blockSums ./ blockCounts
Running this gives:
values =
7 8 9 4 9 1 1 9 6 9 2 9 1 7 1 3 3 3 6 7
blockIds =
1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5 5 5
blockAverages =
7.0000
5.0000
6.5000
3.0000
4.7500
Let me know if I should clarify anything.