Sto cercando di trovare un modo efficiente per calcolare la SOMMA CUMULATIVA di 3 giorni a rotazione.
Ne ho alcuni per loop che funzionano, ma i set di dati con cui sto lavorando sono ~ 500.000 righe con molte più colonne GROUPS, SUBGROUPS e VALUE, quindi il calcolo richiede circa 30 minuti. Il set di dati di esempio è molto ridotto ed essenziale.
Ho spostato i valori in una matrice e questo lo velocizza un po', ma non abbastanza.
Esistono colonne GRUPPO e SOTTOGRUPPO che devono essere SOMMA CUMULATIVA separatamente l'una dall'altra. Anche per ogni riga il CUMULATIVESUM può essere solo per DATE - 3 giorni. Qualsiasi cosa prima di :DATE[]-3 giorni e dopo :DATE[] non viene utilizzata per la SOMMA CUMULATIVA di quella riga.
Ho la sensazione che una formula di colonna che utilizza LAG sarà probabilmente più veloce, ma non riesco a farlo funzionare correttamente.
La velocità è molto importante.
Ho provato a sottoimpostare le tabelle e questo l'ha reso più lento.
Vedi tabella e JSL. I primi due metodi JSL funzionano perfettamente, il terzo metodo JSL non funziona correttamente.
Grazie!
[modifica] 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
);
Buona pesca. Ho commesso un errore da principiante non prendendo il caso d'angolo per un sottogruppo di 1 riga. Sono così felice che JMP non converta più una matrice 1x1 in uno scalare.
<SPAN class="token comment">//JMP12 converted a 1x1 matrix to a scalar</SPAN>
Funziona alla grande. Sto vedendo un aumento della velocità> 200% utilizzando questo metodo con il mio set di dati.
C'era un piccolo bug in cui se c'era solo una riga per un sottogruppo (avevi un commento JSL su una matrice 1x1 convertita in scalare). Non ho idea del perché questo accadrebbe.
Mi sono assicurato che rimanessero una matrice in questo modo:
dsub = Matrix(dmat[b :: e]);
vsub = Matrix(vmat[b :: e]);
Questo dovrebbe essere un po' più veloce. È una semplice routine di conteggio dell'offset, dipende dalla potenza delle funzioni Loc() e di matrice.
Elementi da notare:
- cambia il percorso o user_id o imposta dt = tabella dati corrente ();
- creando un mi piace costante drng trova Nei giorni (3) una volta possibilmente salvata trovandola 500k volte.Questo dipende da come JMP ottimizza/analizza il codice JSL.
- Nella riga 30 della sceneggiatura, ho usato <= drng in modo che i risultati di questo script corrispondano ai tuoi risultati.Tuttavia, considerare se questo dovrebbe essere un rigoroso minore di (<). La riga 37 nella tabella somma le righe 33-37, dal 19 gennaio al 22 gennaio, ovvero 4 giorni. Ci sono altri casi. Supponiamo di volere gli ultimi 30 giorni di gennaio, che dovrebbero essere da Jan02 a Jan31.
- :Date è formattato come m/g/a, tuttavia, il valore della data include l'ora. Se si desidera scorrere in base al giorno del calendario (non all'ora), commentare la riga 30 e decommentare la riga 31.Il valore 86400 = 24*60*60 o Nei giorni (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>Non ho idea se questo sarebbe stato più veloce o più lento, ma per me (in quanto sceneggiatore inesperto) è stato molto più semplice da concettualizzare.
Supponendo che la tabella sia ordinata per GRUPPO, SOTTOGRUPPO e data e che non si abbiano mai più di 10 punti dati in un periodo di 3 giorni (è possibile modificare questa impostazione), questa formula dovrebbe risolvere il problema.
Sommatoria (i = 0, 10,
Se( :GRUPPO == Lag( :GRUPPO, i ) & :SOTTOGRUPPO == Lag( :SOTTOGRUPPO, i ) & Differenza data( Lag( :DATA, i ), :DATA, "Giorno" ) <= 3,
Lag( :VALORE, i ),
0
)
)
Saluti