Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Last active May 6, 2020 17:23
Show Gist options
  • Save ericnormand/5a29561f7e220879210c3726ad64e2de to your computer and use it in GitHub Desktop.
Save ericnormand/5a29561f7e220879210c3726ad64e2de to your computer and use it in GitHub Desktop.

Moving average

Very often, your dataset represents real measurements over time. There's a clear trend in the data, but it's muddied by the noise in each data point. One way to focus on the trend is to perform a moving average. Each data point is the average of the n points around it (the window).

Your task is to write a function that takes a sequence of numbers and returns a sequence of weighted averages. Make n (the window size) an argument.

(defn window
"Return a window of size n, centered on i, of the values (a vector)"
[n values i]
(let [n2 (quot n 2)
n2o (- n n2)
start (max 0 (- i n2o))
end (min (count values) (+ i n2))]
(subvec values start end)))
(defn sum [numbers]
(reduce + 0 numbers))
(defn avg [numbers]
(/ (sum numbers) (count numbers)))
(defn moving-average [size numbers]
(->> numbers
vec
(map-indexed #(window size numbers %2))
(map avg)
(map double)))
(defn- slide [window x]
(-> window (subvec 1) (conj x)))
(defn- windows [n xs]
(let [first-window (vec (take n xs))
the-rest (drop n xs)]
(when (and (< 0 n) (= n (count first-window)))
(reductions slide first-window the-rest))))
(defn- avarage [xs]
(/ (apply + xs) (count xs)))
(defn moving-avarage [n xs]
(->> xs (windows n) (map avarage)))
(moving-avarage 1 []) ; => ()
(moving-avarage -1 [1 2 3 4]) ; => ()
(moving-avarage 0 [1 2 3 4]) ; => ()
(moving-avarage 1 [1 2 3 4]) ; => (1 2 3 4)
(moving-avarage 2 [1 2 3 4]) ; => (3/2 5/2 7/2)
(moving-avarage 3 [1 2 3 4]) ; => (2 3)
(moving-avarage 4 [1 2 3 4]) ; => (5/2)
(moving-avarage 5 [1 2 3 4]) ; => ()
(nth (moving-avarage 3 (range)) 1000000) ; => 1000001
@KingCode
Copy link

KingCode commented May 6, 2020

(defn give-and-take [n i]
  (let [lo (max 0 (inc (- i n))) 
        hi (-> (+ lo n) (#(if (< i %) i %)))]
    [lo, (-> (- hi lo) inc)]))
      
(defn window 
    "Return a trailing window of size n ending at position i, of the values (a vector)"
  [n values i]
  (let [[offset quantity] (give-and-take n i)]
    (take quantity (drop offset values))))

(defn avg [xs]
  (double (/ (apply + xs) (count xs))))

(defn moving-average [n xs]
  (->> (count xs) range
       (map (partial window n xs))
       (mapv avg)))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment