ローリング 3 日間の累積合計を計算する効率的な方法を見つけようとしています。
いくつかの for ループは機能しますが、扱っているデータ セットは 500,000 行で、さらに多くの GROUPS、SUBGROUPS、VALUE 列があるため、計算に約 30 分かかります。 サンプル データ セットは非常に無駄が省かれ、基本的なものになっています。
値を行列に移動したところ、少し速度が上がりましたが、十分ではありませんでした。
それぞれ個別に CUMULATIVE SUM を計算する GROUP 列と SUBGROUP 列があります。 また、各行の CUMULATIVESUM は DATE (3 日間) のもののみになります。 :DATE[]-3 日前と :DATE[] 以降は、その行の CUMULATIVE SUM には使用されません。
LAG を使用した列式がおそらく最速であると感じていますが、適切に動作させることができません。
スピードは非常に重要です。
テーブルをサブセット化しようとしましたが、それにより速度が遅くなりました。
表とJSLを参照してください。 上の 2 つの JSL メソッドは完全に機能しますが、3 番目の JSL メソッドは適切に機能しません。
ありがとう!
[編集] JMP プロ 12.2.0

dt = Current Data Table();
For( i = 1, i <= N Rows( dt ), i = i + 1,
:CUMULATIVE_SUM_VALUE[i] = :VALUE[i];
For( c = i - 1, c > 0, c = c - 1,
If( :SUBGROUP[c] == :SUBGROUP[i] & :GROUP[c] == :GROUP[i] & (:DATE[i] - :DATE[c]) <= In Days( 3 ),
:CUMULATIVE_SUM_VALUE[i] = :CUMULATIVE_SUM_VALUE[i] + :VALUE[c],
c = -1
)
);
);
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
//1=GROUP, 2=SUBGROUP, 3=DATE, 4=VALUE, 5=CUMULATIVE_SUM_VALUE
TT = dt << Get All Columns As Matrix;
For( i = 1, i <= N Rows( TT ), i = i + 1,
TT[i, 5] = TT[i, 4];
For( c = i - 1, c > 0, c = c - 1,
If( TT[c, 2] == TT[i, 2] & TT[c, 1] == TT[i, 1] & (TT[i, 3] - TT[c, 3]) <= In Days( 3 ),
TT[i, 5] = TT[i, 5] + TT[c, 4],
c = -1
)
);
);
For Each Row( :CUMULATIVE_SUM_VALUE = TT[Row(), 5] );
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
dt << New Column( "CUMULATIVE_SUM_VALUE_2",
Formula(
//:VALUE;
If(
Row() == 1, :VALUE,
:GROUP == Lag( :GROUP, 1 ) & :SUBGROUP == Lag( :SUBGROUP, 1 ) & :DATE - Lag( :DATE, 1 ) <= In Days( 3 ), :VALUE + Lag( :CUMULATIVE_SUM_VALUE, 1 )
,
:GROUP != Lag( :GROUP, 1 ) & :SUBGROUP != Lag( :SUBGROUP, 1 ) & :DATE - Lag( :DATE, 1 ) <= In Days( 3 ), :VALUE
,
:GROUP == Lag( :GROUP, 1 ) & :SUBGROUP == Lag( :SUBGROUP, 1 ) & :DATE - Lag( :DATE, 1 ) > In Days( 3 ), :VALUE
,
:GROUP != Lag( :GROUP, 1 ) | :SUBGROUP != Lag( :SUBGROUP, 1 ), :VALUE
)
),
Eval Formula
);
良いキャッチです。 1列のサブグループのコーナーケースをキャッチできなかった初歩的なミスをしてしまいました。 JMP が 1x1 行列をスカラーに変換しなくなったことをとても嬉しく思います。
<SPAN class="token comment">//JMP12 converted a 1x1 matrix to a scalar</SPAN>
これはうまくいきます。 この方法をデータセットで使用すると、200% を超える速度の向上が見られます。
サブグループに 1 行しかない場合 (スカラーに変換された 1x1 行列に関する JSL コメントがある場合)、小さなバグがありました。 なぜこんなことが起こるのか分かりません。
これらが行列のままであることを確認しました。
dsub = Matrix(dmat[b :: e]);
vsub = Matrix(vmat[b :: e]);
これはもう少し速いはずです。 これは単純なオフセット カウント ルーチンであり、Loc() 関数と行列関数の能力に依存します。
注意事項:
- パスまたは user_id を変更するか、 dt = current data table() を設定します。
- 次のような定数を作成しますdrng見つけます数日で(3) 50 万回見つけて保存できる可能性があります。これは、JMP が JSL コードを最適化/解析する方法によって異なります。
- スクリプトの 30 行目で、次のように使用しました。 <= drngこのスクリプトの結果があなたの結果と一致するようにします。ただし、これを厳密に小さい (<) にする必要があるかどうかを検討してください。 テーブルの行 37 は、行 33 ~ 37、1 月 19 日から 1 月 22 日までの合計、つまり 4 日間を合計します。 他にも例があります。 1 月の最後の 30 日間が必要だとします。これは、Jan02 から Jan31 までになります。
- :日付は m/d/y の形式ですが、日付値には時間が含まれます。 (時刻ではなく) 暦日ごとにロールしたい場合は、30 行目をコメントアウトし、31 行目のコメントを解除します。値 86400 = 24*60*60 または数日で(1)
Names Default to Here(1);
dt = Open("c:\users\<USER_ID>\downloads\cumulative_sum_test_table.jmp");
//This assumes the data table is soted by Group and SubGroup and Date
scol = dt << new column("Sequence", numeric, continuous);
scol << set each value(Row());
ccol = dt << new Column("CUMULATIVE_SUM_VALUE_2", numeric, continuous);
summarize(Data Table(dt), _ttt = By(:GROUP, :SUBGROUP), _ibeg=Min(scol), _iend=Max(scol) );
dt << delete column(scol); //no longer needed
dmat = :DATE << get as matrix;
vmat = :VALUE << get as matrix;
//for each group
drng = In Days(3); //259200
For(i=1, i<=nitems(_ttt[1]), i++,
b = _ibeg[i];
e = _iend[i];
dsub = dmat[ b::e ];
vsub = vmat[ b::e ];
For(j = b, j <= e, j++,
//j=37
k= j-b+1;
//JMP12 converted a 1x1 matrix to a scalar
if(k==1,
ccol[j] = vsub[1]
, //else
idx = loc( 0<= -1 * dsub[1::k] + dmat[j] <= drng ); //should this be < not <=<BR /> //idx = loc( 0<= Floor( (-1 * dsub[1::k] + dmat[j])/86400) <= 3 ); // < ??
ccol[j] = sum(vsub[idx])
); //end if
); // end for j
);
</USER_ID>これがより速いか遅いかは分かりませんが、(経験の浅いスクリプト作成者として)私にとっては概念化がずっと簡単でした。
テーブルが GROUP、SUBGROUP、日付で並べ替えられており、3 日間の期間に 10 を超えるデータ ポイントが存在することはないと仮定すると (これは変更できます)、この数式が役立ちます。
合計( i = 0, 10,
If( :GROUP == Lag( :GROUP, i ) & :SUBGROUP == Lag( :SUBGROUP, i ) & Date Difference( Lag( :DATE, i ), :DATE, "Day" ) <= 3,
ラグ(:VALUE, i)、
0
)
)
乾杯