相変わらずツイッターのトレンドをいじってます。 十分なデータが収集されたので、処理を高速化したいと考えています。 トレンドワードのインデックスが付けられた連想配列を使用して、各トレンドワードに整数のインデックスを割り当てたいと考えています。 以下に、テーブルに新しい単語を追加したり、古い単語のインデックスを取得したりするための 3 つのテクニックを示します。
最初の例では、TRY を使用して、単語がテーブルにない場合に例外をキャッチします。 単語が存在する場合は最も速く、例外が発生した場合は (はるかに) 遅くなります。
2 つ目は、CONTAINS を使用して単語が存在するかどうかをテストし、それを追加するか検索します。 追加する場合は最も速く、検索する場合は最も遅くなります (検索が 2 回行われるため)。
最後は、連想配列のデフォルト値を使用して、取得した値がすでに存在するかどうかを確認します。 適度な妥協速度を提供します。
すべての例には 2 回実行される「t」ループがあります。 初めて xN 配列は空であるため、値を入力する必要があります。 2 回目では、すべての値がすでに存在しています。
// Three ways to build a lookup table. No clear winner!
// Goal: assign an integer, starting at 1, to each new
// entry. For timing purposes, use the integer as the
// key and the value. The key will be a string in real
// code. The value will be used as an index elsewhere.
explain = {"create new entry","use existing entry"};
N = 1e5; // several seconds at 1e5
/*
Use TRY to determine when an entry needs to be created.
On the "catch" create the entry. If there is no catch,
the value is already fetched. The "t" loop runs 20X
slower the first time because the "catch" is expensive.
But the second time the "t" loop is the fastest solution.
*/
x1 = [=> ];
For( t = 1, t <= 2, t += 1,
start = HP Time();
For( ix = 1, ix < N, ix += 1,
Try( y = x1[ix], y = (x1[ix] = ix) )
);
stop = HP Time();
Write( Eval Insert( "\!nTry method: ^char((stop - start)/1e6,5,3)^ seconds ^explain[t]^" ) );
);
/*
Use CONTAINS to determine if the entry needs to be created.
Use the if's "then" clause to fetch the existing index or
"else" clause to create a new entry. The "t" loop has the
fastest speed on the first run, by far, and the slowest
speed on the second run.
*/
x2 = [=> ];
For( t = 1, t <= 2, t += 1,
start = HP Time();
For( ix = 1, ix < N, ix += 1,
If( Contains( x2, ix ),
y = x2[ix],
y = (x2[ix] = ix)
)
);
stop = HP Time();
Write( Eval Insert( "\!nContains method: ^char((stop - start)/1e6,5,3)^ seconds ^explain[t]^" ) );
);
/*
Use a DEFAULT value to determine if the entry needs to be
created. Fetch the value for the entry, check for the default
and create if needed. This gives an between value for both
"t" loops. It might be a good compromise choice if no extra
testing will be done and 0 can be used as a default. !y,
below, is faster than y==0.
*/
x3 = [=> 0];
For( t = 1, t <= 2, t += 1,
start = HP Time();
For( ix = 1, ix < N, ix += 1,
y = x3[ix];
If( !y, y = (x3[ix] = ix) );
);
stop = HP Time();
Write( Eval Insert( "\!ndefault 0 method: ^char((stop - start)/1e6,5,3)^ seconds ^explain[t]^" ) );
);
// quick test... the lack of a default value in x1 and x2
// makes them compare not equal, so add it before testing.
x1 << setdefaultvalue( 0 );
x2 << setdefaultvalue( 0 );
Show( x1 == x2, x1 == x3 );
Try メソッド: 2.851 秒 新しいエントリを作成
方法を試してください: 0.099秒は既存のエントリを使用します
メソッドが含まれます: 0.209秒で新しいエントリを作成します
メソッドが含まれます: 0.151 秒は既存のエントリを使用します
デフォルトの 0 メソッド: 0.259秒で新しいエントリを作成します
デフォルトの 0 メソッド: 0.117秒は既存のエントリを使用します
x1 == x2 = 1;
x1 == x3 = 1;
赤で示した TRY と CONTAINS は、既存のエントリと新しいエントリの最速時間を示します。 緑色の場合は、DEFAULT 0 が適切な妥協点となります。 実際には、作成されるエントリの数と、エントリが何回検索されるかによって異なります。
念押し有難う! この例では、一連の許容値の範囲外のデフォルト値を選択したため、デフォルトとしてはあまり役に立ちません。 ゼロはルックアップにとって無効なインデックスです。 missing から始めましたが、isMissing(x) は x==0 よりも高価です。
現在、いくつかの REST API に取り組んでおり、そのため連想配列とデフォルト値と同じ値を持つキーの削除私はデフォルト値の使用を避けています/避けるつもりです。 理由は、JMP ユーザー向けのインタラクティブな UI を構築するために JSON 応答を使用しており、キーが「デフォルト値」であってもキーが必要であるためです。 ただし、この投稿の後は、欠落しているキーを確認してみるという遅延オプションの使用をやめると思います。
もっと見る...