
Plusieurs axes Y avec un graphique de superposition de réponse mis à l'échelle
ARRÊT! Si vous voulez juste le script, il est joint au bas du message.
Il s'agit d'un graphique spécialisé qui met à l'échelle toutes les variables de l'axe des y et les superpose, puis fournit l'échelle de l'axe des y dans les unités et la plage de la variable. Ceci est utile lorsque vous souhaitez superposer 3 ou 4 variables qui sont toutes dans des plages différentes. Dans la figure ci-dessus, les débits sont compris entre 0 et 40 tandis que la puissance est comprise entre 200 et 1 000 et entre 100 et 500. Ce type de graphique est souvent utilisé dans les applications scientifiques et d'ingénierie où plusieurs capteurs enregistrent des données en même temps, mais la portée de chaque capteur est assez différente. Par exemple, dans un exemple de fermentation, le glucose est de 1 à 5 g/dl, le pH est de 6,8 à 7,8, l'acide lactique est de 10 à 50 mmol et le nombre de cellules ou la turbidité est d'un ordre de grandeur supérieur. Un moyen simple de représenter graphiquement la relation entre toutes les variables au fil du temps est très utile pour comprendre la cinétique (ou détecter des modèles inhabituels) du processus.
Ce graphique spécifique a été mon ennemi juré pendant un bon moment. Il faut généralement un copier-coller créatif dans JMP ou dans PowerPoint pour obtenir quelque chose qui semble raisonnablement bon. Alors pourquoi ce graphique est-il si difficile ? D'une manière ou d'une autre, vous devez obtenir les données mises à l'échelle et combinées dans un seul graphique, puis vous devez obtenir tous les composants de l'axe y non mis à l'échelle combinés avec le graphique de données mis à l'échelle. Toutes les méthodes que j'ai vues et essayées impliquent beaucoup, beaucoup trop d'étapes de copier-coller et les méthodes qui impliquent de dégrouper et de reformater des éléments de graphique dans Powerpoint peuvent parfois générer de mauvais résultats en raison de l'ennui d'essayer d'aligner tous les éléments correctement sur mes diapositives. Les processus désordonnés, manuels, fastidieux et répétitifs réclament l'automatisation JSL.
En pensant à une stratégie pour scénariser une solution, il y a quelques étapes à considérer
1. Créez une case sur l'axe des ordonnées pour chaque variable/colonne.
2. Mettez à l'échelle les valeurs de chaque colonne dans la même plage
3. Remodelez les données afin qu'elles fonctionnent dans un tracé de superposition.
4. Ce serait également bien s'il y avait une boîte de dialogue de lancement pour sélectionner les colonnes pour les rôles y et le rôle x, (ou peut-être simplement en utilisant les numéros de ligne)
5. Enfin et surtout, le graphique doit inclure la case de l'axe avec chaque variable dans l'échelle d'origine.
J'ai aiguisé mon crayon JSL et j'ai essayé d'automatiser la fabrication de cette figure. Je vais essayer d'expliquer ce que j'ai fait ici, et j'apprécierais une discussion plus approfondie sur la façon de le rendre plus fonctionnel. L'état actuel est un script qui peut générer un type de chiffre fixe et ne se soucie pas de nettoyer le sillage des tables intermédiaires qu'il laisse derrière lui. (Je joindrai le script complet au message)
Première étape : la boîte de dialogue de lancement
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
Chaque fois que je commence à écrire une boîte de dialogue de lancement, je copie sans vergogne celle de l'index de script, en supprimant ce dont je n'ai pas besoin ou en ajoutant plus si nécessaire. Le but est de capturer les Y et les X. Il y a quelques règles ici, comme les colonnes doivent être numériques continues, et aussi, il doit y avoir au moins 2 Y (la raison en sera plus claire plus tard, mais aussi, si vous n'avez qu'un Y, alors pourquoi utilisez-vous ceci ?) Il y a aussi une limite de 8 Y réponses, n'hésitez pas à modifier cela, plus de 8 semblaient trop brouillons.
Les deux dernières lignes avec eval(eval expr()) expliquent comment je génère les variables ylist et xlist à partir de ce qui est renvoyé dans "cd" à partir de la boîte de dialogue de la colonne.
Tableau de données 1 : Le tableau de données standardisé
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 est le tableau qui contiendra les données après sa standardisation. Si aucun X n'est sélectionné dans la boîte de dialogue, la première colonne est "Ligne" et elle contient des numéros de ligne à utiliser comme axe des x dans le graphique. S'il y a un X, il est ajouté en première position de ylist. Oui c'est vrai, la variable ylist a à la fois les X et les Y.
Remplir dt1 et faire les "boîtes d'axes"
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>
);
Cette partie suivante parcourt la liste des colonnes sélectionnées pour les Y. Si un X a été spécifié, il commence par le deuxième élément de la liste
La première partie utilise une certaine logique pour configurer la boucle et créer un tableau pour le résultat, alors allez-y. La variante de tableau dépend du fait qu'une variable d'axe des x ait été spécifiée ou non.
La partie médiane du script est vraiment importante, même si elle est courte. Il obtient les valeurs de chaque colonne de la table d'origine (dt) et place les valeurs mises à l'échelle dans dt1. Je ne suis vraiment pas fier de cette ligne : dt1 << Nouvelle colonne ( Münger ( ylist[i], 1 , ":" , "" ), définir les valeurs( yycol ) ); Mais voici le truc, la nouvelle colonne veut "colname" ou colname, et :colname tue cette étape, donc je viens de supprimer les deux-points. (Envoyez une meilleure façon de le faire dans les commentaires.)
La dernière partie examine chacun des ensembles de valeurs de colonne et les utilise pour mettre à l'échelle une zone de graphique. La boîte de graphique fait de belles boîtes d'axe, et si la largeur de la boîte de graphique est définie sur 0, seule la boîte de l'axe des ordonnées s'affiche. Ce qui se passe, c'est que cette étape est importante pour le formatage du graphique. La dernière ligne insère l'expression de la boîte graphique dans une boîte d'affichage qui est évaluée à la fin. En utilisant le nom expr pour faire référence à obj1 (la zone graphique), j'obtiens le script pour le créer et toutes les variables du script sont évaluées, mais le script n'est pas évalué. Chaque zone graphique est insérée dans une zone de liste horizontale (objhlb1). Plus tard, cette case est insérée dans une autre case.
Construire un tableau pour faire un graphique à partir de:
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" );
Ci-dessus, j'ai mentionné une contrainte : qu'au moins deux variables y devaient être sélectionnées. Eh bien, il s'avère que vous ne pouvez pas empiler une seule variable (car elle est déjà empilée) et cela fait planter le script s'il n'y en a qu'une. La chose intéressante ici est la logique qui consiste à avoir un X spécifié ou non. S'il y a un X, je ne voulais déjà pas le mettre à l'échelle, et maintenant je ne veux pas l'inclure dans la pile parce que je le veux comme étiquette X. S'il n'y a pas de X spécifié, alors je veux utiliser toutes les variables dans ylist.
La dernière partie en rouge est difficile à expliquer. En une ligne, le script pour faire la pile est construit et exécuté/évalué. "Ping" est le motif qui est remplacé, c'est le premier mot qui m'est venu à l'esprit quand j'ai pensé à faire rebondir ces expressions.
Où est l'expression pour dt2stackcols ? Par souci de clarté et pour ma santé mentale, lorsque je travaillais dessus, j'ai déplacé toutes les expressions définies au début du script. Cela a été utile, car au fur et à mesure que j'ai changé l'ordre du script, les expressions existaient toutes avant qu'elles ne soient nécessaires. Si je les laissais éparpillés dans le script, j'essayais de temps en temps de les insérer dans une expression définie plus tard dans le script. Jetez un œil au script complet, ci-joint, pour voir comment cela se présente. Ce n'est peut-être pas une bonne pratique, mais je mets tout en œuvre pour mon graphique Nemisis.
Assemblage de toutes les pièces
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
L'une des meilleures choses à propos de JSL est qu'il s'agit d'un langage basé sur des fonctions. Cela accomplit deux choses importantes, premièrement, il est facile de faire des choses spécifiques sans avoir à réinventer la roue, et deuxièmement, cela facilite la réflexion sur la façon de construire des structures complexes. (tant que vous les considérez comme un oignon que vous construisez de l'intérieur. Par exemple, une façon typique de penser serait quelque chose comme ça. J'achète d'abord un terrain, puis je construis un bâtiment sur le terrain, puis je mets des pièces dans le bâtiment, puis je mets des trucs comme ma chaise de bureau dans la pièce, puis je m'assois sur la chaise et puis, enfin, Je commence à travailler. La pensée fonctionnelle fonctionne comme ceci; Il y avait d'abord une chaise, et j'y ai mis Byron. Ensuite, j'ai mis la chaise (qui a toujours un Byron) dans le bureau, que j'ai ensuite mis dans le bâtiment, que j'ai mis dans le lot, que j'ai ensuite acheté et enfin, Byron fonctionne.
La première étape consistait à placer les boîtes de graphe dans objhlb1, puis il y a une liste verticale (objvlb) dans laquelle une boîte de contour (objout) et objhlb1 sont insérés. C'est le côté gauche du graphique. Comme il s'agit du côté gauche, ovjvlb est inséré dans une deuxième zone de liste horizontale (objhlb2) d'abord suivie du graphique du générateur de graphes (obj). Enfin objhlb2 (qui contient maintenant tout) est inséré dans une nouvelle boîte de fenêtre afin qu'il puisse être affiché. Et puis pour que tout fonctionne, il est évalué. Si je viens d'utiliser Eval, alors .. Je ne sais pas ce qui se passe mais cela ne fonctionne pas, alors j'ai d'abord fait Eval Expr pour que tout le script évalué de tout soit affiché (essayez d'exécuter juste Eval Expr( objwin ) dans le script pour voir ce que je veux dire) et puis je utilisé Eval pour exécuter cette partie.
Il y a un peu de formatage d'obj, le tracé du générateur de graphes. Le titre est désactivé, la taille de l'image est réajustée et l'axe y est désactivé. C'est tout pour le faire correspondre à l'échelle et à la forme des cases de l'axe y. Il est pratique d'utiliser le tracé de Graph Builder car l'axe des x peut toujours être modifié, la légende peut être déplacée et toutes les options GB sont disponibles pour un formatage ultérieur.
Point final actuel
Dans l'état actuel du script, à la 7e révision majeure, ce n'est pas très ordonné et il laisse un tas de tables traîner. Cela et plusieurs autres choses devraient être corrigés. Donc, si vous avez des idées d'ajouts, n'hésitez pas à les envoyer. Quelque chose ne se produira tout simplement pas, par exemple, les données du graphique ne seront pas liées dynamiquement à la table de données d'origine et la zone de l'axe Y ne sera pas liée à la zone de contour du graphique de réponse à l'échelle, vous devrez dimensionner chacun indépendamment. Ce serait formidable si cela fonctionnait. Enfin, je suis presque sûr à 100% qu'il existe des moyens de casser le script ou de renvoyer des résultats malheureux, alors faites attention aux noms de colonnes spéciaux et aux valeurs aberrantes extrêmes, ou à de nombreuses données manquantes.
J'aimerais des commentaires sur de meilleures façons de faire fonctionner ce script. (en parlant de travail, j'ai testé cela en v13 et v14 sur PC et Mac et ça a bien fonctionné pour poster.)
Une note finale de l'auteur :
Ceci est écrit sur le ton le plus informel, comme si nous parlions de cela.
Ceci est intentionnel car le jargon formel dans le codage, à mon humble avis, entrave la communication. Au lieu de cela, j'évite l'obscurcissement.