[JSL] Script para la prueba T de dos lados y uno en una gran cantidad de métricasHola a todos,
Me gustaría escribir un script JSL para automatizar una TOST (prueba T de dos lados y uno) en una gran cantidad de métricas (más de 500).
El TOST funciona por pares de “niveles”, por lo que trabajar con 2 niveles es fácil, porque requiere solo 1 fila por “par”.
Ya escribí un script que funciona bien para niveles 2x generando una “Tabla de datos combinados” y agregándole más columnas con fórmulas, que terminan con los resultados: “Equivalente” o “No equivalente”.
El problema se complica cuando quiero trabajar con más de 2 niveles, sería necesario encontrar todos los pares únicos posibles de "Niveles" y luego ejecutar el cálculo TOST para cada combinación de Métrica + pares de Niveles.
Encontré una manera (en este foro) de encontrar todos los pares únicos posibles de una lista y ponerlos en una tabla o en una lista anidada (NchooseKmatrix()).
Pero estoy atascado ahí. Lo que necesito es:
1- para rellenar una nueva tabla con todas las combinaciones posibles de Métricas (Columna1) y todos los pares de niveles posibles (Columna2 y Columna3).
2- Luego agrega las estadísticas de cada par de niveles en cada fila
A partir de este punto podré realizar los cálculos para finalizar el TOST.
He adjuntado un script que genera una tabla ficticia, luego una tabla de datos combinada del análisis unidireccional y luego mi intento fallido de crear una tercera tabla con todas las combinaciones de métricas + pares únicos de nivel.
Gracias de antemano por su ayuda
Names Default To Here( 1 );
clear symbols();
Deletesymbols();
//Create table with random distribution (table 1)
dt = New Table( "tabletest",
Add Rows( 500 ),
New Column( "Product", Character, "Nominal", formula("Product " ||char( Random Integer( 1, 4 )))),
New Column( "Metric1", Numeric, "Continuous", formula( Random Normal( 10, 1 ) ) ),
New Column( "Metric2", Numeric, "Continuous", formula( Random Gamma( 10, 1 ) ) ),
New Column( "Metric3", Numeric, "Continuous", formula( Random Gamma( 10, 2 ) ) )
);
Column( dt, "Metric1" ) << set property( "spec limits", {LSL( 5 ), USL( 25 ), Show Limits( 0 )} );
Column( dt, "Metric2" ) << set property( "spec limits", {LSL( 0 ), USL( 40 ), Show Limits( 0 )} );
Column( dt, "Metric3" ) << set property( "spec limits", {LSL( 5 ), USL( 40 ), Show Limits( 0 )} );
dt << save( "$Desktop\tabletest.jmp" );
//Get a list from all unique Product
SplitBy = "Product";
summarize(ColumnSplitBy=by(Column (SplitBy)));
show(ColumnSplitBy);
For( i = N Items( ColumnSplitBy ), i > 0, i--,
If( ColumnSplitBy[i] == "",
Remove From( ColumnSplitBy, i, 1 );
)
);
//Get Column name
MetricCols = dt << Get Column Names( numeric, continuous, "string" );
//Get all unique combo of 2 Products
ColumnSplitByCombo = aslist( nchoosekMatrix(nitems(ColumnSplitBy), 2));
show (ColumnSplitByCombo);
//Create a combinaed datatable with stats (Table 2)
ow = dt << Oneway(
Y(Eval(MetricCols)),
X(Eval(SplitBy)),
Means and Std Dev(1),
SendToReport(
Dispatch(
{"Means and Std Deviations"},
"Std Dev Lower 95%",
NumberColBox,
{Visibility("Visible")}
),
Dispatch(
{"Means and Std Deviations"},
"Std Dev Upper 95%",
NumberColBox,
{Visibility("Visible")}
)
),
Invisible
);
tb = Report(ow[1])[Outline Box("Means and Std Deviations"), Table Box(1)];
dt2 = tb << Make Combined Data Table;
tb << close window;
//Create new table for the result, each row has a unique pair of Levels
dt3 = astable( nchoosekMatrix(nitems(ColumnSplitBy), 2), << column names({"x", "y"}) );
dt3 << new column ("first", character, << set values ( ColumnSplitBy[dt3:x << get values]));
dt3 << new column ("second", character, << set values ( ColumnSplitBy[dt3:y << get values]));
dt3 << delete columns (1::2);
//Create a nested list with each pair of levels
Listdt3 = aslist( nchoosekMatrix(nitems(ColumnSplitBy), 2));
show (Listdt3);
// I tried to use associate array() to match the level and name but couldn't make it work- Predicador
Automatización y scriptingRe: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasAdjunté un documento "Untitled.png" con capturas de pantalla para (con suerte) explicar mejor mi solicitud.
¡Gracias!
Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasPor ejemplo, primero podrías apilar tu columna de grupo, actualizarla con los resultados y dividirla nuevamente.
Este ejemplo no tiene en cuenta que puede haber varias métricas.
// Loop over Metrics and perform these for each of them
// Concatenate results into single table
dt3 = As Table(NChooseK Matrix(N Items(ColumnSplitBy), 2), <<COLUMN names=""></COLUMN>// Metrics should be taken into account here OR stacked table should contain them
dt4 << Update(
With(dt2),
Match Columns(:Data = :Level)
);
o no tiene la división final que probablemente se parece a esto

Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasHola @Voizingu : TOST solo verifica si el intervalo de confianza del 100(1-2alpha)% está contenido completamente dentro de algunos límites de equivalencia; entonces, ¿quizás una solución adecuada sería usar el procedimiento de comparaciones múltiples Ajustar Y por X para hacer todas las combinaciones por usted?
Re: [JSL] Script para la prueba T de dos lados y uno en una gran cantidad de métricas¿Existe alguna razón por la que no puedes utilizar la prueba de equivalencia incorporada en la plataforma Oneway?
Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasHola Jarmo,
¡Apilar > Actualizar > Dividir es una gran idea!
He podido implementarlo con éxito, pero mi código está un poco sucio (muchos bucles For(), pasos intermedios,...).
En lugar de:
- Concatenar la tabla varias veces
- Luego, rellenar una columna a partir de una lista de métricas repetidas
Me gustaría mejorar en la optimización de mi código.
¿Hay alguna manera de rellenar dt5 de una manera más limpia/optimizada? (¿usar comandos JMP nativos tal vez?)
Me encantaría recibir tus sugerencias sobre esto.
Pero de todos modos, muchas gracias por ayudarme en esto. 
- Predicador
Names Default To Here( 1 );
clear symbols();
Deletesymbols();
//Create table with random distribution (table 1)
dt = New Table( "tabletest",
Add Rows( 5000 ),
New Column( "Product", Character, "Nominal", formula("Product " ||char( Random Integer( 1, 4 )))),
New Column( "Metric1", Numeric, "Continuous", formula( Random Normal( 10, 1 ) ) ),
New Column( "Metric2", Numeric, "Continuous", formula( Random Gamma( 10, 1 ) ) ),
New Column( "Metric3", Numeric, "Continuous", formula( Random Gamma( 10, 2 ) ) ),
);
Column( dt, "Metric1" ) << set property( "spec limits", {LSL( 5 ), USL( 25 ), Show Limits( 0 )} );
Column( dt, "Metric2" ) << set property( "spec limits", {LSL( 0 ), USL( 40 ), Show Limits( 0 )} );
Column( dt, "Metric3" ) << set property( "spec limits", {LSL( 5 ), USL( 40 ), Show Limits( 0 )} );
dt << save( "$Desktop\tabletest.jmp" );
//Get a list from all unique Product
SplitBy = "Product";
summarize(ColumnSplitBy=by(Column (SplitBy)));
show(ColumnSplitBy);
For( i = N Items( ColumnSplitBy ), i > 0, i--,
If( ColumnSplitBy[i] == "",
Remove From( ColumnSplitBy, i, 1 );
)
);
//Get Column name
MetricCols = dt << Get Column Names( numeric, continuous, "string" );
show(MetricCols);
//Get all unique combo of 2 Products
ColumnSplitByCombo = aslist( nchoosekMatrix(nitems(ColumnSplitBy), 2));
show (ColumnSplitByCombo);
//Create a combinaed datatable with stats (Table 2)
ow = dt << Oneway(
Y(Eval(MetricCols)),
X(Eval(SplitBy)),
Means and Std Dev(1),
SendToReport(
Dispatch(
{"Means and Std Deviations"},
"Std Dev Lower 95%",
NumberColBox,
{Visibility("Visible")}
),
Dispatch(
{"Means and Std Deviations"},
"Std Dev Upper 95%",
NumberColBox,
{Visibility("Visible")}
)
),
Invisible
);
tb = Report(ow[1])[Outline Box("Means and Std Deviations"), Table Box(1)];
dt2 = tb << Make Combined Data Table;
tb << close window;
//Create new table for the result, each row has a unique pair of Levels
dt3 = astable( nchoosekMatrix(nitems(ColumnSplitBy), 2), << column names({"x", "y"}) );
dt3 << new column ("first", character, << set values ( ColumnSplitBy[dt3:x << get values]));
dt3 << new column ("second", character, << set values ( ColumnSplitBy[dt3:y << get values]));
dt3 << delete columns (1::2);
//Create a nested list with each pair of levels
Listdt3 = aslist( nchoosekMatrix(nitems(ColumnSplitBy), 2));
show (Listdt3);
// I tried to use associate array() to match the level and name but couldn't make it work
// Loop over Metrics and perform these for each of them
// Concatenate results into single table
dt3 = As Table(NChooseK Matrix(N Items(ColumnSplitBy), 2), <<COLUMN names=""></COLUMN>Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasHola MRB3855,
Tienes un punto excelente, no quería poner demasiada información en mi primer mensaje para centrarme en mi problema de script, pero en mi código inicial (que funciona para 2 niveles), establecí los límites de equivalencia como el 5% del rango [LSL;USL].
Por supuesto, este es un valor arbitrario que puedo cambiar si es necesario.
Me permite examinar más de cientos de métricas y marcar las métricas "No equivalentes" de este criterio, que normalmente son una docena.
Luego de esta lista puedo realizar un análisis exhaustivo:
- Análisis de varianza
- TOST con un rango adecuado
- etc...
En cuanto a tu segundo punto, si entiendo bien, sugieres ejecutar la plataforma TOST para cada métrica y seleccionar los datos y colocarlos en una tabla mediante Display Box(). Si es así, esa es una posible solución, pero preferiría ejecutar los cálculos yo mismo para comprender el principio de TOST y también ahorrar algo de tiempo de procesamiento.
Espero que tenga sentido:)
Gracias por ayudar
- Predicador
Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasHola Mark,
Sí, al principio pensé en usar la plataforma TOST y seleccionar las estadísticas con Display Box(), pero preferí el enfoque diferente para calcular cada parámetro individualmente para lograr los mismos resultados que brinda la plataforma.
Después de todo, tal vez hubiera sido un enfoque más rápido que reinventar la rueda, pero ahora he invertido tanto tiempo en esto, y estoy tan cerca de lograrlo que bien podría terminar lo que empecé (de la manera difícil), ¡jaja!
Uno de los beneficios de esto es que ahora entiendo cada paso del método TOST.
Gracias
- Predicador
Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasLo más probable es que yo mismo utilice bucles, pero puedes hacerlo con algunas operaciones con matrices. En mi opinión, este script requeriría una reescritura para que sea más fácil de leer (mejores nombres para las variables, uso del nombre de la tabla de salida, cierre de tablas innecesarias y limpieza general).
Ver más...
Names Default To Here(1);
//Create table with random distribution (table 1)
dt = New Table("tabletest",
Add Rows(5000),
New Column("Product",
Character,
"Nominal",
formula("Product " || Char(Random Integer(1, 4)))
),
New Column("Metric1", Numeric, "Continuous", formula(Random Normal(10, 1))),
New Column("Metric2", Numeric, "Continuous", formula(Random Gamma(10, 1))),
New Column("Metric3", Numeric, "Continuous", formula(Random Gamma(10, 2))),
);
Column(dt, "Metric1") << set property("spec limits", {LSL(5), USL(25), Show Limits(0)});
Column(dt, "Metric2") << set property("spec limits", {LSL(0), USL(40), Show Limits(0)});
Column(dt, "Metric3") << set property("spec limits", {LSL(5), USL(40), Show Limits(0)});
SplitBy = "Product";
Summarize(ColumnSplitBy = by(Column(SplitBy)));
MetricCols = dt << Get Column Names(numeric, continuous, "string");
ColumnSplitByCombo = As List(NChooseK Matrix(N Items(ColumnSplitBy), 2));
//Create a combinaed datatable with stats (Table 2)
ow = dt << Oneway(
Y(Eval(MetricCols)),
X(Eval(SplitBy)),
Means and Std Dev(1),
SendToReport(
Dispatch({"Means and Std Deviations"}, "Std Dev Lower 95%", NumberColBox,
{Visibility("Visible")}
),
Dispatch({"Means and Std Deviations"}, "Std Dev Upper 95%", NumberColBox,
{Visibility("Visible")}
)
),
Invisible
);
tb = Report(ow[1])[Outline Box("Means and Std Deviations"), Table Box(1)];
dt2 = tb << Make Combined Data Table;
tb << close window;
//Create a nested list with each pair of levels
Listdt3 = As List(NChooseK Matrix(N Items(ColumnSplitBy), 2));
mcols = N Items(MetricCols);
l = Repeat(Listdt3, mcols);
m = J(N Items(l) / mcols, mcols) :*</img> (1::mcols);
mfinal = Sort Ascending(Shape(m, 1, N Items(l)));
dt3 = As Table(Matrix(l), <<COLUMN names=""></COLUMN>
</CODE><NOSCRIPT><CODE class=" language-jsl"><DIV class="lia-spoiler-noscript-container"><DIV class="lia-spoiler-noscript-content">Nombres predeterminados aquí (1); //Crear tabla con distribución aleatoria (tabla 1) dt = Nueva tabla ("tabletest", Agregar filas (5000), Nueva columna ("Producto", Carácter, "Nominal", fórmula ("Producto " || Carácter (Entero aleatorio (1, 4))) ), Nueva columna ("Métrica1", Numérico, "Continuo", fórmula (Normal aleatorio (10, 1))), Nueva columna ("Métrica2", Numérico, "Continuo", fórmula (Gamma aleatorio (10, 1))), Nueva columna ("Métrica3", Numérico, "Continuo", fórmula (Gamma aleatorio (10, 2))), ); Columna (dt, "Métrica1") << establecer propiedad ("límites de especificación", {LSL (5), USL (25), Mostrar límites (0)}); Columna(dt, "Metric2") << establecer propiedad("límites de especificación", {LSL(0), USL(40), Mostrar límites(0)}); Columna(dt, "Metric3") << establecer propiedad("límites de especificación", {LSL(5), USL(40), Mostrar límites(0)}); SplitBy = "Producto"; Resumir(ColumnSplitBy = por(Column(SplitBy))); MetricCols = dt << Obtener nombres de columna(numérico, continuo, "cadena"); ColumnSplitByCombo = Como lista(NChooseK Matriz(N elementos(ColumnSplitBy), 2)); //Crear una tabla de datos combinada con estadísticas (Tabla 2) ow = dt << Oneway( Y(Eval(MetricCols)), X(Eval(SplitBy)), Medias y Desviación Estándar(1), SendToReport( Dispatch({"Medias y Desviaciones Estándar"}, "Desviación Estándar Inferior 95%", NumberColBox, {Visibility("Visible")} ), Dispatch({"Medias y Desviaciones Estándar"}, "Desviación Estándar Superior 95%", NumberColBox, {Visibility("Visible")} ) ), Invisible ); tb = Report(ow[1])[Outline Box("Medias y Desviaciones Estándar"), Table Box(1)]; dt2 = tb << Crear tabla de datos combinados; tb << cerrar ventana; //Crea una lista anidada con cada par de niveles Listdt3 = As List(NChooseK Matrix(N Items(ColumnSplitBy), 2)); mcols = N Items(MetricCols); l = Repeat(Listdt3, mcols); m = J(N Items(l) / mcols, mcols) (1::mcols); mfinal = Sort Ascending(Shape(m, 1, N Items(l))); dt3 = As Table(Matrix(l), < <COLUMN names=""></COLUMN></DIV></DIV>
<P><SPAN class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><span class="lia-inline-image-display-wrapper" image-alt="jthi_1-1724774273332.png" style="width: 400px;"><img src="https://us.v-cdn.net/6038925/uploads/images/67576i7A33F8F17FEAD03D/jthi_1-1724774273332.png" role="button" title="jthi_1-1724774273332.png" alt="jthi_1-1724774273332.png" /></span></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></P>
<P><SPAN class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="jthi_2-1724774284315.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_2-1724774284315.png" style="width: 304px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_2-1724774284315.png" style="width: 304px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_2-1724774284315.png" style="width: 304px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_2-1724774284315.png" style="width: 304px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_2-1724774284315.png" style="width: 304px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_2-1724774284315.png" style="width: 304px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_2-1724774284315.png" style="width: 304px;"><span class="lia-inline-image-display-wrapper" image-alt="jthi_2-1724774284315.png" style="width: 304px;"><img src="https://us.v-cdn.net/6038925/uploads/images/67577i906F6E675ADD899F/jthi_2-1724774284315.png" role="button" title="jthi_2-1724774284315.png" alt="jthi_2-1724774284315.png" /></span></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></P>
<P><SPAN class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><SPAN class="lia-inline-image-display-wrapper" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><span class="lia-inline-image-display-wrapper" image-alt="jthi_0-1724774190734.png" style="width: 400px;"><img src="https://us.v-cdn.net/6038925/uploads/images/67575i9361F045B1E7C693/jthi_0-1724774190734.png" role="button" title="jthi_0-1724774190734.png" alt="jthi_0-1724774190734.png" /></span></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></P>
<P> </P> Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasHola Jarmo,
Gracias por su rápida respuesta.
Esta línea está fallando
m = J(N Items(l) / mcols, mcols) (1::mcols);
¿Podrías explicarme qué se pretende hacer?
No suelo utilizar Matrices, he intentado depurarme pero no he podido...
Gracias por tu ayuda
Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasPor alguna razón falta:*
m = J(N Items(l) / mcols, mcols) :*</img> (1 :: mcols);
El propósito es crear una matriz como ésta

que luego conviertes a

usando forma.
También use los bucles, generalmente son mucho más fáciles de leer y comprender que los "trucos" extraños de matrices.
Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasHola jarmo,
Probé varias versiones de esta línea, agregando caracteres especiales que podrían perderse en la traducción, pero no...
Esta línea no funciona en mi caso (estoy usando JMP Pro 17)

Está bien, esta optimización con Matrix es realmente buena. Como dijiste, usar bucles también funciona, así que lo haré.:)
¡Gracias de nuevo por tomarte el tiempo para ayudarme (una vez más)!

- Predicador
Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasHe adjuntado la parte del código que falla en la comunidad como captura de pantalla.

Es la multiplicación elemento por elemento de matrices E Mult()
La comunidad JMP de @Ryan_Gilmore está comiendo:* (:* espacio añadido aquí)
Bloque de código JSL:
m = J(N Items(l) / mcols, mcols) :*</img> (1::mcols);
Texto:
m = J(N elementos(l) / mcols, mcols):* (1::mcols);
Captura de pantalla durante la edición

Captura de pantalla de la vista previa

Re: [JSL] Script para prueba T de dos lados y uno en una gran cantidad de métricasHola @Voizingu Dicho todo esto, parece que tienes una solución que te funciona. ¡Excelente! Sin embargo, para que no se pierda, el intervalo de confianza debe ser un intervalo de 100(1-2alfa)%. Es decir, si quieres que tu tasa de error de tipo 1 sea del 5%, entonces el intervalo utilizado para juzgar la equivalencia debe ser un intervalo de confianza del 90% (no del 95%). Es una prueba de dos caras ... cada cara se prueba con alfa = 0,05. Ver adjunto,