web-dev-qa-db-ja.com

パターンマッチングのデータ構造

次のような多くのエントリを持つ入力ファイルがあるとします。

date, ticker, open, high, low, close, <and some other values>

そして、たとえばローソク足パターンを使用して、そのファイルのエントリ(行)でパターンマッチングルーチンを実行したいとします。 (参照、Doji)そして、そのパターンは任意の均一な時間間隔で現れる可能性があります(t = 1s、5s、10s、1d、7d、2w、2yなど...)。

たとえば、パターンマッチングルーチンが分析を実行するために任意の数の行を取り、任意の数のサブパターンを含むことができるとします。言い換えると、パターンによっては、操作に4つのエントリが必要な場合もあれば、多かれ少なかれ必要な場合もあります。

また、ルーチンは後で、閉区間でのティッカーの極値(ローカルおよびグローバルの最大値と最小値、および変曲点)を検出して分類する必要があることを伝えます。たとえば、3次関数(x ^ 3 )には、区間[-1、1]に extrema があります。 (リンクを参照)

読み取り時に入力ファイルのサイズがわからない場合データ構造の観点から最も自然な選択は何でしょうか?任意のパターンをデータに適用できるように、1行のデータを含むTickerオブジェクトをTickerのコレクションに準拠させるインターフェースについてはどうでしょうか。たとえば、一致を形成するためにn要素を必要とするパターンがあるとします。これらの要素にパターンを適用してから、それらのチャンクのそれぞれを分析する必要があります。

最初に頭に浮かぶのは何ですか?

私は、次のメソッドを持つ二重にリンクされた循環リンクリストを選択しました。

Push_front()
Push_back()
pop_front()
pop_back()
[] //overloaded, can be used with negative parameters

しかし、そのデータ構造は非常に扱いにくいようです。多くのプッシュとポップが行われているため、分析を実行する前にデータ構造の詳細なコピーを作成する必要があります。

ですから、質問を明確にしたかどうかはわかりませんが、主なポイントは次のとおりです。

  1. ランダムアクセスを必要としないパターンに準拠するためにシーケンシャルデータポイントを分析する場合、どのような種類のデータ構造を考慮する必要がありますか?
  2. 一連のデータポイントの極値を分類する場合、どのようなデータ構造を考慮する必要がありますか?

更新

これが私のプログラムのメインアナライザループです(いくつかの言語のポリグロットであるいくつかの疑似コード)

data = new circular_linkedList()
// in my case, data is a circular linked list of elements.
foreach(element in dataFeed) {
    data.Push_front(element)
}

step_value = Pattern.numberRequiredElementsForMatch()
buffer = new circular_linkedList()
// at this point, you'll have a circular linked list
// where pop_back() will return the oldest entry and 
// entry, and pop_front() will return the newest

