JMP 18 aporta nuevas y poderosas capacidades en la integración de Python. Incluye una versión integrada de Python 3.11, soporte para la instalación de paquetes, un editor de scripts compatible con Python que puede ejecutar scripts de Python puros, un paquete jmp de importación que brinda acceso de Python a la funcionalidad JMP, informes de errores mejorados y soporte para índices de scripts que documentan y brindan ejemplos para la nueva funcionalidad en una categoría 'Python'.

El enfoque aquí es específicamente el objeto jmp.DataTable y los cambios que JMP 18 ha realizado con respecto a Python Send (dt) y Python Get(df) . Donde 'dt' es una tabla de datos y 'df' es pandas.DataFrame.
JMP 14-17 brindó la capacidad de transferir una copia de una tabla de datos JMP al entorno Python usando el comando JSL Python Send(dt); . La forma en que esto funcionó detrás de escena fue que JMP escribiera la tabla de datos en un archivo CSV temporal en el sistema de archivos y luego invocara a pandas para leer el archivo CSV del disco en un DataFrame. Esto funciona bien para archivos pequeños pero no es adecuado para datos más grandes. Primero, está la cuestión de tener que utilizar el sistema de archivos para la transferencia de datos. En segundo lugar, pasar de binario a texto y luego volver a binario conlleva la posibilidad de perder precisión. A continuación, el archivo CSV suele ser considerablemente más grande que los datos binarios originales. Finalmente, tienes una copia de los datos en Python, no una forma de manipular la tabla original. Devolver los datos procesados por Python a JMP usando Python Get (df); hace exactamente el mismo proceso a la inversa. Obtener Python (df); se dejó intacto por razones de compatibilidad.
Nuevo en 18, Python Get (dt); recupera la referencia de la tabla de datos a JSL de la tabla real. Esta es una operación de copia cero. Por necesidad Python Enviar (dt); ya no crea un pandas.DataFrame. En Python obtienes un objeto jmp.DataTable que es una referencia activa al Tabla de datos JMP. Esta también es una operación de copia cero. Permite el acceso directo desde Python para acceder, editar y modificar la tabla de datos JMP. Puede crear un objeto jmp.DataTable desde Python y, en el proceso, crear una nueva tabla JMP.
A continuación se muestra un script JSL que utiliza las funciones de JMP 18.0 para tomar una tabla de datos JMP y agregar una columna que sea la proporción de dos columnas de Python. Realizando esto en vivo en la tabla de datos actual.
// BigClass_hw_ratio.jsl
// Description: Show JMP 18 Python integration features
//
Names Default to Here(1);
dt = Open("$SAMPLE_DATA/Big Class.jmp");
Python Send(dt);
Python Submit("\[
import jmp
# print some information on data table properites
print(dt.__class__)
print(dt.name)
print(f'nrows: {dt.nrows}')
print(f'ncols: {dt.ncols}')
# data table object columns may be indexed either by
# column name or index. Note: slice is only supported
# when using indexes. Slice on both columns and rows is supported.
for x in dt[0:]:
print( x.name )
# As is Python convention, indexing is 0-based
print( dt['name'][0] )
# create a new column based on data from the original data.
# Because JMP is column centric, the access to
# jmp.DataTable object is [column][row]
dt.new_column('H/W ratio', jmp.DataType.Numeric)
for idx in range(dt.nrows):
dt['H/W ratio'][idx] = dt['height'][idx] / dt['weight'][idx]
]\");

