2016-11-06 11 views
4

Учитывая список весов:Скользящее среднее в 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, в котором я могу определить веса вручную.

+0

Учитывая некоторую магию, я хотел бы пони. Но серьезно, это ужасно недоказано. Что такое 'x'? Что такое 'w' (возможно, вы имели в виду« вес »)? Является ли 'length w' always' 5'? Есть ли у вас эталонная реализация на другом языке? __Что вы пробовали? – Zeta

+0

X - это входной массив, w - вес, y - результирующий массив. –

+0

Учитывая, что мы находимся на языке без массивов, я предполагаю, что вы начинающий язык. В любом случае, дополнительная информация должна быть отредактирована в самом вопросе, так что другим пользователям не нужно удалять информацию из комментариев. У меня нет времени, чтобы дать полный ответ, но это должно быть осуществимо с помощью 'tails',' zipWith' и немного битмантирования границ. – Zeta

ответ

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 
+2

'takeWhile (> = length weight)' не очень дружелюбен к ресурсам. Вместо этого используйте 'l = length wights' один раз, а затем' not. ноль . drop (l - 1) ', чтобы проверить, содержит ли список не менее элементов' l'. – Zeta

3

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

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

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

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. Тогда машинный подход Мили станет единственным разумным решением.

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

+0

«Тогда машинный подход Мили станет единственным разумным решением» - это довольно сильное требование. У вас есть ссылка? – user3237465

+0

@ user3237465 В итоге вы получаете что-то вроде машины Мили, если в то время вы ограничиваете себя единственным элементом ввода. Может быть какой-то комбинатор, создающий движущиеся буферы для вас, но это машина Мили сама. – phadej

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

 Смежные вопросы

  • Нет связанных вопросов^_^