JMP 18 apporte de nouvelles fonctionnalités puissantes dans l'intégration Python. Y compris une version intégrée de Python 3.11, la prise en charge de l'installation de packages, un éditeur de script compatible Python qui peut exécuter des scripts Python purs, un package d'importation jmp fournissant un accès Python à la fonctionnalité JMP, un rapport d'erreurs amélioré et une prise en charge de l'index de script documentant et fournissant des exemples pour la nouvelle fonctionnalité sous une catégorie 'Python'.

L'accent ici est spécifiquement mis sur l'objet jmp.DataTable et sur les modifications apportées par JMP 18 par rapport à Python Send ( dt ) et Python Get ( df ) . Où 'dt' est une table de données et 'df' est un pandas.DataFrame.
JMP 14-17 offrait la possibilité de transférer une copie d'une table de données JMP vers l'environnement Python à l'aide de la commande JSL Python Send( dt ) ; . La façon dont cela fonctionnait en coulisses consistait pour JMP à écrire la table de données dans un fichier CSV temporaire dans le système de fichiers, puis à appeler pandas pour lire le fichier CSV du disque dans un DataFrame. Cela fonctionne bien pour les petits fichiers mais ne convient pas aux données plus volumineuses. Premièrement, il y a le problème de devoir utiliser le système de fichiers pour le transfert de données. Deuxièmement, passer du binaire au texte puis revenir au binaire peut entraîner une perte de précision. Ensuite, le fichier CSV est généralement considérablement plus volumineux que les données binaires d'origine. Enfin, vous disposez d'une copie des données en Python, pas d'un moyen de manipuler la table d'origine. Ramener les données traitées par Python dans JMP à l'aide de Python Get ( df ); fait exactement le même processus en sens inverse. Python Get( df ); a été laissé intact pour des raisons de compatibilité.
Nouveau dans 18, Python Get ( dt ); récupère la référence de la table de données au JSL de la table réelle. Il s’agit d’une opération zéro copie. Par nécessité Python Send( dt ); ne crée plus de pandas.DataFrame. En Python, vous obtenez un objet jmp.DataTable qui est une référence en direct au Tableau de données JMP. Il s’agit également d’une opération zéro copie. Il permet un accès direct depuis Python pour accéder, éditer et modifier la table de données JMP. Vous pouvez créer un objet jmp.DataTable à partir de Python, lors du processus de création d'une nouvelle table JMP.
Vous trouverez ci-dessous un script JSL utilisant les fonctionnalités de JMP 18.0 pour prendre une table de données JMP et ajouter une colonne représentant le rapport de deux colonnes de Python. Effectuer cela en direct sur la table de données actuelle.
// 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]
]\");

Résultat dans la sortie du journal :
/*:
Grande classe nrows : 40 ncols : 5 nom âge sexe taille poids KATIE 0
Montrons maintenant le travail directement depuis Python. Vous trouverez ci-dessous un script Python qui ouvre 'Big Class.jmp', crée une trame de données pandas en mémoire à partir de l'objet jmp.DataTable, ferme 'Big Class', puis crée une nouvelle table de données JMP à partir du DataFrame, et enfin en JSL. imprime les noms de colonnes de la table de données actuelle. La table actuelle étant celle qui vient d'être créée 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;
''')
Avoir une sortie de journal
/*:
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}
D’autres exemples suivront dans les prochains articles du blog.
Maintenant que cette capacité a été démontrée lors de la conférence des utilisateurs de cette année, je peux la partager.
JMP 19 prend en charge le protocole Portable Dataframe, qui permet de convertir des données vers et depuis un dataframe Pandas ou tout autre dataframe compatible en une seule ligne de code. Veuillez consulter l'index des scripts dans JMP 19 EA ou 19 lorsque vous le recevrez.
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)
Le package jmp dispose désormais d'une méthode from_dataframe(df) qui crée une table de données JMP à partir du dataframe en mémoire. Pandas prend également en charge le protocole Portable Data Frame. Grâce à l'implémentation de cette interface, Pandas peut créer un dataframe directement à partir d'un objet 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)
D'autres packages prenant en charge le protocole Data Frame incluent Polars et Ibis. Consultez l'index des scripts en 19 pour des exemples.
J'ai récemment installé JMP 19.1.1 et testé la méthode `jmp.from_dataframe()` comme décrit par @Paul_Nelson , en utilisant son exemple de code. J'ai constaté qu'elle utilisait toujours le fichier CSV temporaire intermédiaire et j'ai obtenu les messages suivants dans le journal :
: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
Des recherches plus approfondies ont révélé que le protocole d'échange de Dataframes utilisé par JMP semble être obsolète depuis la version 3.0 de pandas.
J'avais installé pandas 3.0.2 avec mon instance JMP de Python. Après être revenu à pandas 2.2.3, les messages d'erreur ont disparu du journal et le tableau de données JMP a été généré sans création de fichier CSV intermédiaire.
Pandas 3.0 a causé quelques problèmes. La gestion des colonnes de type chaîne de caractères a changé entre les versions 2.x et 3.x de Pandas. Cela a provoqué une erreur lors du traitement de ces colonnes, nous obligeant à utiliser le format CSV avec Pandas 3.0.x et JMP 19.x. Nous avons corrigé ce problème dans JMP 20 et nous étudions la possibilité de l'intégrer à la version 19.2. Une quantité importante de code a dû être modifiée.