Optimización de un código JSL (lento) basado en bucles FOR()Hola,
Necesito escribir un código para el análisis de datos de confiabilidad.
Se miden varias métricas (métrica1, métrica2,...métricaN) antes de que comience el experimento (REL_T0) y luego se miden nuevamente después de cada evento de caída (DROP1, DROP2,... DROPN...).
Mi objetivo es calcular el valor de deriva entre REL_T0 y cada medida después de los eventos de caída para cada número de serie y cada métrica.
Mi código inicial se basa en bucles FOR() (que sé que definitivamente no son óptimos) y funciona bien con conjuntos de datos pequeños.
No obstante, necesito optimizar mi código para que se ejecute más rápido con conjuntos de datos más grandes.
Actualmente, con 10000 filas y 50 métricas, el proceso tarda unos 20 minutos en finalizar, lo cual es demasiado.
Intenté organizar los bucles FOR() de manera diferente pero sin ningún impacto significativo.
Estoy seguro de que hay formas de realizar estos cálculos simples de una manera mucho más rápida, utilizando comandos propietarios de JSL.
¿Qué piensan ustedes?
El siguiente código crea una tabla ficticia y luego ejecuta mi código inicial para terminar con nuevas columnas "∆_metricN" con la deriva calculada.
¡Me gustaría hacer lo mismo usando comandos JSL en lugar de bucles FOR() que harían que mi código fuera increíblemente rápido!
Muchas gracias por adelantado.
Predicador.
//Create dummy table
RawData = New Table( "RawData",
Add Rows( 20 ),
New Column( "serial_number", Character, set values( {"A","A","A","A","A","A","A","B","B","B","B","B","B","C","C","C","C","C","C","C"})),
New Column( "rel_event", Character, set values( {
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5","DROP6",
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5",
"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5", "DROP6"})),
New Column("metric1", Continuous, set values({1,2,3,5,7,10,13,2,4,5,6,8,11,1,2,4,5,5,9,12}) ),
New Column("metric2", Continuous, set values({2,3,4,7,9,14,18,2,3,4,7,9,14,2,5,7,8,9,11,13}) );
);
RawData << save( "$Desktop\RawData.jmp" );
Column(RawData, "rel_event") << Set Property( "Value ordering", {"REL_T0","DROP1","DROP2","DROP3","DROP4","DROP5","DROP6"} );
//Create handle for data table to be analyzed
RawData = Current Data Table();
ChosenDataCol = {"metric1", "metric2"};
NumDataCol = N Items( ChosenDataCol );
NumRow = N Row( RawData );
RawDataCol = RawData << get column names;
Show( NumDataCol );
Show( ChosenDataCol );
Show( RawData );
Show( NumRow );
Show( RawDataCol );
//Scan each selected data row for which Deltas must be computed
For( cpt = 1, cpt <= NumDataCol, cpt++,
//Create column to receive delta data
DeltaCol = RawData << New Column( "∆_" || Char( ChosenDataCol[cpt] ), Numeric, "Continuous" );
TargetCol = Column( RawData, ChosenDataCol[cpt] );
serial_numberlist = {}; //List of unique serial_number's within the data table of interest
Show( cpt );
Show( ChosenDataCol[cpt] );
For( i = 1, i <= NumRow, i++,
Currentserial_number = :serial_number[i];
If( Contains( serial_numberlist, Currentserial_number ) == 0,
Insert Into( serial_numberlist, Currentserial_number )
);
);//For i
Show( serial_numberList );
Numserial_number = N Items( serial_numberlist );
Show( Numserial_number );
//Scan data table for each unique serial_number
For( i = 1, i <= Numserial_number, i++,
Currentserial_number = serial_numberList[i];
Currentserial_numberrows = RawData << GetRowsWhere( :serial_number == Currentserial_number ); //matrix of rows containing current serial_number
Show( Currentserial_numberrows );
NumCurrentserial_numberrows = N Row( Currentserial_numberrows );
Show( NumCurrentserial_numberrows );
show(i);
TzeroExist = 0;
TzeroVal = 0;
//identify Time Zero value for each serial_number under consideration
For( j = 1, j <= NumCurrentserial_numberrows, j++,
CurrentRead = :rel_event[Currentserial_numberrows[j]];
Show( CurrentRead );
If( CurrentRead == "REL_T0",
TzeroVal = TargetCol[Currentserial_numberrows[j]];
Show( TzeroVal );
TzeroExist = 1;
);
); //For j
//Calculate Drift
For( j = 1, j <= NumCurrentserial_numberrows, j++,
If( TzeroExist == 1,
DeltaCol[Currentserial_numberrows[j]] = TargetCol[Currentserial_numberrows[j]] - TzeroVal
)
); //For j
);//For i
);//For cpt Modelado estadístico avanzadoAutomatización y secuencias de comandosRe: Optimización de un código JSL (lento) basado en bucles FOR()Gracias Jarmo por tu respuesta.
Me gusta la idea de usar For Each Row(), simplifica todo el script.
Pero para agregar más contexto y hacer que el script sea más robusto, es posible que REL_T0 no siempre sea la primera fila por número de serie (la base de datos no siempre consulta los datos en el orden correcto en la tabla JMP), y también la deriva puede ser positiva. O negativo, así que no sé si Col Min() es adecuado aquí (tal vez no lo leí bien).
¿Es posible anclar start_row al valor REL_T0 por número de serie directamente?
Re: Optimización de un código JSL (lento) basado en bucles FOR()Sí, es posible con pequeños cambios directamente en ese script. Agregué una columna de depuración que puedes eliminar para mostrar que coincide con las filas correctas.
Names Default To Here(1);
dt = New Table("RawData",
Add Rows(27),
Compress File When Saved(1),
New Column("serial_number",
Character,
"Nominal",
Set Values(
{"A", "A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C", "C", "D", "D", "D", "D", "D", "D", "D"}
)
),
New Column("rel_event",
Character,
"Nominal",
Set Values(
{"REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "REL_T0", "DROP1",
"DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "DROP1", "DROP2", "DROP3", "REL_T0", "DROP4", "DROP5", "DROP6"}
)
),
New Column("metric1",
Numeric,
"Continuous",
Format("Best", 10),
Set Values([1, 2, 3, 5, 7, 10, 13, 2, 4, 5, 6, 8, 11, 1, 2, 4, 5, 5, 9, 12, 1, 2, 4, 5, 5, 9, 12])
),
New Column("metric2",
Numeric,
"Continuous",
Format("Best", 10),
Set Values([2, 3, 4, 7, 9, 14, 18, 2, 3, 4, 7, 9, 14, 2, 5, 7, 8, 9, 11, 13, 2, 5, 7, 8, 9, 11, 13])
)
);
debugcol = dt << New Column("REL_T0_ROW", Numeric, Continuous);
delta1col = dt << New Column("delta1", Numeric, Continuous);
delta2col = dt << New Column("delta2", Numeric, Continuous);
For Each Row(dt,
// find REL_T0 row for current serial_number
start_row = Col Min(If(:rel_event == "REL_T0", Row(), .), :serial_number);
debugcol[Row()] = start_row;
delta1col[Row()] = :metric1 - :metric1[start_row];
delta2col[Row()] = :metric2 - :metric2[start_row];
);

Re: Optimización de un código JSL (lento) basado en bucles FOR()¡Muchas gracias Jarmo!
Probé esta nueva versión de una tabla de datos con 18000 filas y 2 métricas:
- Método antiguo: ~5min
- tu método : <2min
¡¡¡Ya es una gran mejora!!!
¡¡Gracias de nuevo!!
Predicador
Re: Optimización de un código JSL (lento) basado en bucles FOR()Hola hogi,
Gracias por tu sugerencia, revisé tu publicación relacionada con el complemento de Normalización. Definitivamente lo tendré en cuenta para futuros proyectos.
Predicador
Re: Optimización de un código JSL (lento) basado en bucles FOR()Lo más probable es que se puedan realizar más mejoras de velocidad con bastante facilidad utilizando matrices para realizar esos cálculos. Añadiendo también
dt << begin data update;
antes del bucle y
dt << end data update;
Lo más probable es que también proporcione algunas mejoras de velocidad, especialmente si su tabla no está abierta como invisible/privada, ya que "retrasará" la actualización de los elementos visuales de la tabla.
Re: Optimización de un código JSL (lento) basado en bucles FOR()La normalización de los datos de medición es una tarea común en el Análisis de Datos.
La GUI de normalización proporciona muchas opciones para hacerlo.
En la GUI, el usuario puede especificar muchas configuraciones diferentes y el complemento genera el código correspondiente.
Las fórmulas generadas se basan en agregaciones Col... , como Col Min, Col Max, Col Median ...
otras opciones:

Para este caso de aplicación, puede utilizar "primero" (basado en la lógica ColMin(row()) )
... o especifique la referencia mediante "usar un subconjunto para la normalización" [para los casos en los que los valores de referencia no sean los "primeros"]:

Re: Optimización de un código JSL (lento) basado en bucles FOR()Una cosa que proporcionará (generalmente) una aceleración es eliminar todas las impresiones de los bucles, pero en este caso, reescribirlo sin esos bucles es más rápido y no usar el bucle for cuando se revisa la tabla de datos (para cada fila es una mejor opción).
Aquí hay una opción que, con suerte, arroja resultados más rápidos (esto también se puede convertir en una fórmula y debería ser bastante rápido si es necesario). Supone que los datos están ordenados de tal manera que REL_T0 siempre es la primera fila para cada número de serie; si ese no es el caso, será necesario un pequeño cambio.
Names Default To Here(1);
//Create dummy table
RawData = New Table("RawData",
Add Rows(20),
New Column("serial_number",
Character,
set values({"A", "A", "A", "A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "C", "C", "C", "C", "C", "C", "C"})
),
New Column("rel_event",
Character,
set values(
{"REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "DROP6", "REL_T0", "DROP1", "DROP2", "DROP3", "DROP4", "DROP5", "REL_T0", "DROP1",
"DROP2", "DROP3", "DROP4", "DROP5", "DROP6"}
)
),
New Column("metric1", Continuous, set values({1, 2, 3, 5, 7, 10, 13, 2, 4, 5, 6, 8, 11, 1, 2, 4, 5, 5, 9, 12})),
New Column("metric2", Continuous, set values({2, 3, 4, 7, 9, 14, 18, 2, 3, 4, 7, 9, 14, 2, 5, 7, 8, 9, 11, 13}))
);
delta1col = RawData << New Column("delta1", Numeric, Continuous);
delta2col = RawData << New Column("delta2", Numeric, Continuous);
For Each Row(RawData,
// find REL_T0 row for current serial_number
start_row = Col Min(Row(), :serial_number);
delta1col[Row()] = :metric1 - :metric1[start_row];
delta2col[Row()] = :metric2 - :metric2[start_row];
);
