Estoy tratando de encontrar una manera eficiente de calcular la SUMA ACUMULADA de 3 días.
Tengo algunos bucles for que funcionan, pero los conjuntos de datos con los que estoy trabajando son ~ 500k filas con muchos más GRUPOS, SUBGRUPOS y columnas de VALOR, por lo que se tarda ~ 30 minutos en calcular. El conjunto de datos de ejemplo es muy sencillo y básico.
Moví los valores a una matriz y eso lo acelera un poco, pero no lo suficiente.
Hay columnas de GRUPO y SUBGRUPO que deben ser SUMA ACUMULATIVA por separado. Además, para cada fila, la SUMA ACUMULADA solo puede ser para FECHA - 3 días. Cualquier cosa anterior a :FECHA[]-3 días, y posterior a :FECHA[] no se utiliza para la SUMA ACUMULATIVA de esa fila.
Tengo la sensación de que una fórmula de columna que use LAG probablemente sea la más rápida, pero no puedo hacer que funcione correctamente.
La velocidad es muy importante.
Intenté subdividir las tablas, y eso lo hizo más lento.
Ver tabla y JSL. Los dos métodos JSL principales funcionan perfectamente, el tercer método JSL no funciona correctamente.
¡Gracias!
[editar] 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
);
Buena atrapada. Cometí un error de novato al no atrapar el caso de la esquina para un subgrupo de 1 fila. Estoy muy contento de que JMP ya no convierta una matriz 1x1 en escalar.
<SPAN class="token comment">//JMP12 converted a 1x1 matrix to a scalar</SPAN>
Esto funciona muy bien. Veo un aumento de velocidad >200 % usando este método con mi conjunto de datos.
Hubo un pequeño error en el que si solo había una fila para un subgrupo (tenía un comentario JSL sobre una matriz 1x1 convertida en escalar). No tengo idea de por qué sucedería esto.
Me aseguré de que estos siguieran siendo una matriz así:
dsub = Matrix(dmat[b :: e]);
vsub = Matrix(vmat[b :: e]);
Esto debería ser un poco más rápido. Es una rutina simple de conteo de compensaciones, depende del poder de las funciones Loc() y matrix.
Elementos a tener en cuenta:
- cambie la ruta o user_id o establezca dt = tabla de datos actual ();
- creando una constante como Drng encuentra En días (3) una vez posiblemente ahorrando encontrarlo 500k veces.Esto depende de cómo JMP optimiza/analiza el código JSL.
- En la línea 30 del guión, usé <= droga para que los resultados de este script coincidan con sus resultados.Sin embargo, considere si esto debería ser un estricto menor que (<). La fila 37 en su tabla suma las filas 33-37, del 19 al 22 de enero, es decir, 4 días. Hay otros casos. Suponga que desea los últimos 30 días de enero, que deberían ser del 2 al 31 de enero.
- :La fecha tiene el formato m/d/y, sin embargo, el valor de la fecha incluye la hora. Si desea avanzar por día calendario (no por hora), comente la línea 30 y elimine el comentario de la línea 31.El valor 86400 = 24*60*60 o En días (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>No tengo idea si esto sería más rápido o más lento, pero fue mucho más simple de conceptualizar para mí (como un programador inexperto).
Suponiendo que la tabla está ordenada por GRUPO, SUBGRUPO y fecha, y que nunca tendría más de 10 puntos de datos en un período de 3 días (puede cambiar esto), esta fórmula funcionaría.
Suma( i = 0, 10,
Si( :GRUPO == Lag( :GRUPO, i ) & :SUBGROUP == Lag( :SUBGROUP, i ) & Diferencia de fecha( Lag( :FECHA, i ), :FECHA, "Día" ) <= 3,
Retraso( :VALOR, i ),
0
)
)
Salud