Rolling 3-day CUMULATIVE SUM을 계산하는 효율적인 방법을 찾고 있습니다.
작동하는 for 루프가 몇 개 있지만 작업 중인 데이터 세트는 더 많은 GROUPS, SUBGROUPS 및 VALUE 열이 있는 ~500k 행이므로 계산하는 데 ~30분이 걸립니다. 예제 데이터 세트는 매우 간단하고 기본적입니다.
나는 값을 행렬로 옮겼고 속도가 약간 빨라졌지만 충분하지는 않았습니다.
서로 별도로 CUMULATIVE SUM이 될 GROUP 및 SUBGROUP 열이 있습니다. 또한 각 행에 대해 CUMULATIVESUM은 DATE - 3일 동안만 가능합니다. :DATE[]-3일 이전 및 :DATE[] 이후는 해당 행의 CUMULATIVE SUM에 사용되지 않습니다.
LAG를 사용하는 열 수식이 가장 빠를 것 같은 느낌이 들지만 제대로 작동하지 않습니다.
속도는 매우 중요합니다.
나는 테이블을 부분 집합화하려고 시도했지만 속도가 느려졌습니다.
테이블 및 JSL을 참조하십시오. 상위 2개의 JSL 방법은 완벽하게 작동하고 세 번째 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% 이상 증가합니다.
하위 그룹에 대한 행이 하나만 있는 경우 작은 버그가 있었습니다(스칼라로 변환된 1x1 행렬에 대한 JSL 주석이 있음). 왜 이런 일이 일어날 지 모르겠습니다.
나는 이것이 다음과 같이 행렬로 유지되도록 했습니다.
dsub = Matrix(dmat[b :: e]);
vsub = Matrix(vmat[b :: e]);
이것은 조금 더 빠를 것입니다. 이것은 간단한 오프셋 계산 루틴이며 Loc() 및 행렬 함수의 기능에 의존합니다.
참고할 항목:
- 경로 또는 user_id를 변경하거나 dt = 현재 데이터 테이블()을 설정합니다.
- 같은 상수 생성 drng 발견하다 며칠 후 (3) 한 번에 50만 번 찾는 것을 절약할 수 있습니다.이는 JMP가 JSL 코드를 최적화/파싱하는 방법에 따라 다릅니다.
- 스크립트의 30번째 줄에서 <= 드링 이 스크립트의 결과가 귀하의 결과와 일치하도록 합니다.그러나 이것이 보다 작음(<)이어야 하는지 여부를 고려하십시오. 테이블의 37행은 1월 19일부터 1월 22일까지, 즉 4일 동안 33-37행을 합산합니다. 다른 경우가 있습니다. 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>이 방법이 더 빠를지 느릴지는 모르겠지만, (스크립트 작성 경험이 부족한) 제게는 개념적으로 훨씬 간단했습니다.
테이블이 그룹, 하위 그룹 및 날짜별로 정렬되어 있고 3일 동안 데이터 포인트가 10개를 넘지 않는다고 가정하면(이 조건은 변경 가능), 다음 수식이 제대로 작동할 것입니다.
합계( i = 0, 10,
만약 ( :GROUP == Lag( :GROUP, i ) & :SUBGROUP == Lag( :SUBGROUP, i ) & 날짜 차이( Lag( :DATE, i ), :DATE, "Day" ) <= 3,
지연( :VALUE, i ),
0
)
)
건배