FOR() ループに基づく (遅い) JSL コードの最適化こんにちは、
信頼性データ分析用のコードを書く必要があります。
実験の開始前 (REL_T0) にいくつかのメトリック (metric1、metric2、...metricN) が測定され、その後、各ドロップ イベント (DROP1、DROP2、... DROPN…) の後に再度測定されます。
私の目標は、各シリアル番号と各メトリックのドロップ イベント後の REL_T0 と各測定値間のドリフト値を計算することです。
私の最初のコードは FOR() ループに基づいていますが、これは明らかに最適ではないことはわかっていますが、小さなデータセットでは問題なく動作します。
それでも、より大きなデータセットでより高速に実行できるようにコードを最適化する必要があります。
現在、10,000 行と 50 個のメトリックがある場合、プロセスが完了するまでに約 20 分かかりますが、これは長すぎます。
FOR() ループを別の方法で構成しようとしましたが、大きな影響はありませんでした。
JSL 独自のコマンドを使用すれば、これらの単純な計算をはるかに高速に実行できる方法が必ずあるはずです。
皆さんはどう思いますか?
以下のコードはダミー テーブルを作成し、最初のコードを実行して、計算されたドリフトを含む新しい列「∆_metricN」を作成します。
FOR() ループの代わりに JSL コマンドを使用して同じことを実行し、コードを非常に高速化したいと思います。
よろしくお願いします。
説教者。
//Create dummy table
RawData = New Table( "RawData",
Add Rows( 20 ),
New Column( "serial_number", Character, set values( {"A","A","A","A","A","A","A","B","B","B","B","B","B","C","C","C","C","C","C","C"})),
New Column( "rel_event", Character, set values( {
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5","DROP6",
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5",
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5", "DROP6"})),
New Column("metric1", Continuous, set values({1,2,3,5,7,10,13,2,4,5,6,8,11,1,2,4,5,5,9,12}) ),
New Column("metric2", Continuous, set values({2,3,4,7,9,14,18,2,3,4,7,9,14,2,5,7,8,9,11,13}) );
);
RawData << save( "$Desktop\RawData.jmp" );
Column(RawData, "rel_event") << Set Property( "Value ordering", {"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5","DROP6"} );
//Create handle for data table to be analyzed
RawData = Current Data Table();
ChosenDataCol = {"metric1", "metric2"};
NumDataCol = N Items( ChosenDataCol );
NumRow = N Row( RawData );
RawDataCol = RawData << get column names;
Show( NumDataCol );
Show( ChosenDataCol );
Show( RawData );
Show( NumRow );
Show( RawDataCol );
//Scan each selected data row for which Deltas must be computed
For( cpt = 1, cpt <= NumDataCol, cpt++,
//Create column to receive delta data
DeltaCol = RawData << New Column( "∆_" || Char( ChosenDataCol[cpt] ), Numeric, "Continuous" );
TargetCol = Column( RawData, ChosenDataCol[cpt] );
serial_numberlist = {}; //List of unique serial_number's within the data table of interest
Show( cpt );
Show( ChosenDataCol[cpt] );
For( i = 1, i <= NumRow, i++,
Currentserial_number = :serial_number[i];
If( Contains( serial_numberlist, Currentserial_number ) == 0,
Insert Into( serial_numberlist, Currentserial_number )
);
);//For i
Show( serial_numberList );
Numserial_number = N Items( serial_numberlist );
Show( Numserial_number );
//Scan data table for each unique serial_number
For( i = 1, i <= Numserial_number, i++,
Currentserial_number = serial_numberList[i];
Currentserial_numberrows = RawData << GetRowsWhere( :serial_number == Currentserial_number ); //matrix of rows containing current serial_number
Show( Currentserial_numberrows );
NumCurrentserial_numberrows = N Row( Currentserial_numberrows );
Show( NumCurrentserial_numberrows );
show(i);
TzeroExist = 0;
TzeroVal = 0;
//identify Time Zero value for each serial_number under consideration
For( j = 1, j <= NumCurrentserial_numberrows, j++,
CurrentRead = :rel_event[Currentserial_numberrows[j]];
Show( CurrentRead );
If( CurrentRead == "REL_T0",
TzeroVal = TargetCol[Currentserial_numberrows[j]];
Show( TzeroVal );
TzeroExist = 1;
);
); //For j
//Calculate Drift
For( j = 1, j <= NumCurrentserial_numberrows, j++,
If( TzeroExist == 1,
DeltaCol[Currentserial_numberrows[j]] = TargetCol[Currentserial_numberrows[j]] - TzeroVal
)
); //For j
);//For i
);//For cpt 高度な統計モデリング自動化とスクリプトRe: FOR() ループに基づく (遅い) JSL コードの最適化返信ありがとうございます、Jarmoさん。
For Each Row() を使用するというアイデアは気に入っています。これにより、スクリプト全体が簡素化されます。
しかし、より多くのコンテキストを追加してスクリプトをより堅牢にするために、REL_T0 は必ずしもシリアル番号ごとの最初の行ではない可能性があり (データベースは必ずしも JMP テーブル内の正しい順序でデータを照会するわけではありません)、またドリフトは正または負になる可能性があるため、Col Min() がここで適切かどうかはわかりません (正しく読んでいないのかもしれません)。
シリアル番号ごとにstart_rowを REL_T0 値に直接固定することは可能ですか?
Re: FOR() ループに基づく (遅い) JSL コードの最適化はい、スクリプトに直接小さな変更を加えることで可能です。正しい行と一致していることを示すために削除できるデバッグ列を追加しました。
Names Default To Here(1);
dt = New Table("RawData",
Add Rows(27),
Compress File When Saved(1),
New Column("serial_number",
Character,
"Nominal",
Set Values(
{"A", "A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C", "C", "D", "D", "D", "D", "D", "D", "D"}
)
),
New Column("rel_event",
Character,
"Nominal",
Set Values(
{"REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "REL_T0", "DROP1",
"DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "DROP1", "DROP2", "DROP3", "REL_T0", "DROP4", "DROP5", "DROP6"}
)
),
New Column("metric1",
Numeric,
"Continuous",
Format("Best", 10),
Set Values([1, 2, 3, 5, 7, 10, 13, 2, 4, 5, 6, 8, 11, 1, 2, 4, 5, 5, 9, 12, 1, 2, 4, 5, 5, 9, 12])
),
New Column("metric2",
Numeric,
"Continuous",
Format("Best", 10),
Set Values([2, 3, 4, 7, 9, 14, 18, 2, 3, 4, 7, 9, 14, 2, 5, 7, 8, 9, 11, 13, 2, 5, 7, 8, 9, 11, 13])
)
);
debugcol = dt << New Column("REL_T0_ROW", Numeric, Continuous);
delta1col = dt << New Column("delta1", Numeric, Continuous);
delta2col = dt << New Column("delta2", Numeric, Continuous);
For Each Row(dt,
// find REL_T0 row for current serial_number
start_row = Col Min(If(:rel_event == "REL_T0", Row(), .), :serial_number);
debugcol[Row()] = start_row;
delta1col[Row()] = :metric1 - :metric1[start_row];
delta2col[Row()] = :metric2 - :metric2[start_row];
);

Re: FOR() ループに基づく (遅い) JSL コードの最適化本当にありがとう、Jarmo!
この新しいバージョンを、18000 行と 2 つのメトリックを含むデータ テーブルで試しました。
- 旧方式: 約5分
- あなたの方法: <2分
すでに素晴らしい改善です!!!
再度、感謝します!!
説教者
Re: FOR() ループに基づく (遅い) JSL コードの最適化ホギさん、こんにちは。
ご提案ありがとうございます。正規化アドインに関する投稿を確認しました。今後のプロジェクトでは必ず考慮に入れておきます。
説教者
Re: FOR() ループに基づく (遅い) JSL コードの最適化さらなる速度向上は、これらの計算を実行する行列を使用することで、おそらく非常に簡単に実現できるでしょう。
dt << begin data update;
ループの前に
dt << end data update;
おそらく速度もいくらか向上するでしょう。特に、テーブルが非表示/プライベートとして開かれていない場合は、テーブル内のビジュアルが更新されるまでに「遅延」が発生します。
Re: FOR() ループに基づく (遅い) JSL コードの最適化測定データの正規化は、データ分析における一般的なタスクです。
正規化 GUI には、これを実行するための多くのオプションが用意されています。
GUI では、ユーザーはさまざまな設定を指定でき、アドインは対応するコードを生成します。
生成された数式は、 Col Min、Col Max、Col MedianなどのCol ...集計に基づいています。
その他のオプション:

このアプリケーションの場合、「first」( ColMin(row())ロジックに基づく)を使用できます。
... または、「正規化にサブセットを使用する」を使用して参照を指定します [参照値が「最初の」値ではない場合]。

Re: FOR() ループに基づく (遅い) JSL コードの最適化一般的に速度を向上させる方法の 1 つは、ループからすべての出力を削除することですが、この場合は、それらのループなしで書き直す方が高速であり、データ テーブルを調べるときに for ループを使用しません (各行の方が良いオプションです)。
より高速な結果が得られると思われるオプションが 1 つあります (これを数式に変換することもできますが、必要に応じてかなり高速になるはずです)。これは、データが REL_T0 が常に各シリアル番号の最初の行になるように順序付けられていることを前提としています。そうでない場合は、小さな変更が必要になります。
Names Default To Here(1);
//Create dummy table
RawData = New Table("RawData",
Add Rows(20),
New Column("serial_number",
Character,
set values({"A", "A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C", "C"})
),
New Column("rel_event",
Character,
set values(
{"REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "REL_T0", "DROP1",
"DROP2", "DROP3", "DROP4", "DROP5", "DROP6"}
)
),
New Column("metric1", Continuous, set values({1, 2, 3, 5, 7, 10, 13, 2, 4, 5, 6, 8, 11, 1, 2, 4, 5, 5, 9, 12})),
New Column("metric2", Continuous, set values({2, 3, 4, 7, 9, 14, 18, 2, 3, 4, 7, 9, 14, 2, 5, 7, 8, 9, 11, 13}))
);
delta1col = RawData << New Column("delta1", Numeric, Continuous);
delta2col = RawData << New Column("delta2", Numeric, Continuous);
For Each Row(RawData,
// find REL_T0 row for current serial_number
start_row = Col Min(Row(), :serial_number);
delta1col[Row()] = :metric1 - :metric1[start_row];
delta2col[Row()] = :metric2 - :metric2[start_row];
);
