Clojure的滚动平均序列

时间:2010-03-01 23:13:53

标签: clojure

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

4 个答案:

答案 0 :(得分:6)

不考虑效率:

(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)

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

答案 1 :(得分:5)

在SO上有一个非常相似的问题:Calculating the Moving Average of a List。它更为通用 - 代表了一些FP友好语言,使用Scala接受了答案 - 但是有一些很好的Clojure解决方案。

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

答案 2 :(得分:4)

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

由于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))))

;;为了帮助了解它正在做什么:

(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)))

答案 3 :(得分:0)

您可以在clojure.core中使用partialsums,而不是reductions fn(这有助于查看正在发生的事情):

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