// initialize the buffer
for(value in range(step_value()) {
    buffer.Push_back(data.pop_back())
    // fill a buffer with the number of
    // values required for a match. 
    // so now buffer will contain
    // [elementk, ... , elementk+n]
    // where buffer[0] < ... < buffer[n]
}
do {
    last = buffer[len(buffer)]
    ...
    first = buffer[0]

    Pattern.match(first, ... , last)
    buffer.pop_front() // remove the first element
    buffer.Push_back(data.pop_back()) //add another element to the back


} while (!data.isEmpty())

...

とにかく、それは私が今行っていることの基本的な考えです。問題は、私が今やっている方法で、ループを繰り返して別のパターンを適用する必要があることです。 (バッファサイズが異なります)これを行うのは非効率的です。また、バッファにデータがなく、一致に必要な要素数で割り切れない場合はどうなりますか?

6
alvonellos

リンクリストの代わりにベクトル

何をするにせよ、これには二重にリンクされたリストを使用しないでください。多くの割り当てとスペースのオーバーヘッドがあります。データは順次読み取られて追加され、ランダムアクセスやアイテムのランダムな削除は行われないため、データの格納にはベクターのようなデータ構造のほうがはるかに適しています。しかし、それでも、以下に示すように、生データには過剰です。

データはリスナーを経由して流れ、その後消えます

線形パターンマッチングを行うには、データをまったく保存する必要はありません。また、データを1回トラバースするだけで済みます。アイデアは、いくつかのマッチャーがパターンを聞いて、データがハミングするようにすることです。これらは、パターンを検出するために必要なデータのみを保存します。パターンの一部になれなくなったアイテムはすべて忘れられます。

それを実現する1つの方法を説明します。実行したいタスクが効率的に行うのは簡単ではないことを警告します。リンクされたリストを使用するというあなたの提案から判断すると、関係する原則に頭を回すのに時間がかかる場合があります。

データをリッスンするマッチャーを継続的に登録します

データのパターンをリッスンするエンティティを追加することから始めましょう。認識したいすべての組み合わせに対してマッチャーファクトリを登録します。通常、各パターンは独自のマッチャークラスにあり、探している解像度によってパラメーター化されます。

次に、データの読み取りを開始し、読み取り時に各項目を各マッチャーファクトリにフィードします。次に、パターンの開始点となる可能性のあるすべての場所について、その工場でマッチャーをインスタンス化/再利用します。たとえば、7日間の解像度を持つマッチャーは、新しい週の最初のデータポイントが来るたびにインスタンス化できます。

マッチャーは、パターンを拒否/受け入れるまで内部状態を更新します

ティッカーアイテムは、アクティブな各マッチャーにもフィードされます。各マッチャーは、独自の内部マッチング状態を追跡する必要があります。たとえば、7日間の解像度を持つマッチャーは、ティッカー値を累積して7日間の平均を計算する場合があります。 7日経過するごとに、配列の次の位置に平均を格納し、後続の着信ティッカーアイテムの新しい平均累積を開始します。これは、を拒否するか、パターンの存在を確認するのに十分な週が経過するまで続きます。これを行う方法についてのアイデアを得るには、「有限状態マシン」を調べてください。

重複する計算を排除することによる効率の向上

もちろん、7日間の解像度でデータを必要とするマッチャーが複数ある場合、それぞれが独自に計算するのは効率的ではありません。マッチャーの階層を構築して、中間パターンを1回だけ計算する必要があります。ローリング平均(または他の集計関数)を維持する方法に関するアイデアについては、リングバッファーを調べます。

関連:パーサージェネレーター

いわゆる「パーサージェネレーター」は、正式な文法に対して同様のことを自動的に行います。生成されたパーサーは有限状態マシンを使用して、ソースデータの1つだけのパスで1つだけを認識するのと同じくらいの労力で数百のパターンを検出します。このようなツールが時系列データを継続的に使用するために存在することもあると思います。または、それらのアイデアを変換して問題に適用することもできます。

お役に立てれば!

7

ファイルからデータを読み取るとおっしゃっていたので、基礎となるデータ構造の最初の選択肢は配列です。これは最もコンパクトな形式のストレージであり、キャッシュに利点があり、データサイズが固定されていて事前にわかっている場合は、配列が非常にうまく機能します。ランダムアクセスを「必要としない」ことで放棄されないでください。ランダムアクセスデータ構造は、逆は当てはまりませんが、シーケンシャルのみのデータ構造に簡単に置き換えることができます。

また、配列にある種のViewオブジェクトを配列に含めることをお勧めします。これにより、ニーズにより適合するインターフェースが提供されます。パターンの開始点、時間間隔などの詳細を内部的に処理できるため、パターンマッチング関数は次のような非常にシンプルなインターフェースを持つことができます。

bool pattern(View &data) {
     return data[1].high > data[0].high;
}

data[0]data[1]が1秒または1年離れている場合、パターンマッチング関数は関係ありません。 data[0]がファイルの最初の行であるか、100万番目であるかは関係ありません。 Viewオブジェクトは、これらの詳細を抽象化します。必要に応じて異なるViewオブジェクトをインスタンス化するだけですが、それらはすべて同じ基本配列を共有します。

極値がパターンマッチングの一部として使用されている場合は、一度計算してViewオブジェクトに格納し、次のように使用できます。

bool pattern(View &data) {
     return (data.max() - data.min()) > 1.0;
}

要約すると、アイデアは、データ構造をマッチング関数から可能な限り切り離すことです。これらの関数は、ドメインの専門家が英語でそれらを表現する方法にできるだけ近いように読んでもらいたい。ファイルからではなくソケットでストリーミングされるパターンに一致させる必要がある場合は、Viewオブジェクトを簡単に変更できます。パターンマッチングの「肉」を変更する必要はありません。

2
Karl Bielefeldt