(defun MOVING-AVERAGE (x width) "Smooths assuming input x is equally spaced." (unless (oddp width) (format t "Width needs to be odd integer >= 3.~%") (setf width (let ((rw (round width)) ) (if (oddp rw) rw (1+ rw)))) ) (let* ((half (/ (1- width) 2)) ; invariants done once (n (length x)) (count half) (sum (sum (select x (iseq half)))) ; initialize terms (drop (- (1+ half))) (add half) (smth ()) ) (dotimes (i n) (when (<= 0 drop) (decf sum (select x drop)) (decf count)) (when (< add n) (incf sum (select x add)) (incf count)) (incf drop) (incf add) ; (format t "~d ~d count=~d~%" drop add count) (push (/ sum count) smth)) (nreverse smth)))The speed of the calculation does not depend on the width of the smoother with this approach. However, as we have made it faster, we have also made it more specialized. This version does not generalize to weighted averages so simply as the prior version. We have built the constant smoothing weights into the algorithm, replacing the call to mean by the decf's and incf's.
In this form, animation works rather nicely as long as the series to be smoothed is no more than 100-200 points.
void MovAvg (double x[], long *n, long *w, double smth[]) { long i, half, count, drop, add; double sum; count = half = (*w - 1)/2; sum = 0.0; for (i=0; i < half; ++i) sum += x[i]; drop = - (1+half); add = half; for (i=0; i< *n; ++i) { if (drop >= 0) { sum -= x[drop]; --count; } if (add < *n) { sum += x[add]; ++count; } smth[i] = sum / count; ++drop; ++add; } }In order to use this function, we first compile it into an "object file" movAvg.o using the Unix cc command:
cc -c +O3 -Aa +Z movAvg.cThe man pages describe these options which
ld -o movAvg.shlb -b movAvg.oNow we can use dyn-load to read the shared library, and then use call-cfun to make things happen.