我正在寻找一种优雅的方法来生成一系列数字滚动平均值的序列。希望比使用lazy-seq更优雅的东西
答案 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))))