Временные ряды и корреляционные стратегии - PullRequest
3 голосов
/ 20 июня 2009

У меня есть различные временные ряды, которые я хотел бы сопоставить и представить в виде 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 ]

4 голосов
/ 20 июня 2009

Вы можете сначала отсканировать все существующие серии на предмет определенных значений (например, агрегировать их в 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, поэтому у меня может быть несколько опечаток. Или могут быть использованы несколько методов LINQ, которых нет в .NET (просто посмотрите в Lokad.Shared.dll ). Но вы должны быть в состоянии понять идею.

Еще несколько заметок, пока я в теме:

  1. Перейти к зубчатому массиву, если вам нужно сохранить все в памяти сразу. Он намного эффективнее словаря и имеет гораздо меньше проблем с памятью, чем прямоугольный массив.

  2. Сохраняйте объекты Value как можно меньшими (т. Е. Плавающие вместо двойных).

  3. Если в будущем ожидается увеличение количества серийных значений, никогда не сохраняйте значения в базе данных в «одной строке на значение». Рекомендуется либо использовать что-то вроде HDF (с интерфейсом .NET), либо использовать фрагменты сохраняемого временного ряда в двоичном виде в БД (как в базах данных временного ряда ) * 1027. *

Придерживаясь этих правил, вы сможете масштабировать до сотен миллионов значений времени без особых проблем (вот что).

2 голосов
/ 20 июня 2009

Вы можете использовать структуру данных, например вложенный словарь, и перебирать содержимое:

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 и т. Д. Фактическими типами данных.

1 голос
/ 24 июня 2009

Привет, Крис. Я понимаю, что вы уже приняли ответ, но решил опубликовать решение, которое я использую. Надеюсь, это кому-нибудь пригодится. Если не по крайней мере, это дает мне место, чтобы найти его когда-нибудь в будущем. : -)

Это код VBA, запускаемый непосредственно из модуля кода Excel 2007. Это может быть легко преобразовано в .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 для вывода матрицы корреляции. С помощью этой техники я создаю лист с матрицей корреляций 600x600 примерно за 45 секунд.

Обратите внимание, что параметры .PivotFields должны быть изменены, чтобы соответствовать именам столбцов ваших данных из вашего источника данных.

0 голосов
/ 20 июня 2009

Одна вещь, которую вы можете сделать, если использование памяти становится проблемой, - это перейти от отслеживания отдельных событий к табличному количеству событий за определенный период времени. Вы теряете некоторую точность относительно того, когда именно что-то произошло, но часто обобщая ваши данные, как это, можно уменьшить сложность вашей картины и сделать тенденции более очевидными.

Если это не очевидно, я имею в виду получение данных, которые выглядят следующим образом:

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

к этому:

12:00-12:15 event1 3
12:00-12:15 event2 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...