Clojure的滚动平均序列

Art*_*ldt 4 clojure

我正在寻找一种优雅的方法来生成一系列数字的滚动平均值序列.希望比使用lazy-seq更优雅

Joh*_*den 7

不考虑效率:

(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)
Run Code Online (Sandbox Code Playgroud)

如果你需要快速,那么有一些相当明显的改进!但它会变得不那么优雅.


Mic*_*zyk 5

在SO上有一个非常相似的问题:计算列表的移动平均值.它更通用 - 代表了许多FP友好语言,使用Scala接受了答案 - 但是有一些很好的Clojure解决方案.

在那里发布了自己的解决方案.注意它确实使用lazy-seq,但那是因为我希望它在很长一段时间内表现良好(这意味着调整每一步的平均值,而不是计算每个大小=周期窗口到输入列表中的单独平均值).查看Q以获得良好的解决方案,从而进行另一种权衡,从而产生更短的代码,具有更多的声明感,实际上在非常短的时间段内表现更好(尽管在较长时期内会出现明显的减速,如预期的那样).


Joh*_*den 5

这个版本更快一点,特别是对于长窗口,因为它保持滚动总和并避免重复添加相同的东西。

由于lazy-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))))
Run Code Online (Sandbox Code Playgroud)

;; 为了帮助查看它在做什么:

(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)
Run Code Online (Sandbox Code Playgroud)

;; 例子

(take 20 (sliding-window-moving-average 5 (iterate inc 0)))
Run Code Online (Sandbox Code Playgroud)

  • 哦,这是一个非常优雅的想法...性能与我的滑动窗口版本几乎相同,但绝对更美观。您是否会考虑将其发布为其他问题的答案(请参阅我的答案中的链接)?无论如何+1! (2认同)