Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculo.Fondo
* Uso de Python en el entorno de scripting JMP19.
* Creación de dataframe (df) desde pandas a partir de una tabla de datos JMP existente (dt).
* Función que requiere entrada de DataFrame y devuelve un nuevo resultado de DataFrame. Llamémosla df_results.
* Transferencia de resultados de vuelta a la columna de la tabla de datos JMP.
Esto funciona, pero hay dos cosas que me hacen sospechar que no lo estoy haciendo correctamente. Primero, recibo una advertencia en el registro local. Segundo, la primera vez que lo ejecuto con una gran cantidad de filas, tarda mucho. Mi temporizador indicaba 25 segundos para 100 000 filas. Si lo vuelvo a ejecutar, la segunda vez tarda 3 segundos. Configuré el temporizador para que me diera detalles de cada paso y así poder averiguar si alguna línea de código consumía la mayor parte del tiempo.
Esta es la única línea de código que me genera dudas. Tardó 25 segundos en procesar 100 000 filas SOLO la primera vez que se rellenó la columna inicial constante (o nula; probé ambas). Después, fue casi 10 veces más rápido en cada ejecución posterior.
dt[NombreColumna] = df_resultados[NombreColumna]
Este es el mensaje de advertencia en el registro integrado.
Advertencia: El uso de `Series.__getitem__` para tratar las claves como posiciones está obsoleto. En una versión futura, las claves enteras siempre se tratarán como etiquetas (en consonancia con el comportamiento de DataFrame). Para acceder a un valor por su posición, utilice `ser.iloc[pos]`.
Además, realicé esta prueba con un total de 4 columnas distintas de 2 dataframes diferentes. Todas mostraron el mismo comportamiento (y la misma advertencia). La primera ejecución fue lenta, pero las siguientes fueron rápidas. La diferencia entre las columnas 1, 2, 3 y 4 fue mínima.
Esto es solo una parte de una tabla grande con 1,3 millones de filas. Por lo tanto, 25 segundos x 4 columnas se convertirán en más de 250 segundos x 4 columnas si no logro obtener velocidades más rápidas al primer intento.
Gracias
Automatización y scriptingRe: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoEn JMP 18 era necesario construir una tabla de datos columna por columna a partir de un data frame. JMP 19 admite el protocolo de data frame portátil, por lo que se necesita una sola línea de código para convertir un data frame (dt) en un data frame (df), y otra sola línea de código para convertir un data frame (df) en un data frame (dt) en memoria.
Además, ten en cuenta que la primera vez que se ejecuta un script de Python en JMP, es necesario inicializar Python. La primera vez que importas un paquete, este debe cargarse, lo que posiblemente incluya bibliotecas compartidas o la compilación de bytecode de los archivos .py.
La advertencia proviene del paquete pandas. Para eliminarla, debe ejecutar el comando sugerido `ser.iloc[pos]`. La tabla de datos `dt` de JMP se puede indexar mediante un índice de columna basado en 0 o por nombre de columna. La indexación sería más rápida, ya que evitaría comparaciones de cadenas.
Ayuda -> Índice de scripting, categoría 'Python', jmp.from_dataframe. Ejemplo de 'Saltar a Pandas'.
import jmp
import jmputils
try:
if not jmputils.is_installed('pandas'):
jmputils.jpip('install', 'pandas', echo=False)
except Exception as e:
print(f'Install failed with exception: {e}')
import pandas as pd
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
pandas_df = (pd.api.interchange.from_dataframe(dt))
print(pandas_df)
...
# new dt_result data table from dataframe df_result
dt_result = jmp.from_dataframe( df_result )
# or continue to do the loop on columns if you need to update the original data table from the resulting dataframe.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculo¿Y cuál es la forma más rápida de rellenar una única columna JMP con nuevos valores desde Python?
Por ejemplo, ¿para una tabla de datos con 100.000 filas?
Re: Python en JMP: prácticas recomendadas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoGracias Paul, implementaré jmp.from_dataframe para volver a datatables y evitar el error.
La otra parte de mi pregunta era sobre por qué existía una diferencia de velocidad tan grande, y para responderla tenía que asegurarme de poder reproducir de forma fiable la lentitud en el cálculo. Al principio no pude. Todas las ejecuciones, incluso la primera tras abrir JMP y cargar Python, fueron bastante rápidas. Entonces me di cuenta de que podría haber una diferencia crucial. Si añado una nueva columna (manualmente, aún no la había incluido en el código) y ejecuto el código, la actualización de esa nueva columna solo será lenta, y solo la primera vez.
Como prueba, actualicé cuatro columnas: tres preexistentes y una nueva. La actualización de 100 000 filas de la columna 1 tardó un segundo, la de la columna 2 otro segundo, la de la columna 3 otro segundo y la de la columna 4, 19 segundos.
Creé otra tabla y una cuarta columna nueva. Si guardo y cierro la tabla con la nueva columna, la vuelvo a abrir y ejecuto el código, la cuarta columna se actualiza en aproximadamente un segundo, igual de rápido que las columnas 1, 2 y 3. Sin embargo, si solo guardo la tabla, pero no la cierro ni la vuelvo a abrir, y luego ejecuto el código, la actualización de la cuarta columna tardará aproximadamente 19 segundos.
Esto me indica que la velocidad tiene que ver con alguna peculiaridad de la gestión de memoria de JMP.
No sé si este problema tiene solución, y quizá solo se presente si no uso el paso `from_dataframe`. Es un comportamiento muy extraño que me alegra haber podido reproducir. Ahora puedo probar la versión mejorada del código y ver si el problema persiste.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoUno de mis colegas señaló que en JSL existen las funciones Start Data Update y End Data Update, así que...
jmp.run_jsl( 'dt << Begin Data Update;' )
# your python code filling new column
...
jmp.run_jsl( 'dt << End Data Update;' )
Lo más probable es que queramos implementar esto desde Python. Probablemente usaremos un bloque `with` para suspender las actualizaciones de la interfaz de usuario de la tabla al llenar o actualizar una gran cantidad de filas.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoSe estaría configurando la columna completa utilizando dt[index] envuelto en jsl Begin Data Update.
jmp.run_jsl( 'dt << Begin Data Update;' )
dt[ index ] = list_like_object # numpy array, list, dataframe column,...
jmp.run_jsl( 'dt << End Data Update;' )
O bien, creando la tabla de datos invisible hasta que la hayas llenado, y luego haciéndola visible.
import jmp
dt = jmp.DataTable('Powered By Python', 40, visibility='Invisible')
...
# fill table
...
jmp.globals['jdt'] = dt # set jdt in JSL to reference dt
jmp.run_jsl('::jdt << Show Window(1);')
El parámetro de visibilidad opcional puede ser JMP 19 o superior. La función jmp.globals[ ] es una característica de la versión 19.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoLa función `jmp.run_jsl('dt << Begin Data Update;')` asume que `dt` es la variable JSL de la tabla, tanto en JSL como en Python. Esto se debe a que `dt` se genera mediante una llamada a `Send(dt);` desde JSL, pero no necesariamente será la misma si la tabla se creó desde Python.
Re: Python en JMP: prácticas recomendadas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoMuchas gracias por la explicación.
Entonces, el código:
dt[ index ] = list_like_object
¿En Python, la columna se llena paso a paso ? ¿Qué está causando el problema de velocidad?
Se parece a
dt[ 0, columnname] = [values ...]
En JSL , esto rellena la columna en un solo paso y no necesita estar envuelto por `dt << Inicio/fin de actualización de datos`.
Me alegra saberlo. ¿Dónde puedo encontrar más documentación con los detalles?
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoNo sé cuántos usuarios saben lo esencial que es.
dt << Begin Data Update;
sirve para escribir buen código. Pero estoy seguro de que muchos usuarios no lo saben.
Deseo relacionado de @bswedlove para cada fila():
para cada fila (actualización de datos = 1, ...)
Es una excelente idea institucionalizar una solución, para que el problema desaparezca para el usuario (la solución siempre está ahí).
Se mantiene para JSL y Python.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculo" La tabla de datos dt de JMP se puede indexar mediante un índice de columna basado en 0 o por nombre de columna. El índice sería más rápido ya que evitaría comparaciones de cadenas. "
Estoy intentando reescribir el código usando índices, pero no logro encontrar la sintaxis equivalente para determinar el índice de una columna de una tabla de datos a partir de un nombre que tengo para los dataframes.
dfColumnIndex = df.get_loc['NameofDataFrameColumn']
dtColumnIndex = ???
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculo@jthi , ¿cómo se llamaba la acción de "agregar código para solucionar un problema que no existe"?
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoEn el caso de que `df` se haya creado a partir de `dt`, se asume que ambos tienen el mismo índice. Si has añadido columnas al dataframe o si uno no es el origen del otro, usar `dt['col_name'] =` probablemente sea tu única opción. No debería ser tan costoso comparado con un índice, pero se trata de una comparación de cadenas frente a la indexación numérica directa.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoTodo esto ocurre gracias a la magia de los Protocol Buffers de Python, donde los datos se mapean en memoria entre los bloques de memoria compatibles. Puede que no sea necesario usar `Begin | End Data Update`, pero si no se puede mapear la columna completa a la vez, se recurrirá a iterar sobre los valores, por lo que usar `Data Update` es una buena medida de seguridad.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoRealicé numerosos experimentos con diferentes métodos. Algunos generaron advertencias, otros no. El más sencillo parecía ser crear un objeto `dt` a partir del `df` y luego usarlo para transferir datos. Esto nunca generó ningún error.
Sin embargo, en cuanto al problema de velocidad... lo ÚNICO que marcó la diferencia fue si intentaba escribir valores en una columna recién creada o en una columna que ya existía cuando se abrió (o creó) la tabla de datos. Las columnas recién creadas se actualizaban con extrema lentitud. Las columnas preexistentes se actualizaban con bastante rapidez.
Resultados del último texto en 100.000 filas. Ya existían 4 parámetros y se añadieron 5 columnas nuevas antes de ejecutar este código. En total, se actualizaron 9 columnas.
#Todas estas transferencias utilizaron la sintaxis dt['colname'] = dt_results['colname']. No se utilizaron DataFrames después de que DF se utilizara para crear dt_results.
Tiempo de creación del paso df_results: 0,3434 segundos
Tiempo de transferencia del parámetro del paso 1: 0,1795 segundos
Tiempo de transferencia del parámetro del paso 2: 0,1758 segundos
Tiempo de creación de df_results del paso: 0.0231 segundos #Este es un nuevo conjunto de resultados de un retorno de función df diferente.
Tiempo de transferencia del parámetro del paso 3: 0,1838 segundos
Tiempo de transferencia del parámetro del paso 4: 0,1890 segundos
#El código aquí era: dt['NewCol1Test'] = df_results[stringnameofcolumn]
Advertencia: El uso de `Series.__getitem__` para tratar las claves como posiciones está obsoleto. En una versión futura, las claves enteras siempre se tratarán como etiquetas (en consonancia con el comportamiento de DataFrame). Para acceder a un valor por su posición, utilice `ser.iloc[pos]`.
Transferencia de df a dt con nombres: tiempo de transferencia: 15,8251 segundos
#El código aquí era: dt['NewCol2Test'] = df_results.iloc[:,dfColumnIndex]
Advertencia: El uso de `Series.__getitem__` para tratar las claves como posiciones está obsoleto. En una versión futura, las claves enteras siempre se tratarán como etiquetas (en consonancia con el comportamiento de DataFrame). Para acceder a un valor por su posición, utilice `ser.iloc[pos]`.
#No sé por qué seguía apareciendo esa advertencia cuando usé iloc
Tiempo de transferencia de df a dt con iloc: 16,0874 segundos
#El código aquí era: dt['NewCol3Test'] = dt_results[dfColumnIndex]
Tiempo de transferencia del índice dt al nombre dt: 14,5659 segundos
#El código aquí era: dt[NewColumnIndex] = dt_results[dfColumnIndex]
Tiempo de transferencia de índice dt a índice dt: 14,4699 segundos
#El código aquí era:
jmp.run_jsl('dt << "Comenzar actualización de datos;");
dt['NewCol5Test'] = dt_results[dfColumnIndex]
jmp.run_jsl('dt << Fin de la actualización de datos;')
#Resultado de advertencia/error:
"Nombre no resuelto: dt en el acceso o evaluación de 'dt', dt/*###*/
en la línea 1
Nombre no resuelto: dt en el acceso o evaluación de 'dt', dt/*###*/
en la línea 1"
#Esta impresión estaba en realidad en la misma línea que el segundo mensaje de error 'en la línea 1'.
Transferencia de datos entre datos con índices envueltos en actualizaciones de inicio/fin: tiempo de transferencia: 14,3627 segundos
#Resumen final del tiempo de ejecución en 100.000 filas.
Tiempo total de ejecución: 77,0904 segundos
Re: Python en JMP: mejores prácticas para convertir columnas de marco de datos en columnas de tabla de datos e implicaciones en la velocidad de cálculoguarde sus datos (*) antes de ejecutar este código una segunda vez (**) - ¡o habilite la actualización de datos de inicio/fin!
import jmp
import numpy as np
dt = jmp.open(jmp.SAMPLE_DATA + "Wafer Stacked.jmp")
jmp.globals['jdt'] = dt
jmp.run_jsl('Graph Builder( Variables( Y( :defects ) ), Elements( Bar( Y, Label( "Label by Value" ) ) ))');
# jmp.run_jsl('jdt << Begin Data Update;')
dt[ 5 ] = [np.random.randint(1000)]*177875
# jmp.run_jsl('jdt << End Data Update;')
(*) Es broma, no perderás tus datos, solo tómate un café y espera a que JMP termine.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculo
@hogi escribió:
... una segunda vez (**)
(**) TS - 00251136
@Dahlia_Watkins notó que a veces se necesitan 2 ejecuciones del código para solucionar el problema de velocidad.
Parece ser un problema de sincronización.
Tal vez: cuando ejecutas el código por primera vez, el gráfico no se crea hasta que Python finaliza la actualización de datos fila por fila.
editar:
En realidad, el gráfico no es necesario como elemento interactivo para ralentizar la actualización de datos de Python.
En las siguientes publicaciones se pueden encontrar más ejemplos sin elementos interactivos.
Re: Python en JMP: prácticas recomendadas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoAñadí el tema a Precaución: Lugares donde Jmp hace algo inesperado
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoRespecto al problema original: "acceso lento a los datos SI hay una columna nueva"
Aquí tenéis un ejemplo ilustrativo:
import jmp
dt = jmp.DataTable('dt_new', 50000)
col = dt.new_column("newCol")
# --------- cut here to make it slow - why? -------------
dt[ 0 ] = [1]*50000
Dividido en 2 pasos, JMP tardará varios segundos en actualizar las 50.000 entradas.
misterioso:
Al ejecutar ambos bloques en una sola ejecución, JMP no presenta ningún problema.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoAdemás de las soluciones alternativas mencionadas en la publicación de @Paul_Nelson , pensé que había algunas adicionales como:
- Utilice JSL para crear la columna/tabla de datos.
- Utilice JSL para rellenar la columna con valores ficticios
- Utilice JSL para guardar la tabla de datos, cerrarla y volver a abrirla.
Muchos de ellos parecen buenos a primera vista, pero es engañoso.
La alta velocidad se debe simplemente a que los mismos valores se escriben de nuevo en la columna y JMP no activa una "alerta de actualización de datos":
p.ej
jmp.globals['jdt'] = dt
jmp.run_jsl('''
nc = n cols(jdt);
Column(jdt,nc) << set each value(1)
''');
nc = dt.ncols
data = [1]*50000
dt[ nc - 1 ] =data
El mismo milagro a través de Python:
# 1st: slow
nc = dt.ncols
dt[ nc - 1 ] = range(50000)
# fast ! (same values!)
dt[ nc - 1 ] = range(50000)
# new values: slow again
dt[ nc - 1 ] = [5]*50000
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoSe puede configurar un disparador para recibir una notificación cuando JMP considere que los datos se han actualizado.
import jmp
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
jmp.run_jsl('''
New Column( "trigger",
Formula(
If( Row() == 1,
Try( Caption( Char( ::i=::i+1 ) ), ::i = 1 )
);
:age;i
)
)
''')
dt[ 1 ] = range(40)
dt[ 1 ] = range(40) # no data update !
dt[ 1 ] = [2]*40
# for loop without begin/end data update -> finished before JMP notices the data update
jmp.run_jsl('''
for each row(:age = row());
wait(5);
''')
# for loop without begin/end data update - and some delay to give JMP the time to notice:
jmp.run_jsl('''
for each row(:age = 2*row();wait(0))
''')
Así pues, en efecto, no hay "actualización de datos" cuando los valores originales y los nuevos son idénticos.
¿Implementado para combatir los bucles infinitos? ¡Genial!
Interesante,
import jmp
dt = jmp.open(jmp.SAMPLE_DATA + "Wafer Stacked.jmp")
jmp.run_jsl('''
delete symbols(::i)
New Column( "trigger",
Formula(
If( Row() == 1,
Try( Caption( Char( ::i=::i+1 ) ), ::i = 1 )
);
:Y_Die;i
)
)
''')
nr = dt.nrows
dt[ 4 ] = range(nr)
tarda más de 1 minuto - Y yo cuento hasta 2.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculo
@Paul_Nelson wrote:
...
Most likely we will want to do support this from Python. Probably using a with block to suspend the UI updates of the table when filling / updating large number of rows.
¡Excelente idea!
Asegúrese de volver a activar la interactividad incluso cuando Python genere un error dentro del bloque "with".
Como alternativa:
Por favor, agregue un indicador a la cuadrícula de datos para mostrar cuándo la interactividad está deshabilitada porque hubo un
Iniciar actualización de datos sin una actualización de datos final .
Indicador: la interactividad está deshabilitada para esta tabla de datos.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoConclusiones de las publicaciones anteriores, resumidas en:
import jmp
# open data table and wait for the "Interactivity Manager" to get active
jmp.run_jsl('''
dt = new table("test", add rows(100000), new column ("col"));
Python send(dt);
wait(0) // disable the delay to make it fast
'''
);
nr=dt.nrows
# Python data update: slow!
dt[ 0 ] = range(nr)
# --- run step by step: -----
# same values: fast
dt[ 0 ] = range(nr)
#new values: slow
dt[ 0 ] = [1]*nr
Actualizar una columna JSL mediante Python es muy lento.
Excepciones:
- derecho de escritura después de crear/abrir la tabla de datos (antes de que se active algún tipo de "Administrador de interactividad")
- No hay ninguna actualización (los valores no cambian).
y las soluciones alternativas mencionadas en la publicación de @Paul_Nelson :
- tablas de datos privados
- Actualización de datos en Python delimitada por Actualización de datos de inicio/fin de JSL()

Acceso rápido a datos en Python, ¡solo con la ayuda de JSL! 
Supongo que los problemas son lo suficientemente importantes como para ser resueltos en la próxima versión ".x"...
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoEn realidad, para el segundo caso, debe ser una tabla privada, con visibilidad establecida en 'Privada'. La tabla 'Invisible' conserva su interfaz gráfica de usuario (GUI) aunque esté oculta, y por lo tanto sigue presentando retrasos debido a las actualizaciones de la GUI que se producen en la tabla de datos cada vez que se edita una celda.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoSí, esto es importante. Estamos trabajando en una solución. Queremos que el rendimiento de Python sea tal que los usuarios quieran usarlo como herramienta principal, sin necesidad de recurrir a métodos poco ortodoxos para obtener un buen rendimiento.
Me sorprende que esto no apareciera en JMP 18 ni durante las pruebas de JMP 19 EA. El objeto `jmp.DataTable` es, en esencia, la tabla de datos JSL. El objeto de Python es solo una referencia a la tabla de datos JMP subyacente, y estamos llamando a los métodos internos de la tabla de datos JMP, por lo que `run_jsl('dt << Begin Data Update')` marca la diferencia. El propósito de `jmp.run_jsl(''code '''')` era permitir la llamada a JSL desde Python para funcionalidades que aún no hemos implementado o, como en este caso, corregir un error en el rendimiento de la actualización de columnas.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoMe alegra saber que el problema se ha solucionado.
¿Por qué no se detectó antes?
Supongo que muchos usuarios utilizan Python para importar y organizar datos antes de transferirlos a JMP, donde no los manipulan directamente con Python. Si utilizan `get(dt)` de JSL o `from_dataframe()` de Python, no tendrán este problema.
Advertencia: transferencia columna por columna como en Introducción a la integración de Python en JMP 18.
Aquí depende del tamaño de la tabla de datos. Si la tabla es lo suficientemente pequeña, las nuevas columnas se agregan antes de que se active el "Administrador de interactividad".
Cuando detectamos el problema de nuestro lado, teníamos acceso a JMP19EA y nos alegró ver que podíamos acelerar la generación de tablas mediante from_dataframe() , con el beneficio adicional de un código mucho más sencillo.
Por otro lado: el problema surge al usar Python para actualizar columnas individuales de una tabla de datos JMP existente, como en JMPyFacade: Bridging JMP and Python for Seamless Engaging Analysis . Como experto en JSL, @jthi sin duda añadió la solución alternativa necesaria para la fusión de datos en tablas privadas sin darle muchas vueltas.
Re: Python en JMP: Mejores prácticas para convertir columnas de dataframe a datatable e implicaciones en la velocidad de cálculoInformación compartida hoy en el Scripters Club:
el problema de velocidad con
dt[ index ] = list_like_object
Se solucionará en JMP 19.0.3 : )
Re: Python en JMP: mejores prácticas para convertir columnas de marco de datos en columnas de tabla de datos e implicaciones en la velocidad de cálculoDisponible ahora:
