【问题标题】:Time-series and correlation strategies时间序列和相关策略
【发布时间】:2010-11-04 10:18:54
【问题描述】:

我有各种时间序列,我想将它们关联起来并以 csv 文件或内存数据表 (.NET) 的形式呈现。这些时间序列是时间-值对的数组(实际上这些对象不仅仅包含时间和值)。时间序列可能跨越不同的重叠时期,有些甚至可能存在漏洞(给定时间戳的缺失值)。

对于那些感兴趣的人,我正在使用 OPC HDA .NET 库从 OPC HDA 服务器中提取历史时间序列。

生成的数据表应该有一个用于每个时间序列的列,所有时间序列都基于时间戳列按时间顺序排列。请参见下面的示例:

|-------|-------|-------|-------|-------|
   TIME    TS1     TS2     TS3     TS4
|-------|-------|-------|-------|-------|
    1       X               X       X
|-------|-------|-------|-------|-------|
    2       X       X       X       X
|-------|-------|-------|-------|-------|
    3       X       X               X
|-------|-------|-------|-------|-------|
    4       X       X       X 
|-------|-------|-------|-------|-------|
    5       X       X       X 
|-------|-------|-------|-------|-------|

实现这一目标的最有效方法是什么? “有效”是指使用最少的代码。但考虑到时间序列可能会变得非常大,内存使用也可能是一个问题。

【问题讨论】:

  • 时间值对是如何存储的?
  • “有效”在什么意义上?或者,什么是无效的例子?
  • 请查看我编辑过的问题,感谢您对澄清的反馈。

标签: c# algorithm time-series


【解决方案1】:

您可以使用嵌套字典之类的数据结构并迭代内容:

Dictionary <TimeSeries, Dictionary<DateTime, Value>> dict = new Dictionary<TimeSeries, Dictionary<DateTime, Value>>();

foreach (TimeSeries series in dict.Keys) {

    //table row output code goes here
    Dictionary<DateTime, Value> innerDict = dict[series];
    foreach (DateTime date in innerDict.Keys) {
        Value seriesValueAtTimeT = innerDict[date];
        //table column output code goes here
    }
}

根据您的需要,您的输出代码在哪里写入其他内容,并且您将数据类型 TimeSeries、Value 等替换为您的实际数据类型。

【讨论】:

  • 谢谢,Doug R。这为我指明了一个适合我的方向。我正在使用 SortedDictionary 和 Dictionary 集合,如下所示: SortedDictionary> 其中 datetime 是时间戳,嵌套字典包含来自具有该时间戳的不同时间序列的所有值。这样,我可以轻松地遍历时间戳的时间顺序列表,获取所有值,并将数据逐行推送到 DataTable 或文本文件中。
【解决方案2】:

如果内存使用成为问题,您可以做的一件事是从跟踪单个事件转移到给定时间段内的事件列表计数。您会失去一些关于事情发生的确切时间的精确度,但经常像这样汇总数据可以降低图片的复杂性并使趋势更加明显。

如果不是很明显,我的意思是获取如下所示的数据:

12:00 event1
12:01 event2
12:10 event1
12:11 event1

到这里:

12:00-12:15 event1 3
12:00-12:15 event2 1

