3

相関させて、csvファイルまたはメモリ内データテーブル(.NET)として表示したいさまざまな時系列があります。これらの時系列は、時間と値のペアの配列です(実際には、これらは時間と値だけではないオブジェクトです)。時系列は、異なる重複する期間にまたがる場合があり、一部には穴がある場合もあります(特定のタイムスタンプの値が欠落している)。

興味のある方のために、私はOPC HDA .NETライブラリを使用して、OPCHDAサーバーから過去の時系列を抽出しています。

結果のデータテーブルには、時系列ごとに1つの列があり、すべてタイムスタンプ列に基づいて時系列になっている必要があります。以下の例を参照してください。

|-------|-------|-------|-------|-------|
   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 に答える 4

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」のジャグ配列を作成します。その後、すべてのデータの 2 回目のスキャンを実行し、それらをそれぞれの位置にダンプします。

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 オブジェクトをできるだけ小さくします (つまり、double ではなく float)。

  3. 時系列値の数が将来的に大きくなることが予想される場合は、値を「値ごとに 1 行」でデータベースに格納しないでください。HDF (.NET インターフェイスを持つ) のようなものを使用するか、DB でバイナリ形式の永続的な時系列フラグメントを使用することをお勧めします (時系列データベースのように) 。

これらに固執することで、多くの問題なしに数億の時間値までスケールアップできるはずです (実行済み)。

于 2009-06-19T22:52:53.970 に答える
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などを実際のデータ型に置き換えます。

于 2009-06-19T20:40:49.273 に答える
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 パラメーターは、データ ソースのデータの列名に合わせて変更する必要があることに注意してください。

于 2009-06-24T03:40:47.043 に答える
0

メモリ使用量が問題になった場合にできることの1つは、単一のイベントの追跡から、特定の期間内のイベントの表形式のカウントに移行することです。正確にいつ起こったかについてはある程度の精度が失われますが、多くの場合、このようにデータを要約すると、画像の複雑さが軽減され、傾向がより明確になります。

はっきりしない場合は、次のようなデータを取得することを意味します。

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

これに:

12:00-12:15 event1 3
12:00-12:15 event2 1
于 2009-06-19T21:04:59.680 に答える