Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernEine aktuelle Community-Diskussion Re: Skript zum Suchen und Ersetzen eines numerischen Werts durch einen fehlenden Wert in der gesamten Tabelle hatte einen sehr netten eintrag zur verfügung gestellt von @SDF1 die als Lösung ausgewählt wurde
names default to here(1);
dt=current data table();
For( i = 1, i <= N Rows( dt ), i++,
For( l = 1, l <= N Cols( dt ), l++,
If( Column( l )[i] == 999,
Column( l )[i] = .
)
)
);
Als ich mir die Lösung ansah, kam mir eine alte Lehre in den Sinn, die grob besagte: „Durchlaufe keine Datentabelle.Looping ist langsam. Verwenden Sie die integrierten Funktionen von JMP"
Also fing ich an zu modifizieren @SDF1 Code.
Ich habe festgestellt, dass mein Fallback-Standard "Get Rows Where()" schneller sein würde.Immerhin erlaubt es, alle gefundenen Zeilen mit einer Zuweisungsanweisung zu setzen, nachdem das "sehr schnelle" Get Rows Where() die Zeilen gefunden hat.
Eine andere Lösung, die ich gesehen habe geschoben Von der Community empfohlen wird die Recode-Funktion in JMP.
So:
Hypothese: Die eingebauten JMP-Funktionen von Get Rows Where und Recode sind schneller als Looping nach unten und über eine ganze Datentabelle.
Ich habe die Semiconductor Capability-Datentabelle als Tabelle mit guter Größe ausgewählt, 132 Spalten und 1455 Zeilen
Ich habe ein kleines Skript geschrieben, das 10 zufällige Werte in jeder Spalte auf 999 setzt.
names default to here(1);
dt=current data table();
for(i=5,i<=ncols(dt),i++,
if(col stddev(ascolumn(dt,i))!=0,
for(k=1,k<=10,k++,
column(dt,i)[randominteger(1,1455)]=999;
)
));
Dies ist der ursprüngliche Wert, der in der oben erwähnten Diskussion geändert werden soll.Die 999 soll von den Skripten auf fehlende (.) geändert werden.
Methode:
- Starten Sie JMP 16.
- Lesen Sie die Datentabelle zur Halbleiterfähigkeit
- Führen Sie das Skript aus, um die 999 Werte in jeder Spalte festzulegen
- Wiederholen Sie die obigen 2 Schritte für jede der 3 Methoden und codieren Sie die Zeit auf die Millisekunde genau neu.
- Wiederholen Sie die obigen Schritte für JMP15
Hier ist das Skript für den Recode
Names Default To Here( 1 );
dt = Current Data Table();
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
dt << Recode Column(
As Column( dt, i ),
{If( _rcNow == 999, ., _rcNow )},
Target Column( As Column( dt, i ) )
)
);
show(hptime()-start)
Der Code für Get Rows Where
names default to here(1);
dt=current data table();
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[dt<<GET rows="" where=""></GET>
Und hier sind die überraschende Ergebnisse

Die For-Schleife gewinnt zweifellos.Und aufgrund der Ergebnisse sollte Get Rows Where sparsam verwendet werden.
Kommentare und Korrekturen sind willkommen.......
DatentabelleSkripterstellungBetreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernJa ich verstehe @brady_brady . Man könnte dies jedoch tun:
NamesDefaultToHere(1);
// Make an example table with some rogue '999' values
nr = 10; // Number of rows
nc = 10; // Number of columns
p = 0.1; // Fraction of rogue values
val = 999; // Rogue value
m = J(nr, nc, RandomInteger(1, 10));
m = Shape(m, nr*nc, 1);
m[RandomIndex(nr*nc, Floor(nr*nc*p))] = val;
m = Shape(m, nr, nc);
dt = AsTable(m);
dt << setName("Rogue Values");
Wait(3);
// Set rogue values to '.'
mask = J(nr*nc, 1, 1);
mask[Loc(dt[0,0] == val)] = .;
mask = Shape(mask, nr, nc);
dt[0,0] = mask :*</img> dt[0,0];
Aber ich bezweifle sehr, dass dies schneller wäre als das, was Sie bereits haben, nicht zuletzt wegen der elementweisen Multiplikation.
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernSie haben den Vergleich hier etwas falsch gemacht - die Funktion "Get Rows Where" wertet ihren Ausdruck in jeder Zeile aus, daher müssen Sie alle überflüssigen Suchen minimieren, bevor Sie die Schleife ausführen - insbesondere ist Ihr Iterator extern und es müssen Sie einige Scope Walking tun, um es in jeder Reihe zu finden.Außerdem wertet es die AsColumn-Funktion für jede Zeile aus und sucht jedes Mal nach 'dt' und 'i'.Das ist _sehr_ teuer.Der richtige Weg dazu wäre wie folgt:
names default to here(1);
dt=current data table();
start=hptime();
names = dt << Get Column Names( "string" );
for(i=5,i<= ncols(dt),i++,
Eval( Parse( Eval InserT( JSL Quote(
column(dt,i)[dt<<GET rows="" where=""></GET>
Sie werden feststellen, dass dies in der Größenordnung von 3000x schneller ausgeführt wird, was es vergleichbar, wenn nicht sogar besser als die anderen Methoden, die Sie haben, machen sollte
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernJa ... die Hürde besteht darin, dass auf Matrizen zwar mit einem einzigen Index zugegriffen werden kann (was ein Zeilen-Major-Layout voraussetzt), Datentabellen jedoch nicht. Daher gibt es leider kein Analogon zu
m[loc(m, 9999)] = .
wobei m eine pxq-Matrix ist, in der Datentabellenwelt ... daher die Zeilen vor und nach dieser im obigen Code. Obwohl der Code immer noch schnell ist, wäre er WIRKLICH schnell, wenn Einzelindexzugriff für Tabellen verfügbar wäre.
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernFür die ' Overhead beim Erstellen einer Matrix', das vermute ich Subskription von Datentabellen könnte helfen.@Craige_Hales würde wissen.
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernHier ist eine Möglichkeit, Schleifen zu vermeiden, obwohl es einen Overhead gibt, eine Matrix zu erstellen und sie anschließend wieder der Tabelle zuzuweisen. Dieser Ansatz funktioniert bei diesen Daten schnell, ungefähr so wie loc_2d, aber wenn die Tabelle riesig wird, würde ich erwarten, dass ihre relative Leistung darunter leidet, aber ich habe dies nicht überprüft.
expr_mat = expr(
m = dt[0, 5 :: N Col( dt )];
m[Loc( m, 999 )] = .;
dt[0, 5 :: N Col( dt )] = m;
);
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernIch bin vielleicht ein bisschen zu tief darauf eingegangen (aber habe zumindest eine Idee, wie ich diese Art von Tests später durchführen kann, wenn ich muss).
Ich habe eine Art Testskript mit verschiedenen Ausdrücken geschrieben, um verschiedene Möglichkeiten zum Ersetzen der 999 durch fehlende zu testen. Ich habe mit zwei Datensätzen (Semiconductor Capability.jmp und Probe.jmp) getestet und die Anzahl der fehlenden Werte in Spalten geändert.
Loc() ist am schnellsten beim Ersetzen von Werten (keine Überraschung). Missing Value Codes() ist "immer" schnell, hat aber im Vergleich zu Loc() etwas eingeschränktere Anwendungsfälle und ist etwas umständlicher zu verwenden, wenn mehrere Werte zu ersetzen wären.

Am schnellsten war am Ende loc_2d (hat sich nicht die Mühe gemacht, Variablennamen von hier aus zu ändern: Verwenden von Loc mit einer 2D-Matrix ). Ich muss immer noch eine Schleife durchlaufen, um Werte zu setzen (zumindest meines Wissens), also ist der Grund dafür, dass dies schneller als nur Loc() ist, höchstwahrscheinlich, dass es einige Spalten überspringt. Unten sind expr_loc2d und expr_loc:
expr_loc = Expr(
for(i=5,i<= ncols(testData),i++,
column(testData,i)[Loc(testData[0,i], 999)]=.;
);
);
expr_loc_2d = Expr(
mat = testData[0,5::NCols(testData)];
nr = N Rows(mat);
nc = N Cols(mat);
blacklocs = Loc(mat, 999) - 1;
rowlocs = Floor((blacklocs) / nc ) + 1;
collocs = Mod((blacklocs), nc ) + 1 + 4;
uniqCols = Associative Array(collocs) << get keys;
For(i_2d = 1, i_2d <= N Items(uniqCols), i_2d++,
testData[rowlocs[Loc(collocs, uniqCols[i_2d])], uniqCols[i_2d]] = .;
);
);
Auch scheint es wichtig zu sein, AsColumn(Column(dt, i)) like zu verwenden @Ryan_Gilmore sagte (ich habe mir nicht einmal die Mühe gemacht, << Get rows where ohne es zu testen, weil es sehr langsam sein wird), aber ich habe die Recodierung mit und ohne Column() getestet. Ich habe auch gerade bemerkt, dass ich eine Column() von recode verpasst habe, also führe ein paar schnelle Tests durch (nicht in Daten oder Skript angehängt):
Ziemlich großer Unterschied zwischen diesen drei:
expr_recode_ascolumn = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(dt, i),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(testData, i))
)
);
);
expr_recode_ascolumn_column = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(dt, i),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(Column(testData, i)))
)
);
);<BR />
expr_recode_ascolumn_column_column = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(Column(dt, i)),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(Column(testData, i)))
)
);
);

