¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Un debate comunitario reciente Re: script para encontrar y reemplazar un valor numérico con un valor faltante en toda la tabla tuvo una muy buena entrada proporcionada por @SDF1 que se seleccionó como solución
names default to here(1);
dt=current data table();
For( i = 1, i <= N Rows( dt ), i++,
For( l = 1, l <= N Cols( dt ), l++,
If( Column( l )[i] == 999,
Column( l )[i] = .
)
)
);
Cuando vi la solución, me vino a la mente una vieja enseñanza que decía más o menos: "No recorras una tabla de datos.El bucle es lento. Use las capacidades integradas de JMP"
Entonces comencé a modificar @SDF1 código.
Determiné que mi estándar alternativo de "Obtener filas donde ()" sería más rápido.Después de todo, permite que todas las filas encontradas se establezcan con una sola instrucción de asignación, después de que el "muy rápido" Obtener filas donde() haya encontrado las filas.
Otra solución, que he visto. empujado recomendada por la comunidad es la instalación de Recode en JMP.
Asi que:
Hipótesis: Las funciones integradas de JMP de Obtener filas donde y Recodificar serán más rápidas que Bucle hacia abajo y en toda una tabla de datos.
Seleccioné la tabla de datos Capacidad de semiconductores como una tabla de buen tamaño, 132 columnas y 1455 filas
Escribí un pequeño script que establece 10 valores aleatorios en cada columna en 999.
names default to here(1);
dt=current data table();
for(i=5,i<=ncols(dt),i++,
if(col stddev(ascolumn(dt,i))!=0,
for(k=1,k<=10,k++,
column(dt,i)[randominteger(1,1455)]=999;
)
));
Este es el valor original que se cambiará en la Discusión mencionada anteriormente.El 999 debe cambiarse a faltante (.) por los scripts.
Método:
- Puesta en marcha JMP 16.
- Leer en la tabla de datos de capacidad de semiconductores
- Ejecute el script para establecer los valores 999 en cada columna
- Repita los 2 pasos anteriores para cada uno de los 3 métodos y recodifique el tiempo hasta el milisegundo.
- Repita los pasos anteriores para JMP15
Aquí está el guión para el Recode
Names Default To Here( 1 );
dt = Current Data Table();
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
dt << Recode Column(
As Column( dt, i ),
{If( _rcNow == 999, ., _rcNow )},
Target Column( As Column( dt, i ) )
)
);
show(hptime()-start)
El código para obtener filas donde
names default to here(1);
dt=current data table();
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[dt<<GET rows="" where=""></GET>
Y aquí están los resultados sorprendentes

El For Loop gana sin dudas.Y a partir de los resultados, Obtener filas donde debe usarse con moderación.
Se agradecen comentarios y correcciones......
Tabla de datossecuencias de comandosRe: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Si entiendo @brady_brady . Sin embargo, uno podría hacer esto:
NamesDefaultToHere(1);
// Make an example table with some rogue '999' values
nr = 10; // Number of rows
nc = 10; // Number of columns
p = 0.1; // Fraction of rogue values
val = 999; // Rogue value
m = J(nr, nc, RandomInteger(1, 10));
m = Shape(m, nr*nc, 1);
m[RandomIndex(nr*nc, Floor(nr*nc*p))] = val;
m = Shape(m, nr, nc);
dt = AsTable(m);
dt << setName("Rogue Values");
Wait(3);
// Set rogue values to '.'
mask = J(nr*nc, 1, 1);
mask[Loc(dt[0,0] == val)] = .;
mask = Shape(mask, nr, nc);
dt[0,0] = mask :*</img> dt[0,0];
Pero dudo mucho que esto sea más rápido que lo que ya tienes, sobre todo debido a la multiplicación inteligente de elementos.
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Tiene la comparación un poco mal aquí: la funcionalidad "Obtener filas donde" evaluará su expresión en cada fila, por lo que debe minimizar las búsquedas superfluas antes de ejecutar el ciclo; en particular, su iterador es externo y tendrá que caminar un poco para encontrarlo en cada fila.Además, evaluará la función AsColumn en cada fila, buscando tanto 'dt' como 'i' cada vez.Esto es _muy_ caro.La forma correcta de hacerlo sería la siguiente:
names default to here(1);
dt=current data table();
start=hptime();
names = dt << Get Column Names( "string" );
for(i=5,i<= ncols(dt),i++,
Eval( Parse( Eval InserT( JSL Quote(
column(dt,i)[dt<<GET rows="" where=""></GET>
Notará que esto se ejecuta en el orden de 3000 veces más rápido, lo que debería ponerlo comparable, si no mejor, que los otros métodos que tiene.
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Sí... el obstáculo es que, si bien se puede acceder a las matrices con un solo índice (que supone un diseño de fila principal), no se puede acceder a las tablas de datos. Por lo tanto, lamentablemente no hay análogo a
m[ubicación(m, 9999)] = .
donde m es una matriz pxq, en el mundo de la tabla de datos... de ahí las líneas que preceden y siguen a esta en el código anterior. Si bien el código sigue siendo rápido, sería REALMENTE rápido si el acceso de índice único estuviera disponible para las tablas.
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Para el ' sobrecarga en la creación de una matriz' sospecho que subíndice de la tabla de datos podría ayudar.@Craige_Hales sabría.
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Aquí hay una forma de evitar el bucle, aunque hay una sobrecarga en la creación de una matriz y la asignación posterior a la tabla. Este enfoque funciona rápidamente con estos datos, casi igual que loc_2d, pero a medida que la tabla se vuelve enorme, esperaría que su rendimiento relativo sufriera, pero no lo he verificado.
expr_mat = expr(
m = dt[0, 5 :: N Col( dt )];
m[Loc( m, 999 )] = .;
dt[0, 5 :: N Col( dt )] = m;
);
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Quizás profundicé demasiado en esto (pero al menos obtuve una idea de cómo hacer este tipo de prueba más adelante si es necesario).
Escribí una especie de script de prueba con diferentes expresiones para probar diferentes formas de reemplazar el 999 por el que falta. Probé con dos conjuntos de datos (Semiconductor Capability.jmp y Probe.jmp) y cambié la cantidad de valores faltantes en las columnas.
Loc() es el más rápido en reemplazar valores (no es de extrañar). Missing Value Codes() es "siempre" rápido, pero tiene casos de uso un poco más limitados en comparación con Loc() y es un poco más complicado de usar si hay varios valores para reemplazar.

El más rápido terminó siendo loc_2d (no se molestó en cambiar los nombres de las variables de esto: Uso de Loc con una matriz 2D ). Todavía tengo que hacer un bucle para establecer los valores (al menos que yo sepa), por lo que lo más probable es que la razón por la que esto sea más rápido que solo Loc() es que omitirá algunas columnas. A continuación se muestran expr_loc2d y expr_loc:
expr_loc = Expr(
for(i=5,i<= ncols(testData),i++,
column(testData,i)[Loc(testData[0,i], 999)]=.;
);
);
expr_loc_2d = Expr(
mat = testData[0,5::NCols(testData)];
nr = N Rows(mat);
nc = N Cols(mat);
blacklocs = Loc(mat, 999) - 1;
rowlocs = Floor((blacklocs) / nc ) + 1;
collocs = Mod((blacklocs), nc ) + 1 + 4;
uniqCols = Associative Array(collocs) << get keys;
For(i_2d = 1, i_2d <= N Items(uniqCols), i_2d++,
testData[rowlocs[Loc(collocs, uniqCols[i_2d])], uniqCols[i_2d]] = .;
);
);
También parece ser importante usar AsColumn(Column(dt, i)) como @Ryan_Gilmore dijo (ni siquiera me molesté en probar << obtener filas donde sin él, porque será muy lento), pero probé la recodificación con y sin Column(). También me acabo de dar cuenta de que me perdí una columna () de recodificar, así que ejecute un par de pruebas rápidas (no adjuntas en datos o secuencia de comandos):
Diferencia bastante grande entre estos tres:
expr_recode_ascolumn = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(dt, i),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(testData, i))
)
);
);
expr_recode_ascolumn_column = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(dt, i),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(Column(testData, i)))
)
);
);<BR />
expr_recode_ascolumn_column_column = Expr(
For(i = 5, i <= N Cols(testData), i++,
testData << Recode Column(
As Column(Column(dt, i)),
{If( _rcNow == 999, ., _rcNow)},
Target Column(As Column(Column(testData, i)))
)
);
);

