滞后或标准偏差的自定义运算符

时间:2012-12-05 21:52:56

标签: f# system.reactive

使用RX时扩展可用运算符的正确方法是什么?

我想构建一些我觉得有用的操作。

第一个操作只是系列的标准偏差。

第二个操作是第n个滞后值,即如果我们滞后2并且我们的系列是ABCDEF,当按下F时,滞后将是D,当按下A时滞后将是空/空当推迟B时滞后将是当C被推动时为null / empty,Lag将是A

将这些类型的运算符从rx.codeplex.com的内置函数中解决是否有意义还是有更简单的方法吗?

3 个答案:

答案 0 :(得分:1)

其中一些比其他人更容易(像往常一样)。对于按计数(而不是时间)的'滞后',您只需使用等于'滞后'大小的Observable.Buffer创建一个滑动窗口,然后取结果列表的第一个元素。

到目前为止滞后= 3,功能是:

obs.Buffer(3,1).Select(l => l.[0])

转换为扩展功能非常简单。我不知道它是否有效,因为它重复使用相同的列表,但在大多数情况下,这应该无关紧要。我知道你想要F#,翻译很简单。

对于运行聚合,通常可以使用Observable.Scan来获取“运行”值。这是基于到目前为止看到的所有值计算的(并且实现非常简单) - 即您必须实现每个后续元素的所有值都是前一个聚合和新元素。

如果由于某种原因你需要一个基于滑动窗口的运行聚合,那么我们会遇到更困难的领域。在这里,您首先需要一个可以为您提供滑动窗口的操作 - 这由上面的Buffer覆盖。但是,您需要知道从此窗口中删除的值,以及已添加的

因此,我推荐一个新的Observable函数,它根据现有的window + new值维护一个内部窗口,并返回新窗口+删除的值+添加值。您可以使用Observable.Scan编写此内容(我建议使用内部队列进行有效实施)。它应该采用一个函数来确定在给定新值的情况下要删除哪些值(这样它可以按时间或按计数进行参数化。)

此时,Observable.Scan可以再次用于获取旧的聚合+窗口+删除的值+添加的值并提供新的聚合。

希望这有帮助,我确实意识到这是很多话。如果您可以确认该要求,我可以帮助您了解该特定用例的实际扩展方法。

答案 1 :(得分:1)

在惯用的Rx中,任意延迟可以由Zip组成。

let lag (count : int) o = 
    let someo = Observable.map Some o
    let delayed = Observable.Repeat(None, count).Concat(someo)        
    Observable.Zip(someo, delayed, (fun c d -> d))    

对于滚动缓冲区,最有效的方法是使用固定大小的Queue / ResizeArray

let rollingBuffer (size : int) o = 
    Observable.Create(fun (observer : IObserver<_>) -> 
    let buffer = new Queue<_>(size)
    o |> Observable.subscribe(fun v -> 
            buffer.Enqueue(v)
            if buffer.Count = size then
                observer.OnNext(buffer.ToArray())
                buffer.Dequeue() |> ignore
        )
    )

numbers |> rollingBuffer 3 |> log

seq [0L; 1L; 2L]
seq [1L; 2L; 3L]
seq [2L; 3L; 4L]
seq [3L; 4L; 5L]
...

要配对相邻值,您只需使用Observable.pairwise

即可
let delta (a, b) = b - a
let deltaStream = numbers |>  Observable.pairwise |> Observable.map(delta) 
如果您想应用滚动计算,

Observable.Scan会更简洁。

答案 2 :(得分:0)

对于lag,您可以执行类似

的操作
module Observable =
  let lag n obs =
    let buf = System.Collections.Generic.Queue()
    obs |> Observable.map (fun x ->
      buf.Enqueue(x)
      if buf.Count > n then Some(buf.Dequeue())
      else None)

此:

Observable.Range(1, 9) 
  |> Observable.lag 2 
  |> Observable.subscribe (printfn "%A") 
  |> ignore

打印:

<null>
<null>
Some 1
Some 2
Some 3
Some 4
Some 5
Some 6
Some 7