Sintaxis aceptable inconsistenteAllCols = dt << Get Column Names();
AllColNames = dt << Get Column Names(string);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));
nnm2 = Col Number(newcol);
if(nnm2 == nnm1,
newcol << delete formula();
dt << Delete Columns(colcol);
newcol << set name(cname), //else
dt << Delete Columns(newcol);
);
);Estoy usando JMP18 en Windows. Cuando escribo código que quiero usar con frecuencia, me gusta crear un botón de barra de herramientas para él. Pero a veces encuentro que el código que es aceptable para ejecutarse desde una ventana de script abierta no es aceptable cuando se ejecuta desde un ícono de la barra de herramientas. Aquí hay un ejemplo, donde estoy recorriendo columnas y cambiándolas de caracteres a numéricas si todas las entradas son numéricas. Cuando se ejecuta desde una barra de herramientas, la objeción es por la línea:
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));
¡Esta sintaxis es aceptable cuando ejecuto desde una ventana de script!
¿Por qué el intérprete jsl sería diferente en cada situación?
¿Qué sintaxis para esto sería universalmente aceptable?
En el caso de conjuntos de datos y columnas muy grandes, esto puede resultar lento. ¿Existe una mejor manera de hacerlo?
Automatización y scriptingCombinación y limpieza de datosVentanasRe: Sintaxis aceptable inconsistente¿Qué mensaje de error recibiste? Creo que la ventana de script tiene una tabla de datos actual, pero es posible que el script de la barra de herramientas no. Tal vez solo esté agregando
currentDatatable(dt);
Al principio del guión sería suficiente.
Tú podrías
print(currentDatatable());
al inicio del script para averiguar qué sabe el script de la barra de herramientas.
Re: Sintaxis aceptable inconsistenteEs posible que las barras de herramientas estén agregando Nombres predeterminados aquí (1) (que digo que siempre debería tener) a su secuencia de comandos

