如何在RX中对序列进行简单有状态的转换?
假设我们想要对IObservable noisySequence进行指数移动平均变换。
每当noisySequence滴答时,emaSequence应该勾选并返回该值 (previousEmaSequenceValue *(1-lambda)+ latestNoisySequenceValue * lambda)
我想我们使用主题,但究竟是怎么回事?
public static void Main()
{
var rand = new Random();
IObservable<double> sequence = Observable
.Interval(TimeSpan.FromMilliseconds(1000))
.Select(value => value + rand.NextDouble());
Func<double, double> addNoise = x => x + 10*(rand.NextDouble() - 0.5);
IObservable<double> noisySequence = sequence.Select(addNoise);
Subject<double> exponentialMovingAverage = new Subject<double>(); // ???
sequence.Subscribe(value => Console.WriteLine("original sequence "+value));
noisySequence.Subscribe(value => Console.WriteLine("noisy sequence " + value));
exponentialMovingAverage.Subscribe(value => Console.WriteLine("ema sequence " + value));
Console.ReadLine();
}
答案 0 :(得分:7)
这是将状态附加到序列的方法。在这种情况下,它计算最后10个值的平均值。
var movingAvg = noisySequence.Scan(new List<double>(),
(buffer, value)=>
{
buffer.Add(value);
if(buffer.Count>MaxSize)
{
buffer.RemoveAt(0);
}
return buffer;
}).Select(buffer=>buffer.Average());
但你可以使用Window(这是一种泛化)来获得你的平均值。
noisySequence.Window(10)
.Select(window=>window.Average())
.SelectMany(averageSequence=>averageSequence);
答案 1 :(得分:4)
对于其中许多类型的计算,Buffer
是最简单的方法
var movingAverage = noisySequence.Buffer(/*last*/ 3,
/*move forward*/ 1 /*at a time*/)
.Select(x => (x[0] + x[1] + x[2]) / 3.0);
如果你需要携带状态,请使用Scan
运算符,类似于Aggregate
,除了每次迭代都会产生值。
编辑:修正注释语法
答案 2 :(得分:1)
谢谢!这是使用Scan
的解决方案 const double lambda = 0.99;
IObservable<double> emaSequence = noisySequence.Scan(Double.NaN, (emaValue, value) =>
{
if (Double.IsNaN(emaValue))
{
emaValue = value;
}
else
{
emaValue = emaValue*lambda + value*(1-lambda);
}
return emaValue;
}).Select(emaValue => emaValue);