Das Testskript und einige Ergebnisdaten anbei. Das Skript wird höchstwahrscheinlich hier und da einige Fehler enthalten. Sollte ziemlich einfach sein, neue Ausdrücke hinzuzufügen, wenn jemand dies möchte. Eine Sache, die ich überhaupt nicht getestet habe, ist die Verwendung von << Datenaktualisierung beginnen/beenden.
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernInteressante Diskussion! Die Datentabellen-Matrixnotation, die meiner Meinung nach erstmals in JMP 13 auftauchte, ist für die Zuweisung recht effizient.
Für dieses spezielle Problem ist es fast 500-mal schneller als die for-Schleife (0,003 vs. 1,5 Sekunden, JMP 15 Mac-Laptop).
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
Try( dt[Loc( dt[0, i], 999 ), i] = . )
);
Show( HP Time() - start);
try() wird benötigt, um einen Fehler bei Spalten ohne „999“ zu vermeiden.
Das Überschleifen von Spalten ist trotzdem notwendig, da die direkte Zuweisung für die gesamte Matrix, die ich kurz für noch schneller hielt, nicht funktioniert. Fehler: "Mehrere Indizes erfordern einzelne Indizes auf jeder inneren Ebene im Zugriff oder in der Auswertung ...", was auf den zweiten Blick Sinn macht.
// The left Data Table matrix syntax is OK on its own, but not for assignment.
dt[0, 5 :: N Col( dt )][Loc( dt[0, 5 :: N Col( dt )], 999 )] = .;
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernIch war vielleicht irgendwann vorsichtig mit der Verwendung von Schleifen, aber nachdem ich einen Algorithmuskurs besucht hatte (den ich nie beendet habe ...) etwas weniger. Sie müssen nur effizient mit ihnen umgehen, oft loopen Sie viel zu viel (Zeitkomplexität und Big O-Notation). Auch die Verwendung von Schleifen ist viel einfacher als der Versuch, Probleme mit reiner Mathematik zu lösen.
Bei JMP gehe ich meistens nur mit den sicheren Optionen, die ich irgendwann gelernt habe, da die Geschwindigkeit selten ein Problem darstellt. Aber wenn die Zeit ein Problem ist (eine Anwendung brauchte über 6 Minuten, um sie auf der Arbeit zu öffnen), werde ich nach Wegen suchen, um sie schneller zu machen (es ist mir gelungen, sie in weniger als 2 Minuten zu starten). In diesem Fall habe ich es geschafft, es schneller zu machen, indem ich Loc() mit verschiedenen Kombinationen anderer Funktionen (Zeile löschen, Zeilenstatus setzen) verwendet habe.
Zusätzlich zu Loc() scheint die Verwendung von Summarize() im Vergleich zu meiner seit langem bevorzugten Option zum Abrufen eindeutiger Werte in Spalten (assoziatives Array() << Schlüssel abrufen) "viel" schneller zu sein. Wenn die Zeilenanzahl zunimmt, wird das assoziative Array() << get keys immer langsamer und Summarize() scheint nicht unter dem gleichen Problem zu leiden, zumindest nicht so schnell. Also habe ich versucht, Summarize() mehr zu verwenden (man weiß nie, wie groß die Datentabellen werden).
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernAlso überspringen wir AsColumn() mit (mehr mit einer Ahnung und ich habe nicht getestet, dass dies alle korrekten Werte ersetzen wird):
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[Loc(dt[0,i], 999)]=.;
if(mod(i,10)==0,show(i));
);
show(hptime()-start);
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernIch stimme zu, dass Loc() die beste Methode ist.
Für mich scheint die Verlangsamung auf As Column zurückzuführen zu sein. Ich sehe einen signifikanten Unterschied zwischen
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( column(dt, i) );
);
Show( HP Time() - start );
und
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( dt, i );
);
Show( HP Time() - start );
Ändern der As Column-Syntax im Code von @txnelson ursprüngliche Frage, die For-Schleife ist immer noch die schnellste, aber die anderen sind vergleichbarer.
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernHi @txnelson ,
Sehr interessanter Vergleich, und auch nicht die Ergebnisse, die ich erwartet hätte. Ich erinnere mich, als ich am College anfing, Programmieren zu lernen, und mir auch etwas Ähnliches beigebracht wurde: Seien Sie vorsichtig mit Schleifen, weil sie langsam sind.
Bei einigen Berechnungen und in einigen Programmen habe ich natürlich festgestellt, dass For-Schleifen sehr langsam sind – langsamer als die Alternativen. Da ich kein besonders guter Programmierer bin, greife ich eher auf das zurück, was ich kann, was nicht immer schnell oder elegant ist. Manchmal erlaubt mir die Logik einer Schleife, einen funktionierenden Code zu schreiben, um das zu tun, was ich will, was es mir ermöglicht, mit den nächsten Schritten fortzufahren.
Ich finde es interessant, dass die For Loop- und Recode-Optionen für JMP 16 gegenüber 15 etwas langsamer sind, aber die Get Rows Where-Option wurde verbessert, indem die Zeit fast halbiert wurde, das ist eine wesentliche Änderung.
Vielen Dank!,
DS
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernVielleicht @Craige_Hales hätten uns noch schneller eine lösung haben können. Die Idee, Matrixberechnungen (insbesondere Loc()) zu verwenden, habe ich aus seinen Community-Beiträgen/Blogs.
Ich habe ein Tool bei der Arbeit schneller gemacht, das Datensätze öffnet und dann alte Daten löscht (basierend auf der Benutzerauswahl). Ursprünglich wurde << Wo auswählen und Zeilen löschen verwendet. Ich habe es durch etwas Loc()-Magie ersetzt und es wurde auch viel schneller. Ich habe Loc() in letzter Zeit wirklich sehr gemocht.
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändern@jthi ,
Ihr matrixbasierter Code kam am schnellsten herein.
0,299795 Sekunden........
Betreff: Sollten Sie eine Datentabelle durchlaufen oder Recode verwenden oder Get Rows Where verwenden, um Werte in einer Datentabelle zu ändernIch habe bei der Arbeit einige Optimierungen an einigen alten Skripten vorgenommen, die wir haben. Einige der Dinge, die ich ändern musste, bezogen sich darauf, Zeilen zu bekommen, wo. Ich habe begonnen, immer mehr Matrixoperationen (meistens Loc()) zu verwenden, wenn ich schnellen JSL-Code brauche.
Zum Beispiel könnten Sie die Get-Zeilen ändern, wo sie wie folgt sind (habe nicht getestet, ob sie die richtigen Werte oder alle ersetzen):
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[Loc(ascolumn(dt,i) << get as matrix, 999)]=.;
if(mod(i,10)==0,show(i));
);
show(hptime()-start);
Es besteht auch die Möglichkeit, Wertelabels für Spalteneigenschaften und Codes für fehlende Werte zu verwenden, die in einigen Fällen funktionieren könnten. Ich verwende sie gerne, wenn es möglich ist, weil Sie dann die Daten nicht ändern.