What about this test (in OrderedCollectionTest - it suggests a Trait too) ?

testRunningMeans

        | result col |
        col := #(1 1 2 2 3 3) asOrderedCollection.
        result := col runningMeans: 2. 

        self assert: (result = {1. (3/2). 2. (5/2). 3}).
        
        self assert: (result class = Array).
        
        self assert: result size <= (col size). "Running means with 1 has 
little interest ?"
        
        self should: [ col runningMeans: 7 ] raise: SubscriptOutOfBounds.
        self  should: [ col runningMeans: -2 ] raise: SubscriptOutOfBounds.
        self  should: [ col runningMeans: 1.3 ] raise: Error withExceptionDo: [ 
:anException | self assert: anException messageText equals: 'primitive 
#basicNew: in Array class failed’ ]


Cheers,
Cédrick

> Le 13 avr. 2020 à 04:47, Richard O'Keefe <rao...@gmail.com> a écrit :
> 
> I did mention that it was one of eight implementations that I tried.
> In fact you broke it with your change.
> It is important ***NOT*** to keep a running sum but a running MEAN.
> The line
>    m := ((self at: i) - (self at: d)) / width + m
> was written that way for a reason.
> 
> I did think of renaming the variables for general use, but decided to display
> the actual code that I tested.  If you want polished code, here it is.
> 
>    runningMeans: width
>      "This returns an array of running means as if the receiver
>       were broken into overlapping segments 'width' long and the
>       mean of each calculated.  This uses an adaptation of
>       Welford's algorithm for stably updating the mean; it is
>       important to maintain a current MEAN not a current SUM.
>       This has been tested against 7 other algorithms.  It was
>       the most accurate of the faster ones.  The result is an
>       Array no matter what kind of sequence the receiver is.
>       If the receiver is a tree (like a virtual concatenation)
>       or a singly or doubly linked list you should convert the
>       receiver to an Array first.  Note that there is no
>       explicit check that width is an Integer or is in range;
>       none is needed because those checks happen anyway."
>      |result mean resultIndex|
>      result := Array new: self size - width + 1.
>      mean := 0.
>      1 to: width do: [:i | mean := (self at: i) + mean].
>      mean := mean / width.
>      resultIndex := 1.
>      result at: resultIndex put: mean.
>      width + 1 to: self size do: [:i |
>        mean := ((self at: i) - (self at: resultIndex)) / width + mean.
>        resultIndex := resultIndex + 1.
>        result at: resultIndex put: mean].
>      ^result
> 
> 
> 
> On Mon, 13 Apr 2020 at 00:23, Sven Van Caekenberghe <s...@stfx.eu> wrote:
>> 
>> 
>> 
>>> On 12 Apr 2020, at 13:53, Cédrick Béler <cdric...@gmail.com> wrote:
>>> 
>>> Beautiful ^^
>> 
>> I also like it.
>> 
>> But why the single letter variable names ? Why not:
>> 
>> SequenceableCollection>>#runningMeans: width
>>  | means sum index |
>>  means := Array new: self size - width + 1.
>>  sum := 0.
>>  1 to: width do: [ :each |
>>    sum := sum + (self at: each) ].
>>  index := 1.
>>  means at: index put: sum / width.
>>  width + 1 to: self size do: [ :each |
>>    sum := sum - (self at: index) + (self at: each).
>>    index := index + 1.
>>    means at: index put: sum / width ].
>>  ^ means
>> 
>> A good comment, a correct initial bounds check and unit tests are also 
>> needed.
>> 
>>> I would vote for inclusion in the base image ?
>>> With your explanation as comments.
>>> 
>>> I’ll play with it.
>>> 
>>> Thanks
>>> Cedrick
>>> 
>>>> Le 12 avr. 2020 à 12:19, Richard O'Keefe <rao...@gmail.com> a écrit :
>>>> 
>>>> 
>>>> I have coded and benchmarked 8 different running mean algorithms.
>>>> In the presence of inexact numbers it is not as accurate as
>>>> redoing the sums, but it's pretty close, and it's fast.
>>>> If "width" is not an integer or is out of range, an error
>>>> will be reported by #new: or #at:[put:].  It's based on Welford's
>>>> stable update.
>>>> 
>>>> Of course this approach does NOT work for trimmed or Winsorised
>>>> means or for medians or any kind of robust estimate of location.
>>>> 
>>>> SequenceableCollection
>>>> methods for: 'summarising'
>>>>   runningMeans: width
>>>>     |a m d|
>>>>     a := Array new: self size - width + 1.
>>>>     m := 0.
>>>>     1 to: width do: [:i |
>>>>       m := (self at: i) + m].
>>>>     m := m / width.
>>>>     d := 1.
>>>>     a at: d put: m.
>>>>     width + 1 to: self size do: [:i |
>>>>       m := ((self at: i) - (self at: d)) / width + m.
>>>>       d := d + 1.
>>>>       a at: d put: m].
>>>>     ^a
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>> 
>> 
>> 
> 


Reply via email to