Optimisation d'un code JSL (lent) basé sur des boucles FOR()Bonjour,
J'ai besoin d'écrire un code pour l'analyse des données de fiabilité.
Plusieurs métriques (metric1, metric2,...metricN) sont mesurées avant le début de l'expérience (REL_T0), puis mesurées à nouveau après chaque événement de chute (DROP1, DROP2,... DROPN…).
Mon objectif est de calculer la valeur de dérive entre REL_T0 et chaque mesure après des événements de chute pour chaque numéro de série et chaque métrique.
Mon code initial est basé sur des boucles FOR() – qui, je le sais, ne sont certainement pas optimales – et fonctionne bien avec de petits ensembles de données.
Néanmoins, je dois optimiser mon code pour qu'il s'exécute plus rapidement avec des ensembles de données plus volumineux.
Actuellement, avec 10 000 lignes et 50 métriques, le processus prend environ 20 minutes, ce qui est bien trop.
J'ai essayé d'organiser les boucles FOR() différemment mais sans impact significatif.
Je suis sûr qu'il existe des moyens d'effectuer ces calculs simples de manière beaucoup plus rapide, en utilisant les commandes propriétaires JSL.
Qu'en pensez-vous?
Le code ci-dessous crée une table factice puis exécute mon code initial pour obtenir de nouvelles colonnes « ∆_metricN » avec la dérive calculée.
J'aimerais faire la même chose en utilisant des commandes JSL au lieu de boucles FOR() qui rendraient mon code ultra-rapide !
Merci beaucoup d'avance.
Prédicateur.
//Create dummy table
RawData = New Table( "RawData",
Add Rows( 20 ),
New Column( "serial_number", Character, set values( {"A","A","A","A","A","A","A","B","B","B","B","B","B","C","C","C","C","C","C","C"})),
New Column( "rel_event", Character, set values( {
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5","DROP6",
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5",
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5", "DROP6"})),
New Column("metric1", Continuous, set values({1,2,3,5,7,10,13,2,4,5,6,8,11,1,2,4,5,5,9,12}) ),
New Column("metric2", Continuous, set values({2,3,4,7,9,14,18,2,3,4,7,9,14,2,5,7,8,9,11,13}) );
);
RawData << save( "$Desktop\RawData.jmp" );
Column(RawData, "rel_event") << Set Property( "Value ordering", {"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5","DROP6"} );
//Create handle for data table to be analyzed
RawData = Current Data Table();
ChosenDataCol = {"metric1", "metric2"};
NumDataCol = N Items( ChosenDataCol );
NumRow = N Row( RawData );
RawDataCol = RawData << get column names;
Show( NumDataCol );
Show( ChosenDataCol );
Show( RawData );
Show( NumRow );
Show( RawDataCol );
//Scan each selected data row for which Deltas must be computed
For( cpt = 1, cpt <= NumDataCol, cpt++,
//Create column to receive delta data
DeltaCol = RawData << New Column( "∆_" || Char( ChosenDataCol[cpt] ), Numeric, "Continuous" );
TargetCol = Column( RawData, ChosenDataCol[cpt] );
serial_numberlist = {}; //List of unique serial_number's within the data table of interest
Show( cpt );
Show( ChosenDataCol[cpt] );
For( i = 1, i <= NumRow, i++,
Currentserial_number = :serial_number[i];
If( Contains( serial_numberlist, Currentserial_number ) == 0,
Insert Into( serial_numberlist, Currentserial_number )
);
);//For i
Show( serial_numberList );
Numserial_number = N Items( serial_numberlist );
Show( Numserial_number );
//Scan data table for each unique serial_number
For( i = 1, i <= Numserial_number, i++,
Currentserial_number = serial_numberList[i];
Currentserial_numberrows = RawData << GetRowsWhere( :serial_number == Currentserial_number ); //matrix of rows containing current serial_number
Show( Currentserial_numberrows );
NumCurrentserial_numberrows = N Row( Currentserial_numberrows );
Show( NumCurrentserial_numberrows );
show(i);
TzeroExist = 0;
TzeroVal = 0;
//identify Time Zero value for each serial_number under consideration
For( j = 1, j <= NumCurrentserial_numberrows, j++,
CurrentRead = :rel_event[Currentserial_numberrows[j]];
Show( CurrentRead );
If( CurrentRead == "REL_T0",
TzeroVal = TargetCol[Currentserial_numberrows[j]];
Show( TzeroVal );
TzeroExist = 1;
);
); //For j
//Calculate Drift
For( j = 1, j <= NumCurrentserial_numberrows, j++,
If( TzeroExist == 1,
DeltaCol[Currentserial_numberrows[j]] = TargetCol[Currentserial_numberrows[j]] - TzeroVal
)
); //For j
);//For i
);//For cpt Modélisation statistique avancéeAutomatisation et scriptsRe : Optimisation d'un code JSL (lent) basé sur des boucles FOR()Merci Jarmo pour votre réponse.
J'aime l'idée d'utiliser For Each Row(), cela simplifie l'ensemble du script.
Mais pour ajouter plus de contexte et rendre le script plus robuste, REL_T0 peut ne pas toujours être la première ligne par numéro de série (la base de données n'interroge pas toujours les données dans le bon ordre dans la table JMP), et la dérive peut également être positive. OU négatif, donc je ne sais pas si Col Min() est bien adapté ici (peut-être que je ne l'ai pas bien lu).
Est-il possible d'ancrer start_row directement à la valeur REL_T0 par numéro de série ?
Re : Optimisation d'un code JSL (lent) basé sur des boucles FOR()Oui, c'est possible avec de petites modifications directement dans ce script. J'ai ajouté une colonne de débogage que vous pouvez supprimer pour montrer qu'elle correspond aux lignes correctes
Names Default To Here(1);
dt = New Table("RawData",
Add Rows(27),
Compress File When Saved(1),
New Column("serial_number",
Character,
"Nominal",
Set Values(
{"A", "A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C", "C", "D", "D", "D", "D", "D", "D", "D"}
)
),
New Column("rel_event",
Character,
"Nominal",
Set Values(
{"REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "REL_T0", "DROP1",
"DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "DROP1", "DROP2", "DROP3", "REL_T0", "DROP4", "DROP5", "DROP6"}
)
),
New Column("metric1",
Numeric,
"Continuous",
Format("Best", 10),
Set Values([1, 2, 3, 5, 7, 10, 13, 2, 4, 5, 6, 8, 11, 1, 2, 4, 5, 5, 9, 12, 1, 2, 4, 5, 5, 9, 12])
),
New Column("metric2",
Numeric,
"Continuous",
Format("Best", 10),
Set Values([2, 3, 4, 7, 9, 14, 18, 2, 3, 4, 7, 9, 14, 2, 5, 7, 8, 9, 11, 13, 2, 5, 7, 8, 9, 11, 13])
)
);
debugcol = dt << New Column("REL_T0_ROW", Numeric, Continuous);
delta1col = dt << New Column("delta1", Numeric, Continuous);
delta2col = dt << New Column("delta2", Numeric, Continuous);
For Each Row(dt,
// find REL_T0 row for current serial_number
start_row = Col Min(If(:rel_event == "REL_T0", Row(), .), :serial_number);
debugcol[Row()] = start_row;
delta1col[Row()] = :metric1 - :metric1[start_row];
delta2col[Row()] = :metric2 - :metric2[start_row];
);

Re : Optimisation d'un code JSL (lent) basé sur des boucles FOR()Merci beaucoup Jarmo!
J'ai essayé cette nouvelle version sur une table de données avec 18 000 lignes et 2 métriques :
- Ancienne méthode : ~5min
- votre méthode : <2min
C'est déjà une formidable amélioration !!!
Merci encore!!
Prédicateur
Re : Optimisation d'un code JSL (lent) basé sur des boucles FOR()Bonjour Hogi,
Merci pour votre suggestion, j'ai vérifié votre message relatif au complément de normalisation. Je garderai certainement cela à l'esprit pour de futurs projets.
Prédicateur
Re : Optimisation d'un code JSL (lent) basé sur des boucles FOR()Des améliorations supplémentaires de la vitesse peuvent probablement être réalisées assez facilement en utilisant des matrices pour effectuer ces calculs. Ajoutant également
dt << begin data update;
avant la boucle et
dt << end data update;
très probablement, cela apportera également des améliorations de vitesse, surtout si votre table n'est pas ouverte de manière invisible/privée car elle "retardera" alors les visuels de la table sont mis à jour.
Re : Optimisation d'un code JSL (lent) basé sur des boucles FOR()La normalisation des données de mesure est une tâche courante dans l'analyse des données.
L' interface graphique de normalisation propose de nombreuses options pour ce faire.
Dans l'interface graphique, l'utilisateur peut spécifier de nombreux paramètres différents - et le complément génère le code correspondant.
Les formules générées sont basées sur des agrégations Col ... , comme Col Min, Col Max, Col Median ...
autres options:

Pour ce cas d'application, vous pouvez utiliser "first" (basé sur la logique ColMin(row()) )
... ou spécifiez la référence via "utiliser un sous-ensemble pour la normalisation" [pour les cas où les valeurs de référence ne sont pas les "premières"] :

Re: Optimisation d'un code JSL (lent) basé sur des boucles FOR()Une chose qui fournira (généralement) une accélération est de supprimer toutes les impressions des boucles, mais dans ce cas, la réécriture sans ces boucles est plus rapide et de ne pas utiliser la boucle for lors du passage sur la table de données (pour chaque ligne est une meilleure option).
Voici une option qui, espérons-le, donnera des résultats plus rapides (cela peut également être transformé en formule et cela devrait toujours être assez rapide si nécessaire). Il suppose que les données sont ordonnées de telle manière que REL_T0 soit toujours la première ligne pour chaque numéro de série, si ce n'est pas le cas, il faudra un petit changement
Names Default To Here(1);
//Create dummy table
RawData = New Table("RawData",
Add Rows(20),
New Column("serial_number",
Character,
set values({"A", "A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C", "C"})
),
New Column("rel_event",
Character,
set values(
{"REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "REL_T0", "DROP1",
"DROP2", "DROP3", "DROP4", "DROP5", "DROP6"}
)
),
New Column("metric1", Continuous, set values({1, 2, 3, 5, 7, 10, 13, 2, 4, 5, 6, 8, 11, 1, 2, 4, 5, 5, 9, 12})),
New Column("metric2", Continuous, set values({2, 3, 4, 7, 9, 14, 18, 2, 3, 4, 7, 9, 14, 2, 5, 7, 8, 9, 11, 13}))
);
delta1col = RawData << New Column("delta1", Numeric, Continuous);
delta2col = RawData << New Column("delta2", Numeric, Continuous);
For Each Row(RawData,
// find REL_T0 row for current serial_number
start_row = Col Min(Row(), :serial_number);
delta1col[Row()] = :metric1 - :metric1[start_row];
delta2col[Row()] = :metric2 - :metric2[start_row];
);
