我有各种各样的时间序列我想关联并呈现为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
|-------|-------|-------|-------|-------|
实现这一目标的最有效方法是什么? “有效”我的意思是代码量最少。但考虑到时间序列可能变得非常大,内存使用也可能是一个问题。
答案 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)。但你应该能够理解。
还有一些注意事项,而我正处于这个主题:
如果您必须立即将所有内容保存在内存中,请转到锯齿状阵列。它比字典更有效,并且比矩形阵列具有更少的内存问题。
保持Value对象尽可能小(即:float而不是double)。
如果预计未来系列值的数量会变大,则永远不会将数据库中的值存储在“每行一行”中。建议使用HDF(具有.NET接口)或在DB中以二进制形式使用持久时间系列片段(如time serie databases中所述)
坚持这些应该允许你扩展到数亿个时间值而没有很多问题(这样做)。
答案 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