
Mehrere Y-Achsen mit einem skalierten Response-Overlay-Diagramm
PAUSE! Wenn Sie nur das Skript möchten, ist es am Ende des Beitrags angehängt.
Dies ist ein spezielles Diagramm, das alle y-Achsen-Variablen skaliert und überlagert und dann die y-Achsen-Skala in den Einheiten und dem Bereich der Variablen bereitstellt. Dies ist nützlich, wenn Sie 3 oder 4 Variablen überlagern möchten, die alle in unterschiedlichen Bereichen liegen. In der obigen Abbildung liegen die Durchflussraten im Bereich von 0-40, während die Leistung im Bereich von 200-1000 und 100 bis 500 liegt. Diese Art von Diagramm wird häufig in wissenschaftlichen und technischen Anwendungen verwendet, bei denen mehrere Sensoren gleichzeitig Daten aufzeichnen, die Reichweite der einzelnen Sensoren jedoch sehr unterschiedlich ist. Beispielsweise beträgt in einem Fermentationsbeispiel Glukose 1–5 g/dl, der pH-Wert 6,8–7,8, Milchsäure 10–50 mmol und die Zellzahl oder Trübung eine Größenordnung höher. Eine einfache Möglichkeit, die Beziehung zwischen allen Variablen über die Zeit grafisch darzustellen, ist sehr hilfreich, um die Kinetik (oder das Erkennen ungewöhnlicher Muster) des Prozesses zu verstehen.
Dieser spezielle Graph ist seit geraumer Zeit mein Erzfeind. Normalerweise ist kreatives Kopieren und Einfügen entweder in JMP oder in PowerPoint erforderlich, um etwas zu erhalten, das einigermaßen gut aussieht. Warum ist diese Grafik so schwer? Irgendwie müssen Sie die Daten skalieren und zu einem Diagramm kombinieren, und dann müssen Sie alle unskalierten y-Achsenkomponenten mit dem skalierten Datendiagramm kombinieren. Alle Methoden, die ich gesehen und ausprobiert habe, beinhalten viel, viel zu viele Schritte zum Kopieren und Einfügen, und die Methoden, die das Aufheben der Gruppierung und Neuformatierung von Diagrammelementen in Powerpoint beinhalten, können manchmal zu schlechten Ergebnissen führen, da es mühsam ist, alle Elemente richtig auszurichten auf meinen Folien. Unordentliche, manuelle, langwierige und sich wiederholende Prozesse schreien nach JSL-Automatisierung.
Beim Nachdenken über eine Strategie zum Skripten einer Lösung müssen einige Schritte berücksichtigt werden
1. Erstellen Sie für jede Variable/Spalte ein Y-Achsen-Feld.
2. Skalieren Sie die Werte jeder Spalte auf denselben Bereich
3. Gestalten Sie die Daten so um, dass sie in einem Überlagerungsdiagramm funktionieren.
4. Es wäre auch schön, wenn es einen Startdialog zum Auswählen von Spalten für y-Rollen und die x-Rolle gäbe (oder vielleicht nur Zeilennummern verwenden)
5. Und zu guter Letzt muss das Diagramm die Achsenbox mit jeder Variablen in der ursprünglichen Skala enthalten.
Ich spitzte meinen JSL-Bleistift und versuchte, diese Figur zu automatisieren. Ich werde versuchen zu erklären, was ich hier getan habe, und würde mich über weitere Diskussionen darüber freuen, wie man es funktionaler machen kann. Der aktuelle Zustand ist ein Skript, das eine Art von festen Zahlen generieren kann und sich nicht darum kümmert, die Folge von Zwischentabellen zu bereinigen, die es hinterlässt. (Ich werde das vollständige Skript an den Beitrag anhängen)
Erster Schritt: Der Startdialog
cd = Column Dialog(
ylist = ColList( "Y", Min Col( 2 ), Max Col( 8 ), Data Type( "Numeric" ), Modeling Type( {"Continuous"} ) ),
xlist = ColList( "X", Max Col( 1 ), Modeling Type( {"Continuous", "Multiple Response"} ) )
);
Eval( Eval Expr( cd[1] ) ); //eval expr, returns what is in cd[1], and the eval makes it execute
Eval( Eval Expr( cd[2] ) ); //separately run just cd[1], then run evalexpr)cd[1], this is how I get the ylist and xlist variables
Jedes Mal, wenn ich anfange, einen Startdialog zu schreiben, kopiere ich ihn schamlos aus dem Skriptindex, lösche, was ich nicht brauche, oder füge bei Bedarf mehr hinzu. Das Ziel ist es, die Y's und die X zu erfassen. Hier gibt es einige Regeln, wie z. B. müssen die Spalten numerisch fortlaufend sein, und außerdem müssen mindestens 2 Ys vorhanden sein (der Grund dafür wird später klarer, aber auch, wenn Sie nur ein Y haben, warum verwenden Sie dann dies? ?) Es gibt auch ein Limit von 8 Y-Antworten, Sie können dies gerne bearbeiten, mehr als 8 schienen zu chaotisch zu sein.
In den letzten beiden Zeilen mit eval(eval expr()) generiere ich die Variablen ylist und xlist aus dem, was in "cd" aus dem Spaltendialog zurückgegeben wird.
Datentabelle 1: Die standardisierte Datentabelle
dt1 = New Table( "multiple y-axis table" ); //this is the table where the standardized values go
If( N Items( xlist ) == 1, //note that if there is a column selected for xlist, then it doesn't get scaled
Insert Into( ylist, xlist, 1 ), //call me crazy, but the first value in ylist is the "X", if it exists
dt1 << New Column( "Row", formula( Row() ) )
);
DT1 ist die Tabelle, die die Daten nach ihrer Standardisierung enthält. Wenn im Dialogfeld kein X ausgewählt ist, ist die erste Spalte "Zeile" und enthält Zeilennummern, die als X-Achse im Diagramm verwendet werden können. Wenn es ein X gibt, wird es an der ersten Position von ylist hinzugefügt. Ja, das ist richtig, die Variable ylist hat sowohl die X's als auch die Y's.
Füllen von dt1 und Erstellen der "Achsenkästchen"
For(
<FONT color="#FF6600"> If( N Items( xlist ) == 1, //yep, that's an "if" argument inside a "for" loop
i = 2; //if an "x" is specified, then start with the second thing in the list
ycol = As Column( dt, ylist[1] ) << get values;
dt1 << New Column( Munger( ylist[1], 1, ":", "" ), set values( ycol ) );//this is soooo ugly
,i = 1), i <= N Items( ylist ), i++, //end of setup for the For loop</FONT>
<FONT color="#008000"> ycol = As Column( dt, ylist[i] ) << get values;
yycol = ((ycol - Min( ycol )) / (Max( ycol ) - Min( ycol )));
dt1 << New Column( Munger( ylist[i], 1, ":", "" ), set values( yycol ) ); //this is soooo ugly, very sorry</FONT>
<FONT color="#0000FF"> amin = Min( ycol ); //this second part of this script is getting values to make the y-axis boxes
amax = Max( ycol ); //I need the min and max values from each of the unscaled columns, along with their names
ymin = amin - ((amax - amin) * .1); //The y-axis box axis range is 10% higher and lower than the min and max.
ymax = amax + ((amax - amin) * .1); //It looks better and is more closely aligned with the scaled data in gb
obj1 = Graph Box( Framesize( 0, 230 ), yName( Char( ylist[i] ) ), Y Scale( ymin, ymax ) ); </FONT><BR /><FONT color="#0000FF"> //its not a true axis box, just a squished graph box. the axis box was free
Insert Into( objhlb1, Name Expr( obj1 ) ); </FONT><BR /><FONT color="#0000FF"> //there is a lot of inserting of name expr() here. its part of building expressions from the inside out.</FONT>
);
Dieser nächste Teil durchläuft die Liste der Spalten, die für die Y's ausgewählt wurden. Wenn ein X angegeben wurde, beginnt es mit dem zweiten Element in der Liste
Der erste Teil verwendet eine gewisse Logik zum Einrichten der Schleife und zum Erstellen einer Tabelle für das Ergebnis, also gehen Sie weiter. Die Tabellenvariante ist davon abhängig, ob eine x-Achsen-Variable angegeben wurde oder nicht.
Der Mittelteil des Skripts ist wirklich wichtig, auch wenn er kurz ist. Es erhält die Werte aus jeder Spalte in der ursprünglichen Tabelle (dt) und legt die skalierten Werte in dt1 ab. Ich bin wirklich nicht stolz auf diese Zeile: dt1 << Neue Spalte ( Munger ( ylist[i], 1 , ":" , "" ), setze Werte( yycol ) ); Aber hier ist die Sache, Neue Spalte will "colname" oder colname, und :colname beendet diesen Schritt, also habe ich einfach den Doppelpunkt weggelassen. (Senden Sie in den Kommentaren eine bessere Möglichkeit, dies zu tun.)
Der letzte Teil betrachtet jeden der Sätze von Spaltenwerten und verwendet sie, um ein Diagrammfeld zu skalieren. Das Diagrammfeld macht schöne Achsenfelder, und wenn die Breite des Diagrammfelds auf 0 gesetzt ist, wird nur das Y-Achsenfeld angezeigt. Dieser Schritt ist wichtig für die Formatierung des Diagramms. Die letzte Zeile fügt den Ausdruck für die Diagrammbox in eine Anzeigebox ein, die am Ende ausgewertet wird. Indem ich den Namen expr verwende, um auf obj1 (das Diagrammfeld) zu verweisen, erhalte ich das Skript, um es zu erstellen, und alle Variablen im Skript werden ausgewertet, aber das Skript wird nicht ausgewertet. Jedes Diagrammfeld wird in ein horizontales Listenfeld (objhlb1) eingefügt. Später wird diese Box in eine andere Box eingefügt.
Erstellen einer Tabelle zum Erstellen eines Diagramms aus:
For(
If( N Items( xlist ) == 1,
i = 2,
i = 1
), i <= N Items( ylist ), i++,
Insert Into( dt2stackcols, ylist[i] )
);
Insert Into( dt2stack, Name Expr( dt2stackcols ), 1 );
Eval(
Substitute( Expr( dt2 = dt1 << ping ),
Expr( ping ), Eval Expr( dt2stack )
)
); //This is tricky!
dt2 << set name( "multiple y-axis stack" );
Oben erwähnte ich eine Einschränkung: dass mindestens zwei y-Variablen ausgewählt werden mussten. Nun, es stellt sich heraus, dass Sie nicht nur eine Variable stapeln können (weil sie bereits gestapelt ist) und das Skript abstürzt, wenn es nur eine gibt. Das Interessante hier ist die Logik, die sich damit befasst, ein bestimmtes X zu haben oder nicht. Wenn es ein X gibt, wollte ich es bereits nicht skalieren, und jetzt möchte ich es nicht in den Stapel aufnehmen, weil ich es als X-Label haben möchte. Wenn kein X angegeben ist, möchte ich alle Variablen in ylist verwenden.
Der letzte Teil in Rot ist schwer zu erklären. In einer Zeile wird das Skript für den Stack erstellt und ausgeführt/ausgewertet. "Ping" ist das Muster, das ersetzt wird, es war das erste Wort, das mir in den Sinn kam, als ich darüber nachdachte, diese Ausdrücke herumzuspringen.
Wo ist der Ausdruck für dt2stackcols? Aus Gründen der Klarheit und meiner Vernunft habe ich bei der Arbeit daran alle definierten Ausdrücke an den Anfang des Skripts verschoben. Das war hilfreich, denn als ich die Reihenfolge des Skripts änderte, waren alle Ausdrücke vorhanden, bevor sie benötigt wurden. Wenn ich sie einfach im Skript verstreut ließ, versuchte ich hin und wieder, sie in einen Ausdruck einzufügen, der später im Skript definiert wurde. Werfen Sie einen Blick auf das vollständige Skript im Anhang, um zu sehen, wie dies aufgebaut ist. Es ist vielleicht keine Best Practice, aber ich ziehe alle Register für mein Nemisis-Diagramm.
Zusammenbau aller Teile
Insert Into( objout, Name Expr( objhlb1 ) );
Insert Into( objvlb, Name Expr( objout ) );
Insert Into( objhlb2, Name Expr( objvlb ) );
Insert Into( objhlb2, Name Expr( obj ) );
Insert Into( objwin, Name Expr( objhlb2 ) );
Eval( Eval Expr( objwin ) ); //Evaluating all the assembled parts after all the expressions are evaluated
gb << size( 600, 285 ); //if you want to format the graph, send messages to gb
Eines der besten Dinge an JSL ist, dass es eine funktionsbasierte Sprache ist. Dadurch werden zwei wichtige Dinge erreicht: Erstens macht es es einfach, bestimmte Dinge zu tun, ohne das Rad neu erfinden zu müssen, und zweitens macht es es einfacher, darüber nachzudenken, wie man komplexe Strukturen baut. (solange Sie sie als eine Zwiebel betrachten, die Sie von innen nach außen bauen. Eine typische Denkweise wäre zum Beispiel so. Zuerst kaufe ich viel Land, dann baue ich ein Gebäude auf dem Land, dann stelle ich Räume in das Gebäude, und dann stelle ich Dinge wie meinen Bürostuhl in das Zimmer, und dann setze ich mich auf den Stuhl und dann endlich Ich fange an zu arbeiten. Funktionsdenken funktioniert so; Zuerst war da ein Stuhl, und ich habe Byron hineingesetzt. Dann habe ich den Stuhl (hat noch einen Byron) ins Büro gestellt, den ich dann ins Gebäude gestellt habe, den ich auf das Grundstück gestellt habe, den ich dann gekauft habe und dann arbeitet Byron schließlich.
Der erste Schritt war, die Diagrammboxen in objhlb1 zu platzieren, dann gibt es eine vertikale Listbox (objvlb), in die eine Umrissbox (objout) und objhlb1 eingefügt werden. Dies ist die linke Seite des Diagramms. Da es sich um die linke Seite handelt, wird ovjvlb in ein zweites horizontales Listenfeld (objhlb2) eingefügt, gefolgt von dem Graph Builder-Diagramm (obj). Schließlich wird objhlb2 (das jetzt alles enthält) in eine neue Fensterbox eingefügt, damit es angezeigt werden kann. Und damit alles funktioniert, wird es ausgewertet. Wenn ich nur Eval benutzt hätte, dann.. Ich bin mir nicht sicher, was passiert, aber es funktioniert nicht, also habe ich zuerst Eval Expr gemacht, damit das gesamte ausgewertete Skript von allem angezeigt wird (versuchen Sie, nur Eval Expr( objwin ) im Skript auszuführen, um zu sehen, was ich meine) und dann ich benutzte Eval, um diesen Teil auszuführen.
Es gibt eine kleine Formatierung von obj, dem Graph-Builder-Plot. Der Titel wird deaktiviert, die Rahmengröße wird neu angepasst und die Y-Achse wird deaktiviert. Dies ist alles, um es an den Maßstab und die Form der y-Achsen-Boxen anzupassen. Es ist praktisch, den Graph Builder-Plot zu verwenden, da die x-Achse immer noch bearbeitet werden kann, die Legende verschoben werden kann und alle GB-Optionen für die weitere Formatierung verfügbar sind.
Aktueller Endpunkt
So wie das Drehbuch bei der 7. Hauptrevision steht, ist es nicht sehr ordentlich und lässt einen Haufen Tische herumhängen. Das und einige andere Dinge sollten behoben werden. Wenn Sie also Ideen für Ergänzungen haben, senden Sie sie bitte mit. Etwas wird einfach nicht passieren, z. B. werden die Daten im Diagramm nicht dynamisch mit der ursprünglichen Datentabelle verknüpft, und auch das Feld der Y-Achse wird nicht mit dem Umrissfeld des skalierten Antwortdiagramms verknüpft, Sie müssen die Größe anpassen jeder für sich. Es wäre toll, wenn das funktionieren würde. Schließlich bin ich mir zu fast 100 % sicher, dass es Möglichkeiten gibt, das Skript zum Absturz zu bringen oder unglückliche Ergebnisse zurückzugeben. Achten Sie also auf spezielle Spaltennamen und extreme Ausreißer oder viele fehlende Daten.
Ich würde mich über Kommentare zu besseren Möglichkeiten freuen, dieses Skript zum Laufen zu bringen. (Apropos funktionieren, ich habe dies in v13 und v14 auf dem PC und Mac getestet und es lief gut zum Posten.)
Eine Schlussbemerkung des Autors:
Dies ist im informellsten Ton geschrieben, so als ob wir darüber sprechen würden.
Dies ist beabsichtigt, da formaler Jargon in der Codierung meiner Meinung nach die Kommunikation behindert. Stattdessen vermeide ich Verschleierung.