哈斯克尔的移动平均线

时间:2016-11-06 10:25:40

标签: arrays haskell moving-average weighted-average

给出权重列表:

let weights = [0.1, 0.2, 0.4, 0.2, 0.1]

和一系列测量,我想实现加权平均值。

这就是我在Python中的表现:

y=[]
w = length(weights)
for n in range(w,len(x)-w):
    y[n-w/2-1]=sum([a*b for a,b in zip(weights,x[n-w/2:n+w/2+1])])
    #y[n-3]=W[1]*x[n-2]+W[2]*x[n-1]+W[3]*x[n]+W[4]*x[n+1]+W[5]*x[n+2]

我知道Haskell没有数组,我想要实现的是low-pass-filter,我可以在其中手动定义权重。

3 个答案:

答案 0 :(得分:3)

可以使用mealy机器计算移动平均值,其中内部状态是先前的值。

我会在三个参数示例中显示移动平均线,您可以将自己弄到例如使它在大小上具有可辨识性。

Mealy机器本质上是一个初始状态," state + input"到"新州+输出"功能:

Mealy i o ~ (s, s -> i -> (o, s))

我们假设初始状态全为零,并为3以上的移动平均值编写函数。

type S = (Double, Double)
type I = Double
type O = Double

initialState :: S
initialState = (0, 0)

weight0, weight1, weight2 :: Double
weight0 = 0.25
weight1 = 0.5
weight2 = 0.25

ma :: S -> I -> (O, S)
ma (x0, x1) x2 = (o, s)
    where
    s = (x1, x2)
    o = x0 * weight0 + x1 * weight1 + x2 * weight2

现在我们得到了所有的部分,让我们在输入上运行机器:

runMealy :: (S -> I -> (O, S)) -> S -> [I] -> [O]
runMealy _ _ [] = []
runMealy f s (x : xs) =
    let (o, s') = f s x
    in o : runMealy f s' xs

试一试:

λ *Main > runMealy ma initialState [1,2,3,4,5,6,7,8,9]
[0.25,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0]

您可以删除第一个生成的值,因为机器内部状态为"预热"。

对于任意大小的移动平均机,你可以使用Data.Sequence,因为当你推到一端时它会有更好的数据结构,而从另一端弹出,然后是单链表[]

为什么我要谈论Mealy机器?因为在某些时候,您很可能遇到需要在Haskell中使用某些流式库的情况:pipesconduitmachines。那么Mealy机器方法将是唯一合理的解决方案。

你也可以制作自回归模型!

答案 1 :(得分:3)

压缩会自动处理对齐:

wma :: Num a => [a] -> [a] -> [a]
wma weights = map (sum . zipWith (*) weights )   -- weighted-moving-average
                . foldr (zipWith (:)) (repeat []) 
                . take (length weights) 
                . tails 

see also)。

答案 2 :(得分:2)

tails为您提供输入列表的尾部列表。所以tails [1,2,3] = [[1,2,3],[2,3],[3],[]]。由于我们不需要最后一个空列表,因此我们使用(init.tails)来获取尾部列表中除最后一个元素之外的所有内容。

import Data.List (tails)
averages :: Num a => [a] -> [a] -> [a]
averages weights xs = sum . zipWith (*) weights <$> (init.tails) xs

请注意,这很可能不会在列表的开头和结尾以您想要的方式运行。特别是因为它在开始时和结束时表现不同。第一个元素是第一个length weight元素的平均值,但最后一个元素只是head weight * last xs

如果你想在开始时结束行为,你可以使用这样的东西:

import Data.List (tails)
averages :: Num a => [a] -> [a] -> [a]
averages weights xs = sum . zipWith (*) weights <$>
  (init.tails) (replicate (length weights - 1) 0 ++ xs)

如果您想在开始时结束行为,可以使用:

import Data.List (tails)
averages :: Num a => [a] -> [a] -> [a]
averages weights xs = sum . zipWith (*) weights <$>
  takeWhile (not . null . drop (l-1)) (tails xs)
  where l = length weights

如果你想以第一个/最后一个元素与权重列表的中心元素相乘开始和结束,我们必须使用上述两个答案的组合:

import Data.List (tails)
averages :: Num a => [a] -> [a] -> [a]
averages weights xs = sum . zipWith (*) weights <$>
  takeWhile (not . null . drop half) (replicate half 0 ++ xs)
  where half = length weights `quot` 2