Haskell 的移动平均线
Moving average in Haskell
给定权重列表:
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,我可以在其中手动定义权重。
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
如果你想开始和结束 first/last 元素与权重列表的中心元素相乘,我们必须使用以上两个答案的组合:
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
移动平均线可以用粉状机器计算,其中内部状态是以前的值。
我将展示三个参数的移动平均线示例,您可以 fiddle使其大小可参数化。
Mealy machine本质上是一个初始状态,"state + input"到"new state + output"函数:
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]
您可以删除第一个生成的值,因为机器内部状态是 "warming up"。
对于任意大小的移动平均机,您可以使用 Data.Sequence
,因为当您推到一端时它是更好的数据结构,而从另一端弹出,然后 单链表, []
.
为什么我要谈论 Mealy 机器?因为在某些时候,您很可能会 运行 遇到需要在 Haskell 中使用某些流媒体库的情况:pipes
、conduit
或 machines
。那么Mealy机器的方法将是唯一合理的解决方案。
你也可以制作自回归模型!
压缩会自动处理对齐:
wma :: Num a => [a] -> [a] -> [a]
wma weights = map (sum . zipWith (*) weights ) -- weighted-moving-average
. foldr (zipWith (:)) (repeat [])
. take (length weights)
. tails
(see also).
给定权重列表:
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,我可以在其中手动定义权重。
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
如果你想开始和结束 first/last 元素与权重列表的中心元素相乘,我们必须使用以上两个答案的组合:
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
移动平均线可以用粉状机器计算,其中内部状态是以前的值。
我将展示三个参数的移动平均线示例,您可以 fiddle使其大小可参数化。
Mealy machine本质上是一个初始状态,"state + input"到"new state + output"函数:
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]
您可以删除第一个生成的值,因为机器内部状态是 "warming up"。
对于任意大小的移动平均机,您可以使用 Data.Sequence
,因为当您推到一端时它是更好的数据结构,而从另一端弹出,然后 单链表, []
.
为什么我要谈论 Mealy 机器?因为在某些时候,您很可能会 运行 遇到需要在 Haskell 中使用某些流媒体库的情况:pipes
、conduit
或 machines
。那么Mealy机器的方法将是唯一合理的解决方案。
你也可以制作自回归模型!
压缩会自动处理对齐:
wma :: Num a => [a] -> [a] -> [a]
wma weights = map (sum . zipWith (*) weights ) -- weighted-moving-average
. foldr (zipWith (:)) (repeat [])
. take (length weights)
. tails
(see also).