Optimierung eines (langsamen) JSL-Codes basierend auf FOR()-SchleifenHallo,
Ich muss einen Code zur Zuverlässigkeitsdatenanalyse schreiben.
Vor Beginn des Experiments (REL_T0) werden mehrere Metriken (Metrik1, Metrik2, ... MetrikN) gemessen und dann nach jedem Drop-Ereignis (DROP1, DROP2, ... DROPN …) erneut gemessen.
Mein Ziel besteht darin, den Driftwert zwischen REL_T0 und jeder Maßnahme nach Drop-Ereignissen für jede Seriennummer und jede Metrik zu berechnen.
Mein ursprünglicher Code basiert auf FOR()-Schleifen – von denen ich weiß, dass sie definitiv nicht optimal sind – und funktioniert bei kleinen Datensätzen gut.
Trotzdem muss ich meinen Code optimieren, damit er bei größeren Datensätzen schneller läuft.
Derzeit dauert es bei 10.000 Zeilen und 50 Metriken etwa 20 Minuten, bis der Vorgang abgeschlossen ist, was viel zu lange ist.
Ich habe versucht, die FOR()-Schleifen anders zu organisieren, jedoch ohne nennenswerte Auswirkungen.
Ich bin sicher, dass es Möglichkeiten gibt, diese einfachen Berechnungen mithilfe proprietärer JSL-Befehle viel schneller durchzuführen.
Was denkt ihr?
Der folgende Code erstellt eine Dummy-Tabelle und führt dann meinen ursprünglichen Code aus, um am Ende neue Spalten „∆_metricN“ mit der berechneten Drift zu erhalten.
Ich möchte dasselbe mit JSL-Befehlen anstelle von FOR()-Schleifen tun, wodurch mein Code blitzschnell würde!
Vielen Dank im Voraus.
Prediger.
//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 Erweiterte statistische ModellierungAutomatisierung und SkriptingBetreff: Optimierung eines (langsamen) JSL-Codes basierend auf FOR()-SchleifenDanke, Jarmo, für deine Antwort.
Mir gefällt die Idee, For Each Row() zu verwenden, es vereinfacht das gesamte Skript.
Um jedoch mehr Kontext hinzuzufügen und das Skript robuster zu machen: REL_T0 ist möglicherweise nicht immer die erste Zeile pro Seriennummer (die Datenbank fragt die Daten in der JMP-Tabelle nicht immer in der richtigen Reihenfolge ab) und auch die Abweichung kann positiv ODER negativ sein. Daher weiß ich nicht, ob Col Min() hier gut geeignet ist (vielleicht lese ich es falsch).
Ist es möglich, start_row direkt am REL_T0-Wert pro Seriennummer zu verankern?
Betreff: Optimierung eines (langsamen) JSL-Codes basierend auf FOR()-SchleifenJa, das ist mit kleinen Änderungen direkt an diesem Skript möglich. Ich habe eine Debug-Spalte hinzugefügt, die Sie entfernen können, um zu zeigen, dass sie den richtigen Zeilen entspricht.
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];
);

Betreff: Optimierung eines (langsamen) JSL-Codes basierend auf FOR()-SchleifenVielen Dank, Jarmo!
Ich habe diese neue Version an einer Datentabelle mit 18.000 Zeilen und 2 Metriken ausprobiert:
- Alte Methode: ~5min
- Ihre Methode: <2min
Es ist schon eine enorme Verbesserung!!!
Danke noch einmal!!
Prediger
Betreff: Optimierung eines (langsamen) JSL-Codes basierend auf FOR()-SchleifenHallo Hogi,
Vielen Dank für Ihren Vorschlag. Ich habe Ihren Beitrag zum Normalisierungs-Add-In gelesen. Ich werde ihn bei zukünftigen Projekten auf jeden Fall berücksichtigen.
Prediger
Betreff: Optimierung eines (langsamen) JSL-Codes basierend auf FOR()-SchleifenWeitere Geschwindigkeitsverbesserungen können höchstwahrscheinlich recht einfach durch die Verwendung von Matrizen zur Durchführung dieser Berechnungen erreicht werden. Auch das Hinzufügen
dt << begin data update;
vor der Schleife und
dt << end data update;
wird höchstwahrscheinlich auch einige Geschwindigkeitsverbesserungen bringen, insbesondere wenn Ihre Tabelle nicht als unsichtbar/privat geöffnet ist, da es sonst zu einer „Verzögerung“ kommt, wenn die visuellen Elemente in der Tabelle aktualisiert werden.
Betreff: Optimierung eines (langsamen) JSL-Codes basierend auf FOR()-SchleifenDie Normalisierung von Messdaten ist eine häufige Aufgabe in der Datenanalyse.
Die Normalisierungs-GUI bietet hierfür zahlreiche Optionen.
In der GUI kann der Benutzer zahlreiche verschiedene Einstellungen vornehmen – und das AddIn generiert den entsprechenden Code.
Die generierten Formeln basieren auf Col...- Aggregationen wie Col Min, Col Max, Col Median ...
andere Optionen:

Für diesen Anwendungsfall könnten Sie „first“ verwenden (basierend auf der ColMin(row())- Logik).
... oder geben Sie die Referenz über „Teilmenge zur Normalisierung verwenden“ an [für Fälle, in denen die Referenzwerte nicht die „ersten“ sind]:

Betreff: Optimierung eines (langsamen) JSL-Codes basierend auf FOR()-SchleifenEine Möglichkeit, (im Allgemeinen) die Geschwindigkeit zu steigern, besteht darin, alle Ausdrucke aus Schleifen zu entfernen. In diesem Fall ist es jedoch schneller, die Datentabelle ohne diese Schleifen neu zu schreiben und beim Durchlaufen der Datentabelle keine For-Schleife zu verwenden (für jede Zeile ist die bessere Option).
Hier ist eine Option, die hoffentlich schnellere Ergebnisse liefert (dies kann auch in eine Formel umgewandelt werden und sollte bei Bedarf immer noch ziemlich schnell sein). Es wird davon ausgegangen, dass die Daten so angeordnet sind, dass REL_T0 immer die erste Zeile für jede Seriennummer ist. Wenn dies nicht der Fall ist, ist eine kleine Änderung erforderlich
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];
);
