последовательность скользящего среднего в Clojure - PullRequest
4 голосов
/ 02 марта 2010

Я ищу элегантный способ генерации последовательности скользящего среднего для последовательности чисел. Надеюсь, что-то более элегантное, чем использование lazy-seq

Ответы [ 4 ]

7 голосов
/ 03 марта 2010

Без учета эффективности:

(defn average [lst] (/ (reduce + lst) (count lst)))

(defn moving-average [window lst] (map average (partition window 1 lst)))


user> (moving-average 5 '(1 2 3 4 5 6 7 8))
(3 4 5 6)

Если вам нужно, чтобы это было быстро, есть несколько довольно очевидных улучшений! Но он станет менее элегантным.

5 голосов
/ 02 марта 2010

Очень похожий вопрос по SO: Расчет скользящего среднего для списка . Он более общий - представлен ряд языков, дружественных к FP, с принятым ответом с использованием Scala, но есть несколько хороших решений Clojure.

Я отправил мое собственное решение там . Обратите внимание, что он использует lazy-seq, но это потому, что я хотел, чтобы он работал хорошо в течение больших периодов (что означает корректировку среднего значения на каждом шаге, а не вычисление отдельного среднего для каждого окна размера = периода во входном списке). Посмотрите на этот Q для хороших решений, которые сделали другой компромисс, что привело к более короткому коду с несколько более декларативным чувством, которое на самом деле работает лучше в течение очень коротких периодов (хотя, как и следовало ожидать, значительно замедляется в течение более длительных периодов).

4 голосов
/ 06 марта 2010

Эта версия немного быстрее, особенно для длинных окон, так как она сохраняет скользящую сумму и избегает многократного добавления одних и тех же вещей.

Из-за ленивого-seq, он также совершенно универсален и не дает удар по стеку

(defn partialsums [start lst]
  (lazy-seq
    (if-let [lst (seq lst)] 
          (cons start (partialsums (+ start (first lst)) (rest lst)))
          (list start))))

(defn sliding-window-moving-average [window lst]
  (map #(/ % window)
       (let [start   (apply + (take window lst))
             diffseq (map - (drop window lst) lst)]
         (partialsums start diffseq))))

;; Чтобы понять, что он делает:

(sliding-window-moving-average 5 '(1 2 3 4 5 6 7 8 9 10 11))

start = (+ 1 2 3 4 5) = 15

diffseq = - (6 7 8 9 10 11)
            (1 2 3 4  5  6 7 8 9 10 11)

        =   (5 5 5 5  5  5)

(partialsums 15 '(5 5 5 5 5 5) ) = (15 20 25 30 35 40 45)

(map #(/ % 5) (20 25 30 35 40 45)) = (3 4 5 6 7 8 9)

;; Пример

(take 20 (sliding-window-moving-average 5 (iterate inc 0)))
0 голосов
/ 01 июня 2016

Вместо partialsums fn (что полезно видеть, что происходит), вы можете использовать reductions в clojure.core:

(defn sliding-window-moving-average [window lst]
  (map #(/ % window)
       (let [start   (apply + (take window lst))
             diffseq (map - (drop window lst) lst)]
         (reductions + start diffseq))))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...