Скользящая средняя в Хаскеле

Дан список весов:

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 нет массивов, я пытаюсь добиться фильтра нижних частот, в котором я могу определять веса вручную.

3 ответа

Решение

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

Скользящее среднее можно рассчитать с помощью мучного автомата, где внутренним состоянием являются предыдущие значения.

Я приведу пример скользящего среднего по трем аргументам, вы можете поиграться, например, сделать его параметризуемым по размеру.

Мили-машина - это, по сути, начальное состояние и функция "состояние + вход" для функции "новое состояние + выход":

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, так как это намного лучше структура данных, когда вы нажимаете на один конец, в то время как всплывающее окно с другого, а затем один связанный список, [],


Почему я говорю о машине Мили? Потому что в какой-то момент вы, скорее всего, столкнетесь с ситуацией, когда вам нужно использовать некоторую потоковую библиотеку в Haskell: pipes, conduit или же machines, Тогда подход Мили-машины будет единственным разумным решением.

Также вы можете делать авторегрессивные модели!

Застежка-молния обеспечивает автоматическое выравнивание:

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

( см. также).

Другие вопросы по тегам