【讨论】:

    【解决方案3】:

    您可以首先扫描所有现有系列的不同值(例如,将它们聚合到 HashSet 中),然后简单地将它们转储到日期数组中(将日期和索引位置之间的匹配存储在字典中)。

    var distinctDates = allSeries
      .SelectMany(s => s.Values.Select(v => v.Date))
      .Distinct()
      .OrderBy(d => d)
      .ToArray();
    
    var datePositions = distinctDates
      .Select((d,index) => new 
        {
          Date = d,
          Index = index
        }).
      .ToDictionary(x => x.Date, x => x.Index);
    

    然后,创建一个宽度为“NumberOfSeries”、长度为“NumberOfDates”的锯齿状数组。之后,对所有数据进行第二次扫描并将它们转储到它们的位置。

    var values = new float[allSeries.Length][];
    for (var i=0;i<allSeries.Length;i++)
    {
      values[i] = new float[distinctDates.Length];
      var currentSerie = allSeries[i];
      foreach(var value in currentSerie.Values)
      {
        var index = datePositions[value.Date];
        values[i][index] = value.Value;
      }      
    }
    

    我在没有接触 VisualStudio 的情况下编写了这段代码,所以我可能有一些拼写错误。或者可能使用了一些 .NET 中不存在的 LINQ 方法(只需查看Lokad.Shared.dll)。不过你应该能明白。

    还有一些笔记,当我谈到这个话题时:

    1. 如果您必须一次将所有内容保存在内存中,请选择锯齿状数组。它比字典更高效,内存问题也比矩形数组少很多。

    2. 保持 Value 对象尽可能小(即:float 而不是 double)。

    3. 如果预计未来时间序列值的数量会变大,则永远不要将值存储在数据库中的“每个值一行”中。建议使用类似HDF(具有.NET 接口)或在DB 中以二进制形式使用持久时间序列片段(如time serie databases

    坚持这些应该可以让您在没有很多问题的情况下扩展到数亿时间值(做到这一点)。

    【讨论】:

    • 感谢您的建议,但在这个特定的项目中,我现在坚持使用 .NET 2.0。
    • 你可以在 .NET 2.0 abdullin.com/…987654324@ 中使用所有这些
    【解决方案4】:

    嘿,克里斯。我意识到您已经接受了答案,但我想我会发布我使用的解决方案。希望它对某人有用。如果不是,至少它为我在未来某个时候找到它提供了一个地方。 :-)

    这是直接从 Excel 2007 代码模块触发的 VBA 代码。它可以很容易地转换为.Net。

    数据操作的关键是数据透视表对象。我发现它在将数据放入您指定的布局方面非常有效。

    Sub GetIndexData ()
    Dim cn as ADODB.Connection, cmd As ADODB.Command, rs As ADODB.Recordset
    Dim rPivotTopLeft As Range, rPivotBottomRight As Range
    
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    
    'Get the data.'
    Set cn = New ADODB.Connection
    With cn
      .Provider = "SQLOLEDB"
      .ConnectionString = "Database=" & mDBName & ";" & _
                          "Server=" & mDBServerName & ";" & _
                          "UID=" & mDBUserID & ";" & _
                          "Password=" & mDBPassword & ";" & _
                          "Persist Security Info=True;"
      .CursorLocation = adUseClient
      .Open
    End With
    
    Set cmd = New ADODB.Command
    Set rs = New ADODB.Recordset
    With cmd
      .ActiveConnection = adoTools.DBConnection
      .CommandType = adCmdText
      .CommandText = "SELECT YourData From YourSource WHERE YourCritera"
      Set rs = .Execute
    End With
    
    
    
    If Not (rs.BOF And rs.EOF) Then 'Check that we have some data.'
    
    'Put the data into a worksheet.'
    With wsRawData
      .Cells.CurrentRegion.Clear
    
      Set rPivotTopLeft = .Range("A1")
      With ThisWorkbook.PivotCaches.Add(SourceType:=xlExternal)
        Set .Recordset = rs
        .CreatePivotTable _
            TableDestination:=rPivotTopLeft, _
            TableName:="MyPivotTable"
      End With
    
      'Massage the data into the desired layout.'
      With .PivotTables("MyPivotTable")
        .ManualUpdate = True
    
        .PivotFields("Date").Orientation = xlRowField
        .PivotFields("Index").Orientation = xlColumnField
        .AddDataField .PivotFields("Return"), "Returns", xlSum
    
        .DisplayFieldCaptions = False
        .ColumnGrand = False
        .RowGrand = False
    
        .ManualUpdate = False
      End With
    
      mMonthCount = Range(.Range("A3"), .Cells(Rows.Count, "A").End(xlUp)).Count
      mIndexCount = Range(.Range("B2"), .Cells(2, Columns.Count).End(xlToLeft)).Count
    
      'Convert pivot table to values.'
      Set rPivotBottomRight = .Cells(mMonthCount + 2, mIndexCount + 1)
      With .Range(rPivotTopLeft, rPivotBottomRight)
        .Copy
        .PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
      End With
    
      'Format the worksheet.'
      .Range("A3").Resize(mMonthCount, 1).NumberFormat = "mmm-yy"
      .Range("B3").Resize(mMonthCount, mIndexCount).NumberFormat = "0.00%"
      Union(.Rows(2), .Columns(1)).Font.Bold = True
      .Cells.ColumnWidth = 7.14
      .Rows(1).Delete
    
    End With
    
    
    rs.close
    Set rs = Nothing
    cmd.ActiveConnection = Nothing
    Set cmd = Nothing
    cn.close
    Set cn = Nothing
    
    End Sub
    

    从那里可以相对容易地利用内置的 excel 回归统计来输出相关矩阵。使用这种技术,我可以在大约 45 秒内生成一个包含 600x600 相关矩阵的工作表。

    请注意,应更改 .PivotFields 参数以适应数据源中数据的列名。

    【讨论】:

      猜你喜欢
      • 2018-11-15
      • 2014-10-08
      • 2017-04-20
      • 2019-11-03
      • 1970-01-01
      • 1970-01-01
      • 2021-02-24
      • 1970-01-01
      • 2016-07-03
      相关资源
      最近更新 更多