
Asse Y multiplo con un grafico di sovrapposizione della risposta in scala
FERMARE! Se vuoi solo lo script, è allegato in fondo al post.
Questo è un grafico specializzato che ridimensiona tutte le variabili dell'asse y e le sovrappone, quindi fornisce la scala dell'asse y nelle unità e nell'intervallo della variabile. Ciò è utile quando si desidera sovrapporre 3 o 4 variabili che sono tutte in intervalli diversi. Nella figura sopra le portate sono comprese nell'intervallo 0-40 mentre la potenza è nell'intervallo 200-1000 e tra 100 e 500. Questo tipo di grafico viene spesso utilizzato in applicazioni scientifiche e ingegneristiche in cui più sensori registrano dati contemporaneamente, ma la portata di ciascun sensore è piuttosto diversa. Ad esempio, in un esempio di fermentazione, il glucosio è 1-5 g/dl, il pH è 6,8 - 7,8, l'acido lattico è 10-50 mmol e la conta cellulare o la torbidità è un ordine di grandezza maggiore. Un modo semplice per rappresentare graficamente la relazione tra tutte le variabili nel tempo è molto utile per comprendere la cinetica (o rilevare schemi insoliti) del processo.
Questo grafico specifico è stato la mia nemesi per un bel po'. Di solito è necessario copiare e incollare in modo creativo all'interno di JMP o in PowerPoint per ottenere qualcosa che sembra ragionevolmente buono. Allora perché questo grafico è così difficile? In qualche modo devi ottenere i dati ridimensionati e combinati in un grafico e quindi devi ottenere tutti i componenti dell'asse y non ridimensionati combinati con il grafico dei dati ridimensionati. Tutti i metodi che ho visto e provato implicano troppi, troppi passaggi di copia e incolla e i metodi che implicano la separazione e la riformattazione degli elementi del grafico in Powerpoint a volte possono generare scarsi risultati a causa della noia di cercare di allineare tutti gli elementi correttamente sulle mie diapositive. Processi disordinati, manuali, noiosi e ripetitivi richiedono l'automazione JSL.
Nel pensare a una strategia per scrivere una soluzione, ci sono un paio di passaggi da considerare
1. Crea una casella dell'asse y per ogni variabile/colonna.
2. Ridimensionare i valori di ciascuna colonna nello stesso intervallo
3. Rimodellare i dati in modo che funzionino in un grafico di sovrapposizione.
4. Sarebbe anche bello se ci fosse una finestra di dialogo di avvio per selezionare le colonne per i ruoli y e il ruolo x (o forse solo usando i numeri di riga)
5. E, ultimo ma non meno importante, il grafico deve includere la casella dell'asse con ciascuna variabile nella scala originale.
Ho affilato la mia matita JSL e ho provato ad automatizzare la creazione di questa figura. Cercherò di spiegare cosa ho fatto qui e apprezzerei ulteriori discussioni su come renderlo più funzionale. Lo stato corrente è uno script che può generare un tipo di cifra fissa e non si preoccupa di ripulire la scia delle tabelle intermedie che lascia dietro di sé. (allego al post lo script completo)
Primo passo: la finestra di dialogo di avvio
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
Ogni volta che inizio a scrivere una finestra di dialogo di avvio, copio spudoratamente quella dall'indice di scripting, eliminando ciò che non mi serve o aggiungendone altro se necessario. L'obiettivo è catturare le Y e le X. Ci sono alcune regole qui, come le colonne devono essere numeriche continue, e inoltre, devono esserci almeno 2 Y (la ragione di ciò sarà più chiara in seguito, ma anche, se hai solo una Y, allora perché stai usando questo ?) C'è anche un limite di 8 risposte Y, sentiti libero di modificarlo, più di 8 sembravano troppo disordinate.
Le ultime due righe con eval(eval expr()) sono il modo in cui genero le variabili ylist e xlist da ciò che viene restituito in "cd" dalla finestra di dialogo della colonna.
Tabella dati 1: La tabella dati standardizzata
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 è la tabella che conterrà i dati dopo la sua standardizzazione. Se nessuna X è selezionata nella finestra di dialogo, la prima colonna è "Riga" e contiene numeri di riga da utilizzare come asse x nel grafico. Se c'è una X, viene aggiunta nella prima posizione di ylist. Sì, è vero, la variabile ylist ha sia le X che le Y.
Riempimento dt1 e creazione delle "caselle degli assi"
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>
);
La parte successiva scorre l'elenco delle colonne selezionate per le Y. Se è stata specificata una X, inizia con il secondo elemento nell'elenco
La prima parte utilizza una logica per impostare il ciclo e creare una tabella per il risultato, quindi entra. La variante della tabella dipende dal fatto che sia stata specificata o meno una variabile dell'asse x.
La parte centrale della sceneggiatura è davvero importante, anche se è breve. Ottiene i valori da ciascuna colonna nella tabella originale (dt) e inserisce i valori in scala in dt1. Non sono proprio orgoglioso di questa linea: dt1 << Nuova colonna ( Munger ( ylista[i], 1 , ":" , "" ), imposta i valori (yycol)); Ma ecco la cosa, la nuova colonna vuole "colname" o colname e :colname uccide questo passaggio, quindi ho appena eliminato i due punti. (Invia un modo migliore per farlo nei commenti.)
L'ultima parte esamina ciascuno degli insiemi di valori di colonna e li utilizza per ridimensionare una casella del grafico. La casella del grafico crea delle belle caselle dell'asse e se la larghezza della casella del grafico è impostata su 0, viene visualizzata solo la casella dell'asse y. Quello che succede è che questo passaggio è importante per la formattazione del grafico. L'ultima riga inserisce l'espressione per la casella del grafico in una casella di visualizzazione che viene valutata alla fine. Usando name expr per fare riferimento a obj1 (la casella del grafico) ottengo lo script per farlo e tutte le variabili nello script vengono valutate, ma lo script non viene valutato. Ogni casella del grafico viene inserita in una casella di riepilogo orizzontale (objhlb1). Successivamente questa casella viene inserita in un'altra casella.
Costruire una tabella per creare un grafico da:
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" );
Sopra ho menzionato un vincolo: che dovevano essere selezionate almeno due variabili y. Bene, si scopre che non puoi impilare solo una variabile (perché è già impilata) e lo script si arresta in modo anomalo se ce n'è solo uno. La cosa interessante qui è la logica che si occupa di avere una X specificata o meno. Se c'è una X, non volevo già ridimensionarla e ora non voglio includerla nello stack perché la voglio come etichetta X. Se non è specificata una X, voglio utilizzare tutte le variabili in ylist.
L'ultima parte in rosso è difficile da spiegare. In una riga, lo script per eseguire lo stack viene compilato ed eseguito/valutato. "Ping" è lo schema che viene sostituito, è stata la prima parola che mi è venuta in mente quando stavo pensando di far rimbalzare queste espressioni.
Dov'è l'espressione per dt2stackcols? Per motivi di chiarezza, e per la mia sanità mentale, quando stavo lavorando su questo ho spostato tutte le espressioni che sono definite all'inizio del copione. Questo è stato utile, perché quando ho cambiato l'ordine della sceneggiatura, le espressioni esistevano tutte prima che fossero necessarie. Se li lasciassi sparsi nello script, ogni tanto proverei a inserirli in un'espressione che è stata definita più avanti nello script. Dai un'occhiata allo script completo, allegato per vedere come è strutturato. Potrebbe non essere una buona pratica, ma sto tirando fuori tutte le fermate per il mio grafico della nemisi.
Assemblaggio di tutte le parti
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
Una delle cose migliori di JSL è che è un linguaggio basato su funzioni. Questo realizza due cose importanti, in primo luogo rende facile fare cose specifiche senza dover reinventare la ruota, e in secondo luogo, rende facile pensare a come costruire strutture complesse. (purché pensi a loro come a una cipolla che stai costruendo dall'interno verso l'esterno. Ad esempio, un tipico modo di pensare sarebbe qualcosa del genere. Prima compro molto terreno, poi costruisco un edificio sul terreno, poi metto stanze nell'edificio, e poi metto cose come la mia sedia da ufficio nella stanza, e poi mi siedo sulla sedia e infine, infine, Inizio a lavorare. Il pensiero funzionale funziona in questo modo; Prima c'era una sedia e ci ho messo Byron. Poi metto la sedia (ha ancora un Byron) nell'ufficio, che poi metto nell'edificio, che metto nel lotto, che poi compro e poi, finalmente, Byron lavora.
Il primo passo è stato inserire le caselle del grafico in objhlb1, quindi c'è una casella di riepilogo verticale (objvlb) in cui sono inseriti una casella di contorno (objout) e objhlb1. Questo è il lato sinistro del grafico. Poiché è il lato sinistro, ovjvlb viene inserito in una seconda casella di riepilogo orizzontale (objhlb2) seguita prima dal grafico del generatore di grafici (obj). Infine objhlb2 (che ora contiene tutto) viene inserito in una nuova finestra in modo che possa essere visualizzato. E poi per far funzionare tutto, viene valutato. Se ho usato solo Eval, allora... Non sono sicuro di cosa succede ma non funziona, quindi ho eseguito prima Eval Expr in modo che venga visualizzato l'intero script valutato di tutto (prova a eseguire solo Eval Expr( objwin ) nello script per vedere cosa intendo) e poi ho ha usato Eval per eseguire quella parte.
C'è una piccola formattazione di obj, il grafico del generatore di grafici. Il titolo viene disattivato, la dimensione del fotogramma viene riadattata e l'asse y viene disattivato. Tutto questo per farlo corrispondere alla scala e alla forma delle caselle dell'asse y. È conveniente utilizzare il grafico del Generatore di grafici perché l'asse x può ancora essere modificato, la legenda può essere spostata e tutte le opzioni GB sono disponibili per ulteriori formattazioni.
Punto finale attuale
Allo stato attuale della sceneggiatura, alla settima revisione principale, non è molto ordinata e lascia un mucchio di tavoli in giro. Questo e molte altre cose dovrebbero essere risolte. Quindi, se avete idee per aggiunte, inviatele insieme. Qualcosa semplicemente non accadrà, ad esempio i dati nel grafico non saranno collegati dinamicamente alla tabella di dati originale e anche la casella dell'asse Y non sarà collegata alla casella del profilo del grafico di risposta in scala, dovrai ridimensionare ciascuno indipendentemente. Sarebbe fantastico se funzionasse. Infine, sono quasi sicuro al 100% che ci siano modi per interrompere lo script o restituire risultati sfortunati, quindi cerca nomi di colonne speciali e valori anomali estremi o molti dati mancanti.
Mi piacerebbe commentare modi migliori per far funzionare questo script. (parlando di lavoro, l'ho testato in v13 e v14 su PC e Mac e ha funzionato bene per pubblicare.)
Una nota finale dell'autore:
Questo è scritto nel tono più informale, proprio come se stessimo parlando di questo.
Questo è intenzionale perché il gergo formale nella codifica, IMHO, impedisce la comunicazione. Invece, evito l'offuscamento.