时间序列和相关策略

时间:2009-06-19 20:27:52

标签: c# algorithm time-series

我有各种各样的时间序列我想关联并呈现为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 个答案:

答案 0 :(得分:4)

您可以先扫描所有当前系列中的不同值(例如,将它们聚合在一个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中所述)

  4. 坚持这些应该允许你扩展到数亿个时间值而没有很多问题(这样做)。

答案 1 :(得分:2)

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

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 :(得分:1)

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

这是直接从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参数以适合数据源中数据的列名。

答案 3 :(得分:0)

如果内存使用成为问题,您可以做的一件事就是从跟踪单个事件转移到给定时间段内事件的表格计数。在事情发生的时候你会精确地失去一些准确性,但是经常总结你的数据会降低你的图片的复杂性并使趋势更加明显。

如果不明显,我的意思是采用这样的数据:

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

到此:

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