Resultando en la salida del registro:
/*:
Big Class nrows: 40 ncols: 5 nombre edad sexo altura peso KATIE 0
Ahora mostremos cómo trabajar directamente desde Python. A continuación se muestra un script de Python que abre 'Big Class.jmp', crea un marco de datos pandas en memoria desde el objeto jmp.DataTable, cierra 'Big Class', luego crea una nueva tabla de datos JMP a partir del DataFrame y, finalmente, en JSL. Imprime los nombres de las columnas de la tabla de datos actual. La tabla actual es la que se acaba de crear en Python.

# dt2pandas2dt.py
# Description: Example showing creating a pandas.DataFrame from a .jmp file,
# then creating a new data table from the data frame.
# This process happening live and in-memory.
#
import jmp
import numpy as np
import pandas as pd
from pandas.api.types import is_object_dtype, is_numeric_dtype, is_bool_dtype, is_string_dtype
# creating a Pandas dataframe from a JMP DataTable directly
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
# create pandas DataFrame from Big Class
df = pd.DataFrame()
for idx in range( len(dt) ):
df[ dt[idx].name ] = np.array( dt[idx] )
print(df)
# we can close dt since we no longer need it open, we
# have everyting in df
dt.close(save=False)
# create JMP DataTable from pandas DataFrame
dt2 = jmp.DataTable('BC2', df.shape[0])
# get the column names from the data frame
names = list(df.columns)
# loop across the columns of the data frame
for j in range( df.shape[1] ):
# check if the coulumn data type is string or numeric
if is_string_dtype(df[ names[j] ] ):
dt2.new_column(names[j], jmp.DataType.Character )
else:
dt2.new_column(names[j], jmp.DataType.Numeric )
# populate the JMP column with data
dt2[j] = list(df.iloc[:,j])
# Just as JSL has a Python Submit(); to execute Python code
# the jmp object has a run_jsl() command to execute JSL from Python
jmp.run_jsl('''
Names Default to Here(1);
Current Data Table() << Get Column Names;
''')
Tener salida de registro
/*:
name age sex height weight
0 KATIE 12.0 F 59.0 95.0
1 LOUISE 12.0 F 61.0 123.0
2 JANE 12.0 F 55.0 74.0
3 JACLYN 12.0 F 66.0 145.0
4 LILLIE 12.0 F 52.0 64.0
5 TIM 12.0 M 60.0 84.0
6 JAMES 12.0 M 61.0 128.0
7 ROBERT 12.0 M 51.0 79.0
8 BARBARA 13.0 F 60.0 112.0
9 ALICE 13.0 F 61.0 107.0
10 SUSAN 13.0 F 56.0 67.0
11 JOHN 13.0 M 65.0 98.0
12 JOE 13.0 M 63.0 105.0
13 MICHAEL 13.0 M 58.0 95.0
14 DAVID 13.0 M 59.0 79.0
15 JUDY 14.0 F 61.0 81.0
16 ELIZABETH 14.0 F 62.0 91.0
17 LESLIE 14.0 F 65.0 142.0
18 CAROL 14.0 F 63.0 84.0
19 PATTY 14.0 F 62.0 85.0
20 FREDERICK 14.0 M 63.0 93.0
21 ALFRED 14.0 M 64.0 99.0
22 HENRY 14.0 M 65.0 119.0
23 LEWIS 14.0 M 64.0 92.0
24 EDWARD 14.0 M 68.0 112.0
25 CHRIS 14.0 M 64.0 99.0
26 JEFFREY 14.0 M 69.0 113.0
27 MARY 15.0 F 62.0 92.0
28 AMY 15.0 F 64.0 112.0
29 ROBERT 15.0 M 67.0 128.0
30 WILLIAM 15.0 M 65.0 111.0
31 CLAY 15.0 M 66.0 105.0
32 MARK 15.0 M 62.0 104.0
33 DANNY 15.0 M 66.0 106.0
34 MARTHA 16.0 F 65.0 112.0
35 MARION 16.0 F 60.0 115.0
36 PHILLIP 16.0 M 68.0 128.0
37 LINDA 17.0 F 62.0 116.0
38 KIRK 17.0 M 68.0 134.0
39 LAWRENCE 17.0 M 70.0 172.0
//:*/
Names Default to Here(1);
Current Data Table() << Get Column Names;
/*:
{name, age, sex, height, weight}
Más ejemplos a seguir en futuras publicaciones de blog.
Ahora que esta capacidad se ha demostrado en la Conferencia de usuarios de este año, puedo compartirla.
JMP 19 es compatible con el protocolo Portable Dataframe, que permite convertir entre pandas y cualquier otro dataframe compatible con una sola línea de código. Consulte el índice de scripts en la versión 19 EA o en la versión 19 cuando la obtenga.
import pandas as pd
import numpy as np
pandas_df = pd.DataFrame(
{
"A": 1.0,
"B": pd.Timestamp("20130102"),
"C": pd.Series(1, index=list(range(4)), dtype="float32"),
"D": np.array([3] * 4, dtype="int32"),
"E": pd.Categorical(["test", "train", "test", "train"]),
"F": "foo",
}
)
print(pandas_df)
dt = jmp.from_dataframe(pandas_df)
print(dt)
El paquete jmp ahora incluye un método from_dataframe(df) que crea una tabla de datos JMP a partir del dataframe en memoria. Pandas también es compatible con el protocolo Portable Data Frame. Gracias a la implementación de la interfaz, Pandas puede crear un dataframe directamente desde un objeto jmp.DataTable.
import jmp
import pandas as pd
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
pandas_df = (pd.api.interchange.from_dataframe(dt))
print(pandas_df)
Otros paquetes compatibles con el protocolo Data Frame son Polars e Ibis. Consulte el índice de scripts en la sección 19 para ver ejemplos.
Recientemente instalé JMP 19.1.1 y probé el método jmp.from_dataframe() descrito anteriormente por @Paul_Nelson , utilizando su código de ejemplo. Descubrí que seguía utilizando el archivo CSV temporal intermedio y tenía los siguientes mensajes en el registro:
:16: Pandas4Warning: The Dataframe Interchange Protocol is deprecated.
For dataframe-agnostic code, you may want to look into:
- Arrow PyCapsule Interface: https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html
- Narwhals: https://github.com/narwhals-dev/narwhals
ValueError: ArrowCTypes for string columns must be UTF-8 Strings or UTF-8 Large Strings.
The above exception was the direct cause of the following exception:
SystemError:
returned a result with an exception set
Una investigación más a fondo reveló que, al parecer, el Protocolo de Intercambio de Dataframes que utiliza JMP ha quedado obsoleto en pandas 3.0.
Tenía instalada la versión 3.0.2 de pandas en mi instancia de Python JMP. Volví a la versión 2.2.3 de pandas y los mensajes del registro desaparecieron, y la tabla de datos de JMP se generó sin crear un archivo CSV intermedio.
Pandas 3.0 ha causado algunos problemas. El manejo de columnas de cadena cambió en pandas de la versión 2.x a la 3.x. Esto provocó un error en nuestro procesamiento de columnas de cadena, lo que a su vez obligó a recurrir al formato CSV con Pandas 3.0.x y JMP 19.x. Hemos solucionado el problema en JMP 20 y estamos investigando si es factible incluirlo en la versión 19.2. Fue necesario modificar una cantidad considerable de código.