Création de fonctions JSL implémentées en PythonRécemment, j'ai donné une conférence interne à JMP sur les trucs et astuces Python. Il y a une astuce en particulier que je trouve puissante, j'aimerais donc la partager avec vous : créer une fonction JSL qui encapsule l'appel à une fonction Python. En d’autres termes, il s’agit d’un code utilitaire qui peut être écrit en Python mais doté d’une interface JSL. Pour faciliter l'illustration, j'ai implémenté une simple fonction Python qui prend deux objets et les additionne, puis enveloppe cet appel dans une fonction JSL. Je commence avec trois types de données différents (numériques, chaînes et listes) et une fonction qui additionne les deux objets Python.
Names Default to Here(1);
one = 1;
two = 2;
author = "Paul Nelson";
devel = "Developer: ";
list1 = { 1, 3, 5, 7 };
list2 = { 2, 4, 6, 8 };
Python Submit("\[
def plus(a,b):
print(a, b)
return a+b
]\");
Tout objet Python prenant en charge l'opérateur "+" peut être géré par cette fonction Python.
// Python Execute is an underutilized gem. You can send a list
// of inputs and get a list of results and execute code all in one
// JSL statement.
Python Execute( {one, two}, {result}, "result = plus(one,two)" );
show(result);
Le résultat:
result = plus(one,two)
/*:
1.0
2.0
result = 3;
C'est là que les choses deviennent amusantes ! En particulier, une fonction JSL qui encapsule la fonction plus() définie par Python.
// create a JSL function that wraps the the Python function
blow_mind = Function( {first, second}, // parameters
{pyresult}, // local variables
Python Execute( {first, second}, {pyresult}, "pyresult = plus(first,second)");
Return( pyresult ); // value to return
); // end of the definition
// Call the Python function through our JSL function. Good way to extend JSL
// using code written in Python. Could be useful for add-ins.
// Notice the same Python function handling different types of Python objects.
show( blow_mind(devel, author) );
show( blow_mind(list1, list2) );
Donner les résultats :
//:*/
pyresult = plus(first,second)
/*:
Developer:
Paul Nelson
blow_mind(devel, author) = "Developer: Paul Nelson";
//:*/
pyresult = plus(first,second)
/*:
[1, 3, 5, 7]
[2, 4, 6, 8]
blow_mind(list1, list2) = {1, 3, 5, 7, 2, 4, 6, 8};
Notre fonction triviale plus() gérait la sommation des valeurs numériques, ainsi que la concaténation des chaînes et des listes, qui sont toutes appelables en tant que fonction JSL. Le reste du code JSL ignore parfaitement que ce qui a été appelé s'est réellement produit en Python.
Fichier complet joint.
Vous souhaitez continuer à coder en JSL, mais pour une fonction particulière, vous savez qu'il existe un moyen simple de l'écrire en Python. Alors, comment créer une fonction JSL qui implémente ses fonctionnalités en Python ?
Automatisation et scriptsRe : Création de fonctions JSL implémentées en PythonMerci pour le support Python amélioré @Paul_Nelson !
Les scripts de navigateur avec Python Selenium le montrent
// 1: navigate to jmp.com
nav = Function( {url}, {rc},
Python Execute( {url}, {rc},
"\[
try:
driver.get(url)
rc = "ok"
except Exception as e:
rc = repr(e)
]\" );
return(rc);
);
rc = nav( "https://www.jmp.com/" );
if( rc != "ok", throw("nav: "||char(rc)));
Les deux dernières lignes appellent via la fonction wrapper pour permettre à un navigateur Web d'accéder au site Web de JMP. La fonction JSL fonctionne exactement de la même manière que l'exemple de Paul.
Re : Création de fonctions JSL implémentées en Pythonrecherche Regex globale et multiligne avec plusieurs valeurs de retour
Add Custom Functions(
New Custom Function(
"hogi",
"Regex",
Function({input, pattern},
SubstituteInto(input,"\!n"," ","\!r", " ") ;
Python Send( input );
Python Send( pattern );
error = Python Submit( "\[
import re
print(input)
matches = re.findall(pattern,input)
]\" );
matches = Python Get( matches );
),
<<DESCRIPTION></DESCRIPTION>Re : Création de fonctions JSL implémentées en PythonRemarque concernant @hogi, je pense que Function() devrait avoir un return(matches); déclaration.
De plus, l'utilisation de Python Execute( ) supprime le besoin d'effectuer des envois et des réceptions.
Add Custom Functions(
New Custom Function(
"hogi",
"Regex",
Function({input, pattern},
SubstituteInto(input,"\!n"," ","\!r", " ") ;
Python Execute( {input, pattern}, {matches}, "\[
import re
matches = re.findall(pattern,input)
]\" );
return( matches);
),
<Re : Création de fonctions JSL implémentées en Pythonah, c'est vrai. Python Execute , beaucoup plus sympa 
Dans les fonctions JSL, la dernière expression est la valeur de retour - même sans inclure return() .
Re: Création de fonctions JSL implémentées en PythonJe ne suis pas un adepte de Python, mais il existe certaines bibliothèques Python (comme xxhash) que j'aimerais utiliser.
J'ai une question sur l'utilisation de la mémoire. Si j'encapsule une fonction JSL de cette manière du code Python et qu'un objet Python est créé dans le code Python encapsulé, un appel à ma fonction d'encapsulation JSL créera-t-il un *nouvel* objet Python persistant à chaque fois que la fonction est appelée (par exemple, en utilisant de la mémoire si j'appelle la fonction des dizaines de millions de fois) ?
Et si je créais un objet Python nommé une fois dans mon code (par exemple) jmpstart.jsl et que je référençais cet objet dans ma procédure ; serait-ce mieux ?
Vous trouverez ci-dessous un exemple de ce que je souhaite faire avec xxhash. (Remarque : j'importe xxhash dans l'environnement Python dans mon script jmpstart.jsl. Je suis bloqué en émettant un float JSL à partir de cette fonction car JSL ne prend pas en charge les entiers de 8 octets...)
Add Custom Functions(
New Custom Function(
"Global",
"xxhash64",
Function( {str},
{digest},
Python Execute( {str}, {digest}, "digest = xxhash.xxh64( str ).hexdigest()" );
Return( Hex To Number( digest ) );
)
)
)
Re: Création de fonctions JSL implémentées en PythonPS. J'ai remarqué que ce code est mauvais car il y a une incidence d'environ 0,8 % de digest Hex étant un float IEEE non valide, ce qui fait que la procédure renvoie une valeur manquante JSL. Cela dit, je m'interroge toujours sur la gestion de la mémoire comme indiqué.
Re: Création de fonctions JSL implémentées en Pythonhashlib et base64 sont tous deux des bibliothèques Python intégrées.
Hier, j'avais l'intention d'examiner spécifiquement ce cas. Je peux vous dire qu'au pire, il devrait « fuir » une seule référence à digest pendant toute la durée d'exécution de JMP. Lorsque JMP s'arrête, cette variable sera libérée lorsque Python s'arrêtera. Le code Python Set() crée la variable digest et la place dans le dictionnaire globals() de l'environnement Python. Cela permet à Python Get() de pouvoir rechercher la valeur par nom et de renvoyer la valeur à JSL. Python Execute() est fondamentalement une fonction pratique pour Python Send/Set(), Python Submit(), Python Get(). Le dictionnaire a donc une référence à digest , mais une fois que Python Execute() sort de la portée, le dictionnaire doit être le seul à détenir une référence. Lors de la deuxième boucle, la valeur est écrasée par la nouvelle valeur, donc l'ancienne référence doit être libérée, permettant au garbage collection de renvoyer cette mémoire.
Re: Création de fonctions JSL implémentées en PythonMerci Paul. Concernant la mémoire, c'est ce que j'espérais. Je vais regarder du côté de hashlib et base64.
Contexte : je voulais un hachage de 8 octets, et j'en ai écrit un qui utilisait les fonctions MD5 natives de JSL et les tronquait à 8 octets – mais avec mes entrées de chaîne, les hachages semblent finir par être groupés. Quelqu'un m'a recommandé xxhash64 comme donnant des valeurs de hachage distribuées de manière très aléatoire. Je l'ai donc utilisé et il semblait donner des hachages bien distribués.
Comme mentionné précédemment, xxhash64 peut émettre un longint, mais je ne parviens pas à le transmettre à JSL. Je peux transmettre le résultat sous forme de BLOB JSL, mais JMP ne me permet pas d'utiliser les BLOBS comme identifiants de LIEN, ce pour quoi je souhaite utiliser les hachages. C'est un peu différent de la question initiale, mais si vous avez des idées, elles seront très appréciées.
Re: Création de fonctions JSL implémentées en PythonPS. Je pourrais simplement utiliser la valeur de chaîne de l'hexadécimal comme identifiant de lien, mais j'avais envie de quelque chose de petit et rapide. Je travaille avec des tables contenant environ 80 millions de lignes.
Re: Création de fonctions JSL implémentées en Python@john_madden Les nombres à double précision 64 bits de JMP peuvent contenir un entier signé de -2^52 .. 2^52. Je pense que vous pouvez conserver 13 chiffres hexadécimaux du hachage à 16 chiffres hexadécimaux en utilisant hextonumber et éviter toute question sur les nombres à virgule flottante non valides. Mais 52 bits ne suffisent probablement pas.
J'ai aussi joué dans ce quartier et j'ai trouvé cette table d'attaque d'anniversaire utile. J'utilise un hachage d'un fichier pour créer une clé unique et je voulais savoir de combien de bits j'aurais besoin. D'après la table, je vois que l'ajout de 6 100 000 éléments dans une table de hachage de 64 bits a une chance de collision de 1/1 000 000.
Pour mon cas d'utilisation, le disque du serveur de fichiers est 1e11 et un fichier typique est 1e4, soit une limite de 1e7 fichiers. Le hachage 64 bits est trop petit et le 96 serait correct. J'ai opté pour 256 bien sûr...

Re: Création de fonctions JSL implémentées en PythonBonjour Craig. C'est génial ! Quel type de JMP utilisez-vous pour vos valeurs 256 bits ?
En passant, il serait bien que les développeurs ajoutent à un moment donné une fonction de hachage plus moderne que MD5 à JSL.
Re: Création de fonctions JSL implémentées en PythonVoici ma dernière tentative, qui semble bien fonctionner :
"Accepts a string as input, and returns a JSL floating point number that encodes a 'nearly'-64-bit hash of the string. The hash is generated using the Python xxhash package with the default (0) seed.<BR />Python returns the 64-bit hash as a 16-character hexadecimal string. If the bits 2-12 are 11111111111, they are changed to 01111111111 before being processed further by JSL.<BR />This avoids generating an IEEE 754 floating point value of NaN or ∞, which JSL would interpret as Missing.<BR />This trick cuts the number of possible floating point exponent values<BR />from 2048 to 2046, resulting in a hash with 2^63 * 2046 possible values (= 1.89e22).<BR />For 10 million different inputs, this should yield a collision probability of roughly 1 in 380 milion<BR />according to Craige's reference if I calculcated right. On my M3 Max MacBook Pro, the routine takes around 26 μsec per iteration with random strings of length 11.
Names Default To Here( 1 );
Add Custom Functions(
New Custom Function(
"Global",
"xxhash64",
Function( {str},
{digest},
Python Execute( {str}, {digest}, "digest = xxhash.xxh64( str ).hexdigest()", echo( 0 ) );
startChars = Substr( digest, 1, 3 );
Hex To Number(
Match( startChars,
"7ff", "3ff" || Substr( digest, 4, 13 ),
"fff", "bff" || Substr( digest, 4, 13 ),
digest
)
);
)
) << Description(
"Accepts a string as input, and returns a JSL floating point number that encodes a 'nearly'-64-bit hash of the string.
Python must have already been initialized as follows:
Python Execute( {}, {},
\!"
import jmputils
jmputils.jpip('install', 'numpy pandas scikit-learn xxhash')
import jmp
import numpy
import pandas as pd
import xxhash
\!""
) //
<<PROTOTYPE></PROTOTYPE>Re: Création de fonctions JSL implémentées en PythonJe suppose que Craige utilise une chaîne de chiffres hexadécimaux ou de chiffres codés en base64.
L'un des grands avantages de l'intégration de Python est que vous n'avez pas besoin de JMP pour « rattraper » votre retard et fournir des fonctions de hachage plus modernes. Vous avez la puissance via Python. L'une des raisons pour lesquelles je mentionne hashlib et base64 est qu'ils font partie de la bibliothèque standard Python, donc tous les utilisateurs de JMP les auront à disposition sans avoir à installer un package externe.
Re: Création de fonctions JSL implémentées en Python@john_madden - you want <SPAN class="token number">2</SPAN><SPAN class="token operator">^</SPAN><SPAN class="token number">52</SPAN> <SPAN class="token operator">*</SPAN> <SPAN class="token number">2046</SPAN> rather than <SPAN class="token number">2</SPAN><SPAN class="token operator">^</SPAN><SPAN class="token number">63</SPAN> <SPAN class="token operator">*</SPAN> <SPAN class="token number">2046</SPAN> . Or just use the 64 bit row.
Il y a longtemps, je savais comment les chaînes JSL étaient conservées en mémoire ; si je me souviens bien, il y avait un changement de performance significatif autour de 24 caractères. Vous souhaiterez peut-être utiliser la version 96 bits et vous assurer qu'elle reste à 24 chiffres hexadécimaux ou moins. Pour 10 000 000 de chaînes, l'espace est de 1/4 Go sur votre machine de 16 Go... et vous êtes passé de 1e-6 à 1e-15 de chance de rencontrer une erreur. Si ce n'était pas pour la malchance, je n'aurais pas de chance du tout - https://quoteinvestigator.com/2020/09/02/bad-luck/ .
I'm working with python/django/postgresql currently and I store the hex digits as a database key...a 64 character string gives me the 256 bits.
Re: Création de fonctions JSL implémentées en PythonEncore une fois, merci à Paul et Craige. D'ailleurs, l'intégration de Python m'a suffisamment enthousiasmé pour essayer d'apprendre Python, car mes collègues informatiques ici veulent comprendre une partie de mon code JSL et me demandent toujours quel serait l'équivalent Python. Je suis extrêmement reconnaissant à Paul pour tout ce travail.
Je dois également corriger le calcul du nombre de valeurs de hachage dans mon script ci-dessus - je pense que c'est plutôt 2^64 - 2^63, beaucoup moins que ce que j'ai dit.
En ce qui concerne le stockage des valeurs de hachage, vous m'avez convaincu de ne pas essayer d'être aussi mièvre. Je pense que vous m'avez convaincu que le simple stockage des caractères hexadécimaux est finalement préférable. Je vais devoir expérimenter en faisant quelques jointures sur d'énormes tables pour voir si cela fait vraiment une différence de vitesse si je joins des colonnes de chaînes hexadécimales ou des colonnes de flottants ; ou si j'utilise des chaînes ou des flottants comme identifiants de lien.
Si jamais j'exporte mes grandes tables JMP vers une RDB, je pourrais les convertir en longints à ce stade.
Re: Création de fonctions JSL implémentées en PythonCraige à propos du nombre de valeurs de hachage, vous avez absolument raison et j'étais encore confus et je n'avais pas lu votre message. 2^52 * 2046 est correct.