我正在尝试找到一种有效的方法来计算滚动 3 天累积总和。
我有一些有效的 for 循环,但我正在处理的数据集大约有 500k 行,其中有更多的 GROUPS、SUBGROUPS 和 VALUE 列,因此计算需要大约 30 分钟。 示例数据集非常精简且基础。
我将这些值移入矩阵中,这加快了速度,但还不够。
有一个 GROUP 和 SUBGROUP 列,它们是彼此分开的 CUMULATIVE SUM。 此外,对于每一行,CUMULATIVESUM 只能是 DATE - 3 天。 :DATE[]-3 天之前和 :DATE[] 之后的任何内容都不会用于该行的 CUMULATIVE SUM。
我有一种感觉,使用 LAG 的列公式可能是最快的,但我无法让它正常工作。
速度非常重要。
我尝试过对表进行子集化,但这只会使其速度变慢。
参见表和 JSL。 前两个 JSL 方法工作正常,第三个 JSL 方法无法正常工作。
谢谢你!
[编辑] JMP Pro 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或set dt =当前数据表();
- 创建一个常量,例如博士发现几天内(3)一次可能节省查找50万次。这取决于 JMP 如何优化/解析 JSL 代码。
- 在脚本的第 30 行,我使用了<= 博士以便该脚本的结果与您的结果相匹配。但是,请考虑这是否应该是严格小于 (<)。 表中的第 37 行汇总了 1 月 19 日到 1 月 22 日的第 33-37 行,即 4 天。 还有其他例子。 假设您想要 1 月的最后 30 天,即 1 月 2 日到 1 月 31 日。
- :日期格式为 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,
如果( :GROUP == Lag( :GROUP, i ) & :SUBGROUP == Lag( :SUBGROUP, i ) & Date Difference( Lag( :DATE, i ), :DATE, "Day" ) <= 3,
Lag( :VALUE, i ),
0
)
)
干杯