代码之家  ›  专栏  ›  技术社区  ›  chriscena

时间序列和相关策略

  •  3
  • chriscena  · 技术社区  · 17 年前

    我有各种不同的时间序列,我想关联和呈现为一个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 
    |-------|-------|-------|-------|-------|
    

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

    4 回复  |  直到 17 年前
        1
  •  2
  •   Doug R    17 年前

    您可以使用类似嵌套字典的数据结构,并对内容进行迭代:

    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等替换为实际的数据类型。

        2
  •  4
  •   Rinat Abdullin    17 年前

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

    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”且长度为“numberofstates”的交错数组。之后,对所有数据进行第二次扫描,并将它们转储到它们的位置。

    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. 使值对象尽可能小(即:浮动而不是双精度)。

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

    坚持这些应该可以让您扩展到数亿时间值,而不会有很多问题(做到这一点)。

        3
  •  1
  •   GollyJer    17 年前

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

    这是直接从Excel2007代码模块中触发的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参数以适应数据源中数据的列名。

        4
  •  0
  •   James Thompson    17 年前

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

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

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

    对此:

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