And maybe another one to show deviation problem ? testRunningMeansDeviation
| result1 result2 col1 col2 | col1 := #(0.3s1 1s1 0.1s1 0.3s1 1s1 0.1s1 0.3s1 1s1 0.1s1). result1 := col1 runningMeans: 2. col2 := #(0.3 1 0.1 0.3 1 0.1 0.3 1 0.1). result2 := col2 runningMeans: 2. self assert: result1 first equals: result1 fourth. "presence of a rounding error" self deny: result2 first equals: result2 fourth. self assert: result2 first closeTo: result2 fourth. > Le 13 avr. 2020 à 10:48, Cédrick Béler <cdric...@gmail.com> a écrit : > > 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 >>>>> >>>>> >>>>> >>>>> >>>>> >>>> >>> >>> >> >