看起来Jon Skeet是对的(大惊喜!)问题在于我对Average
扩展提供连续平均值的假设(它没有)。
对于我所追求的行为,我写了一个简单的ContinuousAverage
扩展方法,我在这里实现的方法是为了其他可能需要类似内容的人的利益:
public static class ObservableExtensions {
private class ContinuousAverager {
private double _mean;
private long _count;
public ContinuousAverager() {
_mean = 0.0;
_count = 0L;
}
// undecided whether this method needs to be made thread-safe or not
// seems that ought to be the responsibility of the IObservable (?)
public double Add(double value) {
double delta = value - _mean;
_mean += (delta / (double)(++_count));
return _mean;
}
}
public static IObservable<double> ContinousAverage(this IObservable<double> source) {
var averager = new ContinuousAverager();
return source.Select(x => averager.Add(x));
}
}
我正在考虑继续为其他明显的候选人做上述事情 - 所以ContinuousCount
,ContinuousSum
,ContinuousMin
,ContinuousMax
......也许ContinuousVariance
和ContinuousStandardDeviation
?有什么想法?
我在这里和那里稍微使用Rx Extensions,感觉我已经掌握了基本的想法。
现在这里有些奇怪的事情:我的印象是,如果我写下这个:
var ticks = Observable.FromEvent<QuoteEventArgs>(MarketDataProvider, "MarketTick");
var bids = ticks
.Where(e => e.EventArgs.Quote.HasBid)
.Select(e => e.EventArgs.Quote.Bid);
var bidsSubscription = bids.Subscribe(
b => Console.WriteLine("Bid: {0}", b)
);
var avgOfBids = bids.Average();
var avgOfBidsSubscription = avgOfBids.Subscribe(
b => Console.WriteLine("Avg Bid: {0}", b)
);
我会得到两个IObservable<double>
个对象(bids
和avgOfBids
);一个基本上是我MarketDataProvider
的所有市场出价的流,另一个是这些出价的平均的流。
这样的事情:
Bid Avg Bid
1 1
2 1.5
1 1.33
2 1.5
似乎我的avgOfBids
对象没有做任何事情。我错过了什么?我想我可能误解了Average
实际应该做的事情。 (IObservable<T>
上的所有类似聚合的扩展方法似乎都是这种情况 - 例如,Max
,Count
等。
答案 0 :(得分:3)
执行ContinousAverage的另一种方法是使用.Scan():
bids.Scan(new { sum = .0, count = 0 },
(agg, x) => new { sum = agg.sum + x, count = agg.count + 1 })
.Select(agg => agg.sum / agg.count)
答案 1 :(得分:2)
聚合方法不提供滚动 max / min / count / average等 - 它们在原始流完成时提供单个值。例如,如果您将代码更改为:
var avgOfBids = bids.Take(5).Average();
var avgOfBidsSubscription = avgOfBids.Subscribe(
b => Console.WriteLine("Avg Bid: {0}", b)
);
然后,一旦有5个出价,它将显示它们的平均值。然后“平均”流也将完成。
答案 2 :(得分:0)
订阅出价事件流后;你已经有效地阻止任何其他观察者订阅(订阅返回IDisposable。)
你必须与ticks并行定义另一个Observable,并订阅那个以对它进行平均。
var ticksToAverage = Observable.FromEvent<QuoteEventArgs>(MarketDataProvider, "MarketTick");
var bidsToAverage = ticksToAverage
.Where(e => e.EventArgs.Quote.HasBid)
.Select(e => e.EventArgs.Quote.Bid);
var avgOfBids = bidsToAverage.Average();
var avgOfBidsSubscription = avgOfBids.Subscribe( b => Console.WriteLine("Avg Bid: {0}", b)
);