
Eje Y múltiple con un gráfico de superposición de respuesta escalada
¡DETÉNGASE! Si solo desea el guión, se adjunta en la parte inferior de la publicación.
Este es un gráfico especializado que escala todas las variables del eje y, las superpone y luego proporciona la escala del eje y en las unidades y el rango de la variable. Esto es útil cuando desea superponer 3 o 4 variables que se encuentran en diferentes rangos. En la figura anterior, las tasas de flujo están en el rango de 0 a 40, mientras que la potencia está en el rango de 200 a 1000 y de 100 a 500. Este tipo de gráfico se usa a menudo en aplicaciones científicas y de ingeniería donde varios sensores registran datos al mismo tiempo, pero el rango de cada sensor es bastante diferente. Por ejemplo, en un ejemplo de fermentación, la glucosa es de 1 a 5 g/dl, el pH es de 6,8 a 7,8, el ácido láctico es de 10 a 50 mmol y el recuento de células o la turbidez es un orden de magnitud mayor. Una forma sencilla de graficar la relación entre todas las variables a lo largo del tiempo es muy útil para comprender la cinética (o detectar patrones inusuales) del proceso.
Este gráfico específico ha sido mi némesis durante bastante tiempo. Por lo general, se necesita algo de copia y pegado creativo dentro de JMP o en PowerPoint para obtener algo que se vea razonablemente bien. Entonces, ¿por qué es tan difícil este gráfico? De alguna manera, debe obtener los datos escalados y combinados en un gráfico y luego debe obtener todos los componentes del eje y sin escalar combinados con el gráfico de datos escalados. Todos los métodos que he visto y probado implican demasiados pasos de copiar y pegar, y los métodos que implican desagrupar y reformatear elementos gráficos en Powerpoint a veces pueden generar resultados deficientes debido al tedio de tratar de alinear correctamente todos los elementos. en mis diapositivas. Los procesos desordenados, manuales, tediosos y repetitivos piden a gritos la automatización JSL.
Al pensar en una estrategia para escribir una solución, hay un par de pasos a considerar
1. Cree un cuadro en el eje y para cada variable/columna.
2. Escalar los valores de cada columna al mismo rango
3. Modifique los datos para que funcionen en un gráfico de superposición.
4. También sería bueno si hubiera un cuadro de diálogo de inicio para seleccionar columnas para roles y y el rol x (o tal vez solo usando números de fila)
5. Y por último, pero no menos importante, el gráfico debe incluir el cuadro del eje con cada variable en la escala original.
Saqué punta a mi lápiz JSL e intenté automatizar la creación de esta figura. Trataré de explicar lo que hice aquí, y agradecería más discusión sobre cómo hacerlo más funcional. El estado actual es un script que puede generar un tipo de figura fija y no se molesta en limpiar la estela de tablas intermedias que deja atrás. (Adjuntaré el guión completo a la publicación)
Primer paso: el cuadro de diálogo de inicio
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
Cada vez que empiezo a escribir un cuadro de diálogo de inicio, copio descaradamente el del índice de secuencias de comandos, elimino lo que no necesito o agrego más según sea necesario. El objetivo es capturar las Y y las X. Aquí hay algunas reglas, como que las columnas deben ser numéricas continuas, y también, debe haber al menos 2 Y (la razón de esto es más clara más adelante, pero también, si solo tiene una Y, ¿por qué está usando esto? ?) También hay un límite de 8 Y respuestas, siéntase libre de editar esto, más de 8 parecía demasiado desordenado.
Las últimas dos líneas con eval(eval expr()) son cómo genero las variables ylist y xlist a partir de lo que se devuelve en "cd" desde el cuadro de diálogo de la columna.
Tabla de datos 1: La tabla de datos estandarizada
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 es la tabla que contendrá los datos después de su estandarización. Si no se selecciona una X en el cuadro de diálogo, la primera columna es "Fila" y tiene números de fila para usar como eje x en el gráfico. Si hay una X, se agrega a la primera posición de ylist. Sí, así es, la variable ylist tiene tanto las X como las Y.
Llenando dt1 y haciendo las "cajas de eje"
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 siguiente parte recorre la lista de columnas seleccionadas para las Y. Si se especificó una X, entonces comienza con el segundo elemento de la lista
La primera parte usa algo de lógica para configurar el bucle y hacer una tabla para el resultado, así que entra. La variante de la tabla depende de si se especificó o no una variable del eje x.
La parte central del guión es muy importante, aunque sea breve. Obtiene los valores de cada columna en la tabla original (dt) y coloca los valores escalados en dt1. Realmente no estoy orgulloso de esta línea: dt1 << Nueva columna ( Munger (ylista[i], 1 , ":" , "" ), establecer valores ( yycol ) ); Pero aquí está la cosa, la nueva columna quiere "colname" o colname, y :colname mata este paso, así que simplemente eliminé los dos puntos. (Envíe una mejor manera de hacer esto en los comentarios).
La última parte analiza cada uno de los conjuntos de valores de columna y los usa para escalar un cuadro de gráfico. El cuadro de gráfico crea buenos cuadros de eje, y si el ancho del cuadro de gráfico se establece en 0, solo aparece el cuadro del eje y. Lo que sucede es que este paso es importante para formatear el gráfico. La última línea inserta la expresión del cuadro de gráfico en un cuadro de visualización que se evalúa al final. Al usar name expr para referirme a obj1 (el cuadro del gráfico), obtengo el script para hacerlo y se evalúan todas las variables en el script, pero el script no se evalúa. Cada cuadro de gráfico se inserta en un cuadro de lista horizontal (objhlb1). Más tarde esta caja se inserta en otra caja.
Construyendo una tabla para hacer un gráfico a 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" );
Anteriormente mencioné una restricción: que al menos dos variables y tenían que ser seleccionadas. Bueno, resulta que no puede apilar solo una variable (porque ya está apilada) y hace que el script se bloquee si solo hay una. Lo interesante aquí es la lógica que trata de tener una X específica o no. Si hay una X, ya no quería escalarla, y ahora no quiero incluirla en la pila porque la quiero como etiqueta X. Si no hay una X especificada, entonces quiero usar todas las variables en ylist.
La última parte en rojo es difícil de explicar. En una línea, se construye y ejecuta/evalúa el script para hacer la pila. "Ping" es el patrón que se sustituye, fue la primera palabra que me vino a la mente cuando estaba pensando en rebotar estas expresiones.
¿Dónde está la expresión para dt2stackcols? En aras de la claridad y mi cordura cuando estaba trabajando en esto, moví todas las expresiones definidas al comienzo del guión. Esto fue útil, porque cuando cambié el orden del guión, todas las expresiones existían antes de que fueran necesarias. Si los dejara dispersos en el script, de vez en cuando intentaría insertarlos en una expresión que se definió más adelante en el script. Eche un vistazo al guión completo adjunto para ver cómo se presenta. Puede que no sea una buena práctica, pero estoy haciendo todo lo posible para mi gráfico de nemisis.
Montaje de todas las piezas
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 de las mejores cosas de JSL es que es un lenguaje basado en funciones. Esto logra dos cosas importantes, primero, facilita hacer cosas específicas sin tener que reinventar la rueda y, segundo, facilita pensar en cómo construir estructuras complejas. (siempre y cuando pienses en ellos como una cebolla que estás construyendo de adentro hacia afuera. Por ejemplo, una forma típica de pensar sería algo como esto. Primero compro un lote de terreno, luego construyo un edificio en el terreno, luego coloco habitaciones en el edificio, y luego coloco cosas como la silla de mi oficina en la habitación, y luego me siento en la silla y luego, finalmente, empiezo a trabajar El pensamiento funcional funciona así; Primero había una silla y puse a Byron en ella. Luego pongo la silla (todavía tiene una Byron) en la oficina, que luego pongo en el edificio, que pongo en el lote, que luego compro y luego, finalmente, Byron trabaja.
El primer paso fue colocar los cuadros de gráficos en objhlb1, luego hay un cuadro de lista vertical (objvlb) en el que se insertan un cuadro de contorno (objout) y objhlb1. Este es el lado izquierdo del gráfico. Dado que es el lado izquierdo, ovjvlb se inserta en un segundo cuadro de lista horizontal (objhlb2), seguido primero por el gráfico del generador de gráficos (obj). Finalmente, objhlb2 (que ahora contiene todo) se inserta en un nuevo cuadro de ventana para que se pueda mostrar. Y luego, para que todo funcione, se evalúa. Si acabo de usar Eval, entonces... No estoy seguro de lo que sucede, pero no funciona, así que primero hice Eval Expr para que se muestre todo el script evaluado (intente ejecutar solo Eval Expr (objwin) en el script para ver a qué me refiero) y luego usó Eval para ejecutar esa parte.
Hay un pequeño formato de obj, la trama del generador de gráficos. El título se apaga, el tamaño del marco se reajusta y el eje y se apaga. Todo esto es para que coincida con la escala y la forma de los cuadros del eje y. Es conveniente usar el gráfico Graph Builder porque el eje x todavía se puede editar, la leyenda se puede mover y todas las opciones de GB están disponibles para formatear más.
Punto final actual
Tal como está el guión, en la séptima revisión principal, no está muy ordenado y deja un montón de mesas dando vueltas. Eso y varias cosas más deberían arreglarse. Entonces, si tiene ideas para adiciones, envíelas. Algunas cosas simplemente no sucederán, por ejemplo, los datos en el gráfico no se vincularán dinámicamente a la tabla de datos original y el cuadro del eje Y no se vinculará al cuadro de contorno del gráfico de respuesta escalada, tendrá que ajustar el tamaño. cada uno de forma independiente. Sería genial si eso funcionara. Finalmente, estoy casi 100% seguro de que hay formas de hacer que el script se rompa o de que arroje resultados desafortunados, así que busque nombres de columnas especiales y valores atípicos extremos, o muchos datos faltantes.
Me encantaría recibir comentarios sobre mejores formas de hacer que este script funcione. (hablando de trabajo, probé esto en v13 y v14 en PC y Mac y funcionó bien para publicar).
Una nota final del autor:
Esto está escrito en el tono más informal, como si estuviéramos hablando de esto.
Esto es intencional porque la jerga formal en la codificación, en mi humilde opinión, impide la comunicación. En cambio, evito la ofuscación.