Domanda

Ho varie serie temporali che vorrei correlare e presentare come file csv o dati in memoria (.NET). Queste serie temporali sono matrici di coppie valore-tempo (in realtà si tratta di oggetti che contengono più di un semplice tempo e valore). Le serie temporali possono estendersi su periodi di sovrapposizione diversi e alcuni potrebbero persino presentare buchi (valori mancanti per determinati timestamp).

Per gli interessati, sto usando la libreria OPC HDA .NET per estrarre serie storiche storiche da un server OPC HDA.

Il dato risultante dovrebbe avere una colonna per ogni serie temporale, tutte in ordine cronologico basato su una colonna timestamp. Vedi l'esempio seguente:

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

Quale sarebbe il modo più efficace per raggiungere questo obiettivo? Con "efficace" Intendo con la minima quantità di codice. Ma considerando che la serie temporale potrebbe diventare piuttosto grande, anche l'uso della memoria potrebbe essere un problema.

È stato utile?

Soluzione

Potresti utilizzare una struttura di dati come un dizionario nidificato e scorrere i contenuti:

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

Dove il tuo codice di output sta scrivendo a qualcos'altro, a seconda delle tue esigenze, e sostituisci i tipi di dati TimeSeries, Value, ecc., con i tuoi attuali tipi di dati.

Altri suggerimenti

Puoi prima scansionare tutte le serie presenti per i valori distinti (per esempio, aggregandoli in un HashSet), quindi semplicemente scaricarli in una matrice di date (memorizzando una corrispondenza tra data e posizione dell'indice in un dizionario).

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

Quindi, crea un array frastagliato con larghezza di " NumberOfSeries " e lunghezza di "NumberOfDates". Successivamente, esegui una seconda scansione di tutti i dati e scaricali nelle loro posizioni.

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

Ho scritto questo codice senza toccare VisualStudio, quindi potrei avere alcuni errori di battitura. Oppure potrebbero essere utilizzati alcuni metodi LINQ che non sono presenti in .NET (basta guardare in Lokad.Shared. dll ). Ma dovresti essere in grado di avere l'idea.

Alcune altre note, mentre io sono all'argomento:

  1. Scegli l'array frastagliato, se devi conservare tutto in memoria contemporaneamente. È molto più efficiente di un dizionario e presenta molti meno problemi di memoria rispetto a un array rettangolare.

  2. Mantieni gli oggetti Value il più piccolo possibile (ovvero float anziché double).

  3. Se si prevede che il numero di valori di serie temporali aumenterà in futuro, non archiviare mai i valori nel database in una "riga per valore". Si consiglia di scegliere qualcosa come HDF (che ha un'interfaccia .NET) o utilizzare la serie persist time frammenti in formato binario in DB (come in database di serie temporali )

Attenersi a questi dovrebbe consentire di scalare fino a centinaia di milioni di valori temporali senza molti problemi (fatto ciò).

Ehi Chris. Mi rendo conto che hai già accettato una risposta, ma ho pensato di pubblicare una soluzione che uso. Spero che sia di qualche utilità per qualcuno. Se non altro mi fornisce un posto per trovarlo in futuro. : -)

Questo è il codice VBA emesso direttamente da un modulo di codice Excel 2007. Potrebbe essere facilmente convertito in .Net.

La chiave per la manipolazione dei dati è l'oggetto tabella pivot. Ho trovato estremamente efficace nel portare i dati nel layout che hai specificato.

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

Da lì è relativamente facile sfruttare le statistiche di regressione di Excel integrate per generare una matrice di correlazione. Con questa tecnica sto producendo un foglio di lavoro con una matrice di correlazioni 600x600 in circa 45 secondi.

Nota che i parametri .PivotFields dovrebbero essere modificati per adattarsi ai nomi delle colonne dei tuoi dati dalla tua origine dati.

Una cosa che puoi fare se l'uso della memoria diventa un problema è passare dal tracciamento di singoli eventi ai conteggi tabulari di un evento in un determinato periodo di tempo. Perdi una certa precisione circa esattamente quando sono successe le cose, ma spesso riassumere i tuoi dati in questo modo può ridurre la complessità della tua immagine e rendere più evidenti le tendenze.

Nel caso in cui non sia ovvio, intendo prendere dati che assomigliano a questo:

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

a questo:

12:00-12:15 event1 3
12:00-12:15 event2 1
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top