2008-10-27 52 views
3

我正在设计一个简单的内部框架来处理时间序列数据。由于LINQ是我目前使用的玩具锤子,我想用它打出一切。“加入”时间序列

我想要实现()选择(),何地(等)类的TimeSeries方法,使我可以使用LINQ语法来处理时间序列数据

有些东西是直线前进,如(从A选择x + 10中的x),给出一个新的时间序列。

组合两个或多个时间序列的最佳语法设计是什么? (来自A中,B中B选择a + b)不是很好,因为它表示一个嵌套循环。 也许一些加入?这应该对应于加入隐式时间变量。 (我心目中对应于LISP '拉链' 功能)


编辑:一些澄清是必要的。

时间序列是一种取决于时间的函数,例如,股票报价。 时间序列的组合可能是两个股票价格之间的差异,作为时间的函数。

Stock1.MyJoin(Stock2, (a,b)=>a-b) 

是可能的,但是这可以用一些LINQ语法整齐地表达吗? 我期待在我自己的class MyTimeSeries中实现LINQ方法。

回答

0

如果我正确地理解了这个问题,您想要根据它们在序列中的位置加入多个序列吗?

由于JoinGroupJoin方法都基于连接键,因此System.Linq.Enumerable类中没有任何内容可以执行此操作。然而,恰巧我写了PositionalJoin方法只是为此几天就回来,作为你的例子:

sequenceA.PositionalJoin(sequenceB, (a, b) => new { a, b }); 

下图所示的方法的语义是,它不需要序列是平等的长度,但将其修改为需要此操作并不重要。我还评论了参数检查应该如何使用我们的内部帮助类。

public static IEnumerable<TResult> PositionalJoin<T1, T2, TResult>(
    this IEnumerable<T1> source1, 
    IEnumerable<T2> source2, 
    Func<T1, T2, int, TResult> selector) 
{ 
    // argument checking here 
    return PositionalJoinIterator(source1, source2, selector); 
} 

private static IEnumerable<TResult> PositionalJoinIterator<T1, T2, TResult>(
    IEnumerable<T1> source1, 
    IEnumerable<T2> source2, 
    Func<T1, T2, TResult> selector) 
{ 
    using (var enumerator1 = source1.GetEnumerator()) 
    using (var enumerator2 = source2.GetEnumerator()) 
    { 
     bool gotItem; 
     do 
     { 
      gotItem = false; 

      T1 item1; 
      if (enumerator1.MoveNext()) 
      { 
       item1 = enumerator1.Current; 
       gotItem = true; 
      } 
      else 
      { 
       item1 = default(T1); 
      } 

      T2 item2; 
      if (enumerator2.MoveNext()) 
      { 
       item2 = enumerator2.Current; 
       gotItem = true; 
      } 
      else 
      { 
       item2 = default(T2); 
      } 

      if (gotItem) 
      { 
       yield return selector(item1, item2); 
      } 
     } 
     while (gotItem); 
    } 
} 

不知道这是否正是你想要的,但希望有一些帮助。

+0

我的细节比较复杂,因为两个时间序列可能没有完全相同的时间戳,但这个想法与您所描述的相同。 我怀疑LINQ查询语法不够。 感谢您详细说明为何如此。 – 2008-10-27 23:50:48

1

Union听起来像是正确的方式 - 没有查询表达式的支持,但我认为它表达了你的意思。

您可能有兴趣查看MiscUtil中的基于范围的类,它可以很好地用于时间。有位的扩展方法相结合的乐趣,你可以这样做:

foreach (DateTime day in 19.June(1976).To(DateTime.Today).Step(1.Day())) 
{ 
    Console.WriteLine("I'm alive!"); 
} 

我并不是说这应该替换无论你做什么,只是你也许可以采取一些想法,使其更整洁。请随时回馈:)

+0

我真的不喜欢这个例子,因为它扩展了一个基本类型(int),这只是不好的做法。 – 2008-10-27 21:46:58

+0

“构建日期时间”部分有一点似是而非,但能够通过执行1.To(10)来构建范围,这使得代码非常易读。 – 2008-10-27 21:55:33

1

从我NExtension项目:

public static IEnumerable<TResult> Zip<T1, T2, TResult>(
    this IEnumerable<T1> source1, 
    IEnumerable<T2> source2, 
    Func<T1, T2, TResult> combine) 
{ 
    if (source1 == null) 
     throw new ArgumentNullException("source1"); 
    if (source2 == null) 
     throw new ArgumentNullException("source2"); 
    if (combine == null) 
     throw new ArgumentNullException("combine"); 

    IEnumerator<T1> data1 = source1.GetEnumerator(); 
    IEnumerator<T2> data2 = source2.GetEnumerator(); 
    while (data1.MoveNext() && data2.MoveNext()) 
    { 
     yield return combine(data1.Current, data2.Current); 
    } 
} 

语法是:

Stock1.Zip(Stock2, (a,b)=>a-b) 
+1

请注意,您应该将参数检查和迭代分隔为不同的方法 - 有关详细信息,请参阅http://blogs.msdn.com/ericlippert/archive/2008/09/08/high-maintenance.aspx。 – 2008-10-27 22:30:05

1

Bjarke,看看NEsper,它是一个开源的复杂事件处理应用程序,除其他事情做类似SQL的时间序列查询。你可以学习他们如何完成它,或者甚至可以利用他们的代码来实现你的目标。这里链接http://esper.codehaus.org/about/nesper/nesper.html