Superb, thank you Richard, very interesting answer :)

For the rounding issue, since I got bitten once, I most of the time use ScaleDecimal instead of floats for data anyway.

Cheers,

C��drick

Le 10 avr. 2020 �� 05:04, Richard O'Keefe <raoknz@gmail.com> a ��crit :

Let's take the inheritance issue first.
Yes, Collection has some subclasses where #average makes no sense,
such as String.  If any subclass of Collection should have #average, it is
Array, as #(1 2 3 4) average makes perfect sense.
BUT #($a $b $c $d) average makes exactly as much sense as 'abcd' average.
So we have
 - subclasses where a method never makes sense (String)
 - subclasses where a method always makes sense (ByteArray)
 - subclasses where a method may or may not make sense (Array).
Traits will not help at all with the third category.
Historic Smalltalk practice has been to define methods like
  String>>average self shouldNotImplement.
to "cancel" inappropriate methods to deal with the "never" subclasses,
but that still does not help with the "maybe" ones.

In my own  code I try very hard to ensure that a method is available in
a class if and only if the class can have instances where the method
makes sense.  That is, I try to make sure that #respondsTo: is "honest".
It is not always practical.

This is not a problem that is peculiar to Smalltalk.  Java and C# and C++
and you-name-it also have the "maybe" problem, where a method seems
to be available for an object but thanks to its state it is not.

As long as sending a message to an inappropriate object (whether due to
its class or its state) results in *some* exception, do we really have a problem?
'abcd' average
'abcd' asArray average
result in the same error.

Now for the roundoff error issue.

Oh dear, we really need to do a much better job educating programmers
about floating point, we really do.  In real number arithmetic,
 (a + b) - a = b
is always true.  In floating-point arithmetic it is not.
This is easiest to see in cases like
 (1.0e20 + 1.0) - 1.0e20
where Pharo answers 0.0 instead of 1.0.
Less extreme cases still give you roundoff error.

Much has been written about how to stably update mean, variance, &c.
The simplest thing is to use Welford's algorithm for the weighted mean,
using weight +1 to add the new element and -1 to remove the old.

On Thu, 9 Apr 2020 at 19:33, Christian Haider <christian.haider@smalltalked-visuals.com> wrote:

I don���t see how rounding errors could accumulate, if you keep the sum and not the average.

The rounding errors should be neutral, because each element is added once and subtracted once.

If + and ��� is symmetrical in this respect, rounding inaccuracies should balance out.

 

Cheers,

                Christian

 

Von: Pharo-users <pharo-users-bounces@lists.pharo.org> Im Auftrag von Richard O'Keefe
Gesendet: Donnerstag, 9. April 2020 05:26
An: Any question about pharo is welcome <pharo-users@lists.pharo.org>
Betreff: Re: [Pharo-users] Moving/rolling average implementations ?

 

I note that "self species ofSize: n" is not generally a good idea.

Consider ByteArray, ShortIntegerArray, WordArray.

Computing rolling means of these is perfectly sensible,

but the results will not fit into an array of the same species.

I'd stick with Array.

 

The suggestion about subtracting an old element and adding a new

one is great for integers, but for floating-point numbers risks

accumulating errors.

 

The

 

On Wed, 8 Apr 2020 at 20:07, C��drick B��ler <cdrick65@gmail.com> wrote:

Hi,

 

I wanted to do a moving/rolling average on raw data [1]. 

I haven���t find code for that (maybe this is done in polymath though).

 

So I ended writing that (I thing this is SMA):

 

SequenceableCollection>>movingAverage: anOrder     
"Answer the moving or rolling average for anOrder window"

 

    | retval size x y |

        anOrder <= 0 ifTrue: [ Error signal: 'the order must be positive'].

    size := self  size - anOrder.

    size negative  ifTrue: [ Error signal: 'the collection size is too small'].

    retval := self species ofSize: size + 1.

 

    x := 1.

y := anOrder.

    [y <=  self  size ] whileTrue: [           

 retval at: x put: (self copyFrom: x to: y) average           

 x := x + 1. y := y + 1

 ].

    ^retval

 

Not perfect but seems to works quite well (that���s probably better to remove copyFrom: and use some kind of buffer instead).

 

Any interest in that ? If any existing code too, I���ll be interested especially for other implementation (weighted, exponential) ?

 

<image001.png>

 

(#(118 113 105 105 103 99 98 101 100 107) movingAverage: 3) collect: [:v | v asScaledDecimal: 1 ] .

 

 "an Array(112.0s1 107.7s1 104.3s1 102.3s1 100.0s1 99.3s1 99.7s1 102.7s1)"

 

Cheers,

C��drick