que potencialmente pueden mostrar algunos errores que no serían visibles si todo estuviera en el ámbito global (pero aun así, suelen ser errores). Construyo mis fórmulas usando Eval(EvalExpr()) o Eval(Substitute())
Eval(EvalExpr(
newcol = dt << New Column("Temp", numeric, formula(Num(Expr(NameExpr(AsColumn(dt, i))))));
));
Para conjuntos de datos grandes, es posible que tengas que considerar tomar un muestreo para algo como esto. También hay soluciones sin fórmulas que pueden ser más rápidas, pero también pueden depender de tus datos.
Por alguna razón no he publicado esto en la comunidad JMP (o al menos no pude encontrarlo), Columnas de caracteres a numéricos (github.com) , puedes echar un vistazo al código para ver cómo he realizado esta tarea desde https://github.com/jthi-jmp-tools/character-columns-to-numeric/blob/main/bin/bin/character_columns_to_numeric.jsl . Desde entonces he rediseñado este script y, si recuerdo correctamente, actualmente uso un método un poco diferente.
Re: Sintaxis aceptable inconsistenteLos iconos de la barra de herramientas conocen la tabla de datos actual():
El problema es más bien el inverso.
Por favor, defina "dt=current data table()" antes de
dt << Get Column Names();
Re: Sintaxis aceptable inconsistenteGracias Craig.
El mensaje de error es:
Nombre sin resolver: 2PCS_X(um) en la fila 1 en el acceso o evaluación de '2PCS_X(um)', "2PCS_X(um)"n /*###*/
"2PCS_X(um)" es el nombre de la primera columna que se evaluará.
Entonces la expresión para la primera columna se supone que es:
newcol = dtTOSA << nueva columna("Temp", numérico, fórmula(Num( :"2PCS_X(um)"n )));
Re: Sintaxis aceptable inconsistenteAdemás, en el script actual no utilizo dt como variable. Utilizo una variable mucho más descriptiva, como dtTOSA (ver arriba). Cada vez que quiero hacer referencia a una tabla de datos en particular, siempre lo indico explícitamente para evitar confusiones, como hice en esta declaración. No veo cómo el intérprete podría pensar que me estoy refiriendo a una tabla diferente.
Re: Sintaxis aceptable inconsistenteQuizás, pero el mensaje de error sugiere que JMP no está buscando "2PCS_X(um)"n en el lugar correcto. No estoy seguro de qué contexto puede utilizar el script de la barra de herramientas.
Re: Sintaxis aceptable inconsistenteCraig,
Utilicé su sugerencia de utilizar una declaración de impresión y descubrí que nunca llegó a la línea que pensé que era el problema.
newcol = dt << new column("Temp", numeric, formula(eval(evalexpr(Fexpr))));El problema estaba en esta línea.
nnm1 = Col Number(colcol);
Entonces lo cambié a esto y funcionó.
nnm1 = dtTOSA << Col Number(colcol);
Descubrí que también necesitaba recordarle a JMP nuevamente cuál era la tabla actual más adelante en el código y tuve que usar {} en Eliminar columnas para que funcionara. Por alguna razón, a JMP no le importa si esas llaves están allí si se ejecuta desde una ventana de script.
El fragmento de código ahora es el siguiente. Por cierto, este es un fragmento de un código más largo que también crea esta tabla y otra.
AllCols = dtTOSA << Get Column Names();
AllColNames = dtTOSA << Get Column Names(string);
currentDatatable(dtTOSA);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = dtTOSA << Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dtTOSA << new column("Temp", numeric, set formula(eval(evalexpr(Fexpr))));
nnm2 = dtTOSA << Col Number(newcol);
if(nnm2 == nnm1,
currentDatatable(dtTOSA);
newcol << delete formula();
dtTOSA << Delete Columns({colcol});
wait(0);
newcol << set name(cname), //else
dtTOSA << Delete Columns({newcol});
);
);Sin embargo, ahora hay un problema diferente. Aunque borro la columna anterior antes de cambiar el nombre de la nueva, JMP coloca un 2 no deseado después del nombre de la columna. Nuevamente, no hace esto al ejecutarse desde una ventana de script. Intenté incluir una declaración wait(0) pero eso no ayudó.
¿Cómo se puede solucionar esto?
Re: Sintaxis aceptable inconsistenteEn realidad, no está realizando la operación de eliminar columna.
dtTOSA << Delete Columns({colcol});De esta manera se conserva la columna de caracteres original y la nueva columna numérica.
Re: Sintaxis aceptable inconsistenteCuriosamente, si pasa a la condición "else", la declaración de eliminación funciona bien.
Re: Sintaxis aceptable inconsistenteAunque se hace referencia a la columna de dos maneras diferentes (una como el nombre de la cadena de texto y la otra como un tipo de columna), esto finalmente funcionó. He estado escribiendo código en jsl durante 18 años y todavía encuentro que hacer referencia a columnas como variables en jsl es inconsistente y confuso.
AllCols = dtTOSA << Get Column Names();
AllColNames = dtTOSA << Get Column Names(string);
currentDatatable(dtTOSA);
for(i=3, i<=n items(AllCols), i++,
colcol = AllCols[i];
cname = AllColNames[i];
nnm1 = dtTOSA << Col Number(colcol);
Fstr = "num(:\!""||cname||"\!"n)";
Fexpr = parse(Fstr);
newcol = dtTOSA << new column("Temp", numeric, set formula(eval(evalexpr(Fexpr))));
nnm2 = dtTOSA << Col Number(newcol);
if(nnm2 == nnm1,
currentDatatable(dtTOSA);
newcol << delete formula();
dtTOSA << Delete Columns(eval(cname));
currentDatatable(dtTOSA);
newcol << set name(cname), //else
dtTOSA << Delete Columns({newcol});
);
);Re: Sintaxis aceptable inconsistenteSiempre utilizo cadenas como base de columnas y luego construyo mis referencias a partir de ellas según sea necesario. He descubierto que esta es la forma más flexible de gestionar distintos tipos de referencias.
Por ejemplo, si tomo su script y hago algunas suposiciones sobre su propósito (comenzando desde la tercera columna, recorrerlas todas, crear una nueva columna de fórmula en la que cambie los valores a numéricos, compare los valores faltantes y realice acciones en función del resultado), lo escribiría así en JMP18:
Names Default To Here(1);
dtTOSA = New Table("Untitled",
Add Rows(4),
Compress File When Saved(1),
New Column("Column 1",
Numeric,
"Continuous",
Format("Best", 12),
Set Values([1, 2, 3, 4])
),
New Column("Column 2", Character, "Nominal", Set Values({"A", "1", "2", "3"})),
New Column("Column 3", Character, "Nominal", Set Values({"1", "2", "3", "4"})),
New Column("Column 4", Character, "Nominal", Set Values({"A", "B", "C", "D"}))
);
wait(1); // demo purposes
cols = dtTOSA << Get Column Names("String");
// Remove From(cols, 1, 2); // skipping this for demo purposes
For Each({colname}, cols,
// colname = cols[3]; // for testing
Eval(EvalExpr(
newcol = dtTOSA << New Column("Temp", numeric, Formula(
Num(Expr(Name Expr(As Column(dtTOSA, colname))))
));
));
newcol << Run Formulas;
newcol << delete formula();
orig_vals = Col Number(Column(dtTOSA, colname));
new_vals = Col Number(Column(dtTOSA, (newcol << get name)));
If(orig_vals == new_vals,
dtTOSA << Delete Columns(colname);
newcol << set name(colname);
,
dtTOSA << Delete Columns(newcol)
);
);
Y si tuviera que hacer esto desde el principio pero aún usando una idea similar a la tuya, haría algo como esto (al menos en este momento).
Names Default To Here(1);
dt = New Table("Untitled",
Add Rows(4),
Compress File When Saved(1),
New Column("Column 1",
Numeric,
"Continuous",
Format("Best", 12),
Set Values([1, 2, 3, 4])
),
New Column("Column 2", Character, "Nominal", Set Values({"A", "1", "2", "3"})),
New Column("Column 3", Character, "Nominal", Set Values({"1", "2", "3", "4"})),
New Column("Column 4", Character, "Nominal", Set Values({"A", "B", "C", "D"}))
);
wait(1); // demo purposes
cols = dt << Get Column Names("Character", "String"); // We only need to check character columns
For Each({colname}, cols,
dt_summary = dt << Summary(
Group(Eval(colname)),
Freq("None"),
Weight("None"),
Link to original data table(0),
output table name("Summar of " || colname),
Private
);
orig_vals = Col Number(Column(dt_summary, 1));
Column(dt_summary, 1) << Set Data Type("Numeric");
new_vals = Col Number(Column(dt_summary, 1));
Close(dt_summary, no save);
If(orig_vals == new_vals, // we can change
Column(dt, colname) << Set Data Type("Numeric");
Column(dt, colname) << Set Modeling Type("Continuous");
);
);
Suelo hacer esto utilizando Transform Each y Num, pero a veces puede ser suficiente si dejas que JMP haga las conversiones (aunque hay casos en los que quizás no quieras hacer esto..., mira Num() desde el índice de scripts y cómo se comporta con Num("5%") por ejemplo. << Restrict evita este comportamiento)
Re: Sintaxis aceptable inconsistentejarmo,
Gracias por tus comentarios. A veces tengo miles de columnas y decenas de miles de filas, y la velocidad puede ser un problema. La forma en que lo hago ahora en realidad redujo el tiempo a aproximadamente 1/3 de la forma en que lo hacía antes, pero aún así puede llevar muchos minutos. Podría experimentar con tu método en algún momento, pero me pregunto si crees que hacer una tabla de resumen cada vez sería más lento o más rápido.
En cuanto al manejo de referencias de columnas, he intentado usar Column(dt, colname) pero no siempre funciona. Sin embargo, en tu script también usas As Column(). Como regla general, ¿se usa As Column() para fórmulas?
Re: Sintaxis aceptable inconsistente
@pcarroll1 escribió:
Como regla general, ¿se utiliza As Column() para fórmulas?
Sí, ya sea como columna(nombrecolumna) o :columna(dt, nombrecolumna)[]
Re: Sintaxis aceptable inconsistentePara la primera "pregunta": si intentas optimizar la velocidad para casos de uso específicos, solo tienes que probar cuál funciona mejor, ya que hay muchos métodos diferentes y el más rápido puede depender mucho de tus datos. En este caso, utilicé una tabla de resumen (o Summarize) para evitar verificar valores duplicados, pero, como mencioné, depende de tus datos, de tus versiones de JMP y posiblemente de otras cosas...
Para referencias de columnas; mi regla es: siempre use el nombre de la columna y la referencia de la tabla como punto de partida, y construya la referencia final a partir de ellos . Lo que considero una referencia depende de dónde y cómo lo esté usando. Por ejemplo, si estoy construyendo una fórmula, quiero que mi fórmula haga referencia a las columnas como :mycol (y no AsColumn(mycol) o Column(mycol)[], que generalmente también están bien). Y puedo obtener el formato :mycol usando el estilo bastante largo de NameExpr(AsColumn(dt,colname)) con evaluación de expresión. Generalmente AsColumn(dt, colname) (OR dt:colname) es lo mismo que Column(dt, colname)[Row()] (o Column(dt, colname)[]), que obtendrá el valor de la fila actual (en mi mente esto no siempre es así. Por ejemplo, en plataformas considero que :mycol es una referencia en lugar de un valor específico en una fila...).
Como ejemplo, esto devolverá el valor faltante, ya que mycol se evaluará inmediatamente y el valor inicial de Row() es 0 en JMP (no es un problema dentro de una fórmula, ya que Row() se actualizará todo el tiempo).
Names Default To Here(1);
dt = open("$SAMPLE_DATA/Big Class.jmp");
colname = "age";
mycol = NameExpr(AsColumn(dt,colname));
show(mycol)
Esta es la diferencia más difícil de entender con las referencias de columna, como Column() (valor de la fila actual) vs Column() (referencia a una columna) en JMP. Es muy difícil entender cómo funcionan... Lo he aprendido básicamente fallando muchas, muchas veces. Y ahora tengo una buena idea de cuál usar inicialmente al crear una expresión (es posible que todavía me equivoque de vez en cuando). Este tema es algo sobre lo que deseo escribir una publicación en el blog en algún momento, ya que mis opiniones (actualmente) pueden diferir de las de los demás y me considero un buen programador de JMP sin ser un desarrollador de JMP (JSL).
Una cosa a tener en cuenta : no soy un desarrollador de JMP y realmente no me importa cómo piensan los desarrolladores que funciona JSL (a veces discrepo con ellos). Todas mis respuestas siempre se basan en mis experiencias con las versiones de JMP que he usado.
Lo siento mucho si esta publicación es un poco confusa (puede que haya tomado algunas cervezas esta noche):) ). Si deseas obtener más explicaciones sobre mis opiniones sobre este tema, puedes publicar en este tema (pero etiquétame con @ para que lo note más fácilmente). También puedes enviarme un mensaje privado si es necesario.