Pergunta

Eu tenho vários de séries temporais que eu gostaria de correlacionar e presente tanto como um arquivo CSV ou na memória tabela de dados (NET). Estes-séries de tempo são matrizes de tempo de valor-pares (na verdade, estes são objectos que contêm mais do que apenas um tempo e custo). A série temporal pode abranger entre diferentes períodos de sobreposição, e alguns podem até ter furos (valores em falta para determinadas marcas de tempo).

Para os interessados, eu estou usando a biblioteca OPC HDA .NET para extrair histórico séries temporais a partir de um servidor OPC HDA.

A tabela de dados resultante deve ter uma coluna para cada série temporal todos em ordem cronológica com base em uma coluna timestamp. Veja o exemplo abaixo:

|-------|-------|-------|-------|-------|
   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 
|-------|-------|-------|-------|-------|

Qual seria a maneira mais eficaz de conseguir isso? Com "eficaz" Quero dizer com a menor quantidade de código. Mas, considerando que os timeseries poderia se tornar muito grande, o uso de memória também pode ser um problema.

Foi útil?

Solução

Você pode ir com uma estrutura de dados como um dicionário aninhado e iterar sobre o conteúdo:

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
    }
}

Onde o código de saída está escrevendo para outra coisa, dependendo de suas necessidades, e você substituir os TimeSeries Datatypes, valor, etc., com seus tipos de dados reais.

Outras dicas

Você pode primeira varredura todas as séries atuais para os valores distintos (por exemplo, agregando-os em um HashSet), então simplesmente despejá-los em uma matriz de datas (armazenamento de um jogo entre data e posição de índice em um dicionário).

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);

Em seguida, crie uma matriz irregulares que tem largura de "NumberOfSeries" eo comprimento do "NumberOfDates". Depois disso, fazer uma segunda verificação de todos os dados e despejá-los para suas posições.

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;
  }      
}

Eu escrevi este código sem tocar VisualStudio, então eu pode ter alguns erros de digitação. Ou pode ser usado alguns métodos LINQ que não estão presentes no .NET (basta olhar em Lokad.Shared. dll ). Mas você deve ser capaz de obter a idéia.

Alguns mais notas, enquanto eu estou no tópico:

  1. Vá para a matriz irregulares, se você tem que manter tudo na memória de uma vez. É a maneira mais eficiente do que um dicionário e tem muito menos problemas de memória do que uma matriz retangular.

  2. Mantenha Valor objetos tão pequenos quanto possível (ou seja .: flutuador em vez de duplo).

  3. Se é esperado número de valores de série temporal para ir grande no futuro, depois nunca armazenar valores na base de dados num "uma linha por valor". Recomenda-se quer ir para algo como HDF (que tem uma interface .NET) ou o uso persistem tempo serie fragmentos em formato binário em DB (como em tempo bases de dados serie )

Furar a estes deve permitir que você escalar até centenas de milhões de valores de tempo sem um monte de problemas (feito isso).

Hey Chris. Eu sei que você já aceitou uma resposta, mas pensei que eu ia postar uma solução que eu uso. Espero que seja de alguma utilidade para alguém. Se não, pelo menos, ele fornece um lugar para me para encontrá-lo em algum momento no futuro. : -)

Este é o código VBA disparou diretamente de um módulo de código Excel 2007. Pode ser facilmente convertidos em .Net.

A chave para a manipulação de dados é o objecto tabela pivô. Eu achei que fosse massivamente eficiente em obter os dados para o layout que você especificou.

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

A partir daí sua relativamente fácil de alavancagem construído na estatística de regressão do Excel para saída de uma matriz de correlação. Com esta técnica que eu estou produzindo uma planilha com uma matriz de correlações 600x600 em cerca de 45 segundos.

Note que os parâmetros .PivotFields deve ser mudado para caber os nomes de seus dados da coluna de sua fonte de dados.

Uma coisa que você pode fazer se o uso de memória se torna um problema é passar de rastreamento de eventos únicos a contagem tabulados de um evento dentro de um determinado período de tempo. Você perde alguma precisão sobre exatamente quando as coisas aconteceram, mas muitas vezes resumindo seus dados como este pode reduzir a complexidade de sua imagem e fazer as tendências mais evidente.

No caso, não é óbvio, quero dizer obtenção de dados que se parece com isso:

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

a esta:

12:00-12:15 event1 3
12:00-12:15 event2 1
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top