IObservable <double> .Average应该如何工作?</double>

时间:2010-05-07 19:59:06

标签: .net average system.reactive

更新

看起来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));
    }
}

我正在考虑继续为其他明显的候选人做上述事情 - 所以ContinuousCountContinuousSumContinuousMinContinuousMax ......也许ContinuousVarianceContinuousStandardDeviation?有什么想法?


原始问题

我在这里和那里稍微使用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>个对象(bidsavgOfBids);一个基本上是我MarketDataProvider的所有市场出价的流,另一个是这些出价的平均的流。

这样的事情:

Bid    Avg Bid
1      1
2      1.5
1      1.33
2      1.5

似乎我的avgOfBids对象没有做任何事情。我错过了什么?我想我可能误解了Average实际应该做的事情。 (IObservable<T>上的所有类似聚合的扩展方法似乎都是这种情况 - 例如,MaxCount等。

3 个答案:

答案 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)
                );