J'essaie de trouver un moyen efficace de calculer la SOMME CUMULÉE sur 3 jours.
J'ai des boucles for qui fonctionnent, mais les ensembles de données avec lesquels je travaille sont ~ 500 000 lignes avec beaucoup plus de colonnes GROUPS, SUBGROUPS et VALUE, il faut donc environ 30 minutes pour calculer. L'ensemble de données d'exemple est très dépouillé et basique.
J'ai déplacé les valeurs dans une matrice et cela l'accélère un peu, mais pas assez.
Il y a des colonnes GROUP et SUBGROUP qui doivent être CUMULATIVE SUM séparément les unes des autres. De plus, pour chaque ligne, le CUMULATIVESUM ne peut être que pour DATE - 3 jours. Tout ce qui se trouve avant :DATE[]-3 jours et après :DATE[] n'est pas utilisé pour la SOMME CUMULÉE de cette ligne.
J'ai l'impression qu'une formule de colonne utilisant LAG sera probablement la plus rapide, mais je n'arrive pas à la faire fonctionner correctement.
La vitesse est très importante.
J'ai essayé de créer des sous-ensembles de tables, ce qui l'a rendu plus lent.
Voir tableau et JSL. Les deux premières méthodes JSL fonctionnent parfaitement, la troisième méthode JSL ne fonctionne pas correctement.
Merci!
[modifier] 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
);
Bonne prise. J'ai fait une erreur de recrue en n'attrapant pas le cas du coin pour un sous-groupe d'une rangée. Je suis tellement content que JMP ne convertisse plus une matrice 1x1 en scalaire.
<SPAN class="token comment">//JMP12 converted a 1x1 matrix to a scalar</SPAN>
Cela fonctionne très bien. Je constate une augmentation de vitesse > 200 % en utilisant cette méthode avec mon jeu de données.
Il y avait un petit bogue où s'il n'y avait qu'une seule ligne pour un sous-groupe (vous aviez un commentaire JSL sur une matrice 1x1 convertie en scalaire). Je n'ai aucune idée pourquoi cela arriverait.
Je me suis assuré que ceux-ci resteraient une matrice comme suit:
dsub = Matrix(dmat[b :: e]);
vsub = Matrix(vmat[b :: e]);
Cela devrait être un peu plus rapide. Il s'agit d'une simple routine de comptage de décalage, qui repose sur la puissance des fonctions Loc() et matricielles.
Éléments à noter :
- changer le chemin ou user_id ou définir dt = table de données actuelle ();
- créer une constante comme Drng trouve En jours (3) une fois peut-être en économisant le trouver 500k fois.Cela dépend de la manière dont JMP optimise/analyse le code JSL.
- À la ligne 30 du script, j'ai utilisé <= drng afin que les résultats de ce script correspondent à vos résultats.Cependant, considérez si cela doit être strictement inférieur à (<). La ligne 37 de votre tableau additionne les lignes 33 à 37, du 19 janvier au 22 janvier, soit 4 jours. Il y a d'autres cas. Supposons que vous souhaitiez les 30 derniers jours de janvier, c'est-à-dire du 02 janvier au 31 janvier.
- :La date est au format m/j/a, cependant, la valeur de la date inclut l'heure. Si vous vouliez rouler par jour calendaire (et non par heure), commentez la ligne 30 et décommentez la ligne 31.La valeur 86400 = 24*60*60 ou En jours (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>Je n'ai aucune idée si ce serait plus rapide ou plus lent, mais c'était beaucoup plus simple à conceptualiser pour moi (en tant que programmeur inexpérimenté).
En supposant que le tableau soit trié par GROUPE, SOUS-GROUPE et date, et que vous n'ayez jamais plus de 10 points de données sur une période de 3 jours (vous pouvez modifier cela), cette formule fera l'affaire.
Sommation( i = 0, 10,
Si( :GROUP == Lag( :GROUP, i ) & :SUBGROUP == Lag( :SUBGROUP, i ) & Date Difference( Lag( :DATE, i ), :DATE, "Day" ) <= 3,
Retard( :VALEUR, i ),
0
)
)
Acclamations