El script de prueba y algunos datos de resultados adjuntos. Lo más probable es que el guión tenga algunos errores aquí y allá. Debería ser bastante fácil agregar nuevas expresiones si alguien quiere hacerlo. Una cosa que no probé en absoluto es usar << Begin/End data update.
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?¡Interesante debate! La notación de matriz de tabla de datos, que creo que apareció por primera vez en JMP 13, es bastante eficiente para la asignación.
Para este problema en particular, es casi 500 veces más rápido que el bucle for (0,003 frente a 1,5 segundos, computadora portátil JMP 15 Mac).
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
Try( dt[Loc( dt[0, i], 999 ), i] = . )
);
Show( HP Time() - start);
Se necesita try() para evitar un error en las columnas sin "999".
Todavía es necesario recorrer las columnas porque la asignación directa para toda la matriz, que por un momento pensé que sería aún más rápida, no funciona. Error: "Múltiples subíndices requieren subíndices únicos en cada nivel interno en acceso o evaluación...", lo que, pensándolo bien, tiene sentido.
// The left Data Table matrix syntax is OK on its own, but not for assignment.
dt[0, 5 :: N Col( dt )][Loc( dt[0, 5 :: N Col( dt )], 999 )] = .;
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Es posible que haya desconfiado de usar bucles en algún momento, pero después de tomar un curso de algoritmos (que nunca terminé...) un poco menos. Solo tiene que ser eficiente con ellos, a menudo hace demasiados bucles (complejidad de tiempo y notación Big O). Además, usar bucles es mucho más fácil que tratar de resolver problemas con matemáticas puras.
Con JMP, por lo general, solo uso las opciones seguras que he aprendido en algún momento, ya que la velocidad rara vez es un problema. Pero cuando el tiempo es un problema (una aplicación tardó más de 6 minutos en abrirse en el trabajo), comenzaré a buscar formas de hacerlo más rápido (logré que se iniciara en menos de 2 minutos). En este caso logré hacerlo más rápido usando Loc() con diferentes combinaciones de otras funciones (eliminar fila, establecer estado de fila).
Además de Loc(), usar Summarize() parece ser "mucho" más rápido en comparación con mi opción favorita desde hace mucho tiempo para obtener valores únicos en la columna (matriz asociativa() << obtener claves). Cuando el recuento de filas aumenta, la matriz asociativa () << get keys se vuelve cada vez más lenta y Summarize () no parece sufrir el mismo problema, al menos no tan rápido. Así que he intentado comenzar a usar Summarize() más (nunca se sabe qué tan altas son las tablas de datos).
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Entonces omitimos AsColumn() con (vamos más con una corazonada y no he probado que esto reemplace todos los valores correctos):
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[Loc(dt[0,i], 999)]=.;
if(mod(i,10)==0,show(i));
);
show(hptime()-start);
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Estoy de acuerdo en que Loc() es el mejor método.
Para mí, la desaceleración parece ocurrir debido a As Column. Veo una diferencia significativa entre,
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( column(dt, i) );
);
Show( HP Time() - start );
y
start = HP Time();
For( i = 5, i <= N Cols( dt ), i++,
As Column( dt, i );
);
Show( HP Time() - start );
Cambiando la sintaxis de As Column en el código de @txnelson pregunta original, el bucle For sigue siendo el más rápido, pero los demás son más comparables.
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Hola @txnelson ,
Comparación muy interesante, y tampoco los resultados que hubiera esperado. Recuerdo cuando comencé a aprender programación en la universidad y también me enseñaron algo similar: tenga cuidado con los bucles porque son lentos.
Sin duda, con algunos cálculos y en algunos programas, he descubierto que los bucles For son muy lentos, más lentos que las alternativas. Sin embargo, como no soy tan buen programador, tiendo a recurrir a lo que sé hacer, que no siempre es rápido o elegante. A veces, para mí, la lógica de un bucle me permite escribir un código funcional para hacer lo que quiero que haga, lo que me permite pasar a los siguientes pasos.
Me parece interesante que las opciones For Loop y Recode sean un poco más lentas para JMP 16 en comparación con 15, pero Get Rows Where mejoró al reducir el tiempo casi a la mitad, eso es un cambio sustancial.
¡Gracias!,
SD
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?Quizás @Craige_Hales podría tener una solución aún más rápida. Se me ocurrió la idea de usar los cálculos Matrix (especialmente Loc()) de las publicaciones/blog de su comunidad.
Hice una herramienta en el trabajo más rápido que abre el conjunto de datos y luego elimina los datos antiguos (según la selección del usuario). Originalmente usaba << Seleccionar dónde y Eliminar filas. Lo reemplacé con un poco de magia Loc() y también se volvió mucho más rápido. Últimamente me ha gustado mucho Loc().
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?@jthi ,
Su código basado en matriz fue el más rápido.
0.299795 segundos ........
Re: ¿Debería recorrer una tabla de datos o usar Recode, o usar Get Rows Where para cambiar valores en una tabla de datos?He estado optimizando en el trabajo algunos scripts antiguos que tenemos. Algunas de las cosas que tuve que cambiar estaban relacionadas con obtener filas donde. Empecé a usar más y más operaciones matriciales (principalmente Loc()) si necesito tener un código JSL rápido.
Por ejemplo, podría cambiar las filas de obtención a las siguientes (no probó si reemplaza los valores correctos o todos ellos):
start=hptime();
for(i=5,i<= ncols(dt),i++,
column(dt,i)[Loc(ascolumn(dt,i) << get as matrix, 999)]=.;
if(mod(i,10)==0,show(i));
);
show(hptime()-start);
También existe la opción de usar etiquetas de valores de propiedades de columna y códigos de valores faltantes que podrían funcionar en algunos casos. Me gusta usarlos cuando sea posible, porque entonces no cambiará los datos.