Creazione di funzioni JSL implementate in PythonRecentemente ho tenuto un discorso interno a JMP su Python Tips and Tricks. C'è un suggerimento in particolare che ritengo potente, quindi vorrei condividerlo con te: creare una funzione JSL che esegua il wrapper della chiamata a una funzione Python. In altre parole, si tratta di codice di utilità che può essere scritto in Python ma dotato di un'interfaccia JSL. Per facilità di illustrazione, ho implementato una semplice funzione Python che prende due oggetti e li somma insieme, quindi racchiude la chiamata in una funzione JSL. Inizio con tre diversi tipi di dati (numerici, stringhe ed elenchi) e una funzione che aggiunge insieme i due oggetti 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
]\");
Qualsiasi oggetto Python che supporta l'operatore "+" può essere gestito da questa funzione 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);
Il risultato:
result = plus(one,two)
/*:
1.0
2.0
result = 3;
È qui che le cose si divertono! In particolare, una funzione JSL che racchiude la funzione plus() definita da 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) );
Dando i risultati:
//:*/
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};
La nostra banale funzione plus() gestiva la somma di valori numerici, nonché la concatenazione di stringhe ed elenchi, che sono tutti richiamabili come funzione JSL. Il resto del codice JSL è beatamente inconsapevole del fatto che ciò che è stato effettivamente chiamato sia avvenuto in Python.
In allegato il file completo.
Vuoi continuare a scrivere codice in JSL, ma per una particolare funzione sai che esiste un modo semplice per scriverla in Python. Quindi come si crea una funzione JSL che implementa la sua funzionalità in Python?
Automazione e scriptingRi: Creazione di funzioni JSL implementate in PythonGrazie per il supporto Python migliorato @Paul_Nelson !
Lo scripting del browser con Python Selenium lo mostra
// 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)));
Le ultime due righe chiamano tramite la funzione wrapper per far sì che un browser Web navighi al sito Web JMP. La funzione JSL funziona esattamente allo stesso modo dell'esempio di Paul.
Ri: Creazione di funzioni JSL implementate in PythonRicerca Regex globale e multilinea con più valori restituiti
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>Ri: Creazione di funzioni JSL implementate in PythonNota riguardante @hogi credo che Function() dovrebbe avere un return(matches); dichiarazione.
Inoltre, l'uso di Python Execute() elimina la necessità di eseguire invii e ricevi.
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);
),
<Ri: Creazione di funzioni JSL implementate in PythonAh giusto. Python Execute , molto più carino 
Nelle funzioni JSL, l' ultima espressione è il valore restituito, anche senza racchiudere return() .
Re: Creazione di funzioni JSL implementate in PythonNon sono un esperto di Python, ma ci sono alcune librerie Python (come xxhash) che mi piacerebbe usare.
Ho una domanda sull'utilizzo della memoria. Se JSL-function-wrap un po' di Python in questo modo, e un oggetto Python viene creato nel codice Python wrappato, una chiamata alla mia funzione JSL wrapper creerà un *nuovo* oggetto Python persistente ogni volta che la funzione viene chiamata (ad esempio, utilizzando memoria se chiamo la funzione decine di milioni di volte)?
Cosa succederebbe se creassi un oggetto Python denominato una volta nel mio codice (ad esempio) jmpstart.jsl e facessi riferimento a quell'oggetto all'interno della mia procedura; sarebbe meglio?
Di seguito è riportato un esempio di cosa voglio fare con xxhash. (Nota che importo xxhash nell'ambiente Python nel mio script jmpstart.jsl. Sono bloccato nell'emettere un float JSL da questa funzione perché JSL non supporta interi a 8 byte...)
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: Creazione di funzioni JSL implementate in PythonPS. Ho notato che questo è un codice pessimo perché c'è un'incidenza di circa lo 0,8% del digest esadecimale che è un float IEEE non valido, con conseguente restituzione di un valore JSL mancante da parte della procedura. Detto questo, mi chiedo ancora della gestione della memoria come indicato.
Re: Creazione di funzioni JSL implementate in Pythonhashlib e base64 sono entrambe librerie Python integrate.
Volevo esaminare questo caso in modo specifico ieri. Posso dirti che, nel peggiore dei casi, dovrebbe "perdere" un singolo riferimento a digest durante l'intero periodo di esecuzione di JMP. Quando JMP si arresta, quella variabile verrà rilasciata quando Python si arresta. Il codice Python Set() crea la variabile digest e la inserisce nel dizionario globals() dell'ambiente Python. Ciò consente a Python Get() di cercare il valore per nome e restituire il valore a JSL. Python Execute() è fondamentalmente una funzione di convenienza per Python Send/Set(), Python Submit(), Python Get(). Quindi il dizionario ha un riferimento a digest , tuttavia una volta che Python Execute() esce dall'ambito, il dizionario dovrebbe essere l'unico a contenere un riferimento. Nel secondo ciclo, il valore viene sovrascritto con il nuovo valore, quindi il vecchio riferimento dovrebbe essere rilasciato, consentendo alla garbage collection di restituire quella memoria.
Re: Creazione di funzioni JSL implementate in PythonGrazie Paul. Per quanto riguarda la memoria, era quello che speravo. Darò un'occhiata a hashlib e base64.
Il background è: volevo un hash di 8 byte e ne ho scritto uno che usava le funzioni MD5 native JSL e le troncava a 8 byte, ma con i miei input di stringa gli hash sembrano finire per essere raggruppati. Qualcuno mi ha consigliato xxhash64 perché fornisce valori hash distribuiti in modo molto casuale. Quindi l'ho usato e sembrava fornire hash ben distribuiti.
Come detto, xxhash64 può emettere un longint, ma non riesco a farlo passare in JSL. Posso far passare il risultato come un BLOB JSL, ma JMP non mi lascia usare i BLOBS come LINK ID, che è ciò per cui voglio usare gli hash. È un po' diverso dalla domanda originale, ma se hai qualche idea, te ne sarei molto grato.
Re: Creazione di funzioni JSL implementate in PythonPS. potrei semplicemente usare il valore stringa dell'esadecimale come Link ID, ma desideravo qualcosa di piccolo e veloce. Ho a che fare con tabelle con ~80 milioni di righe.
Re: Creazione di funzioni JSL implementate in Python@john_madden I numeri a doppia precisione a 64 bit di JMP possono contenere un intero con segno da -2^52 .. 2^52. Penso che tu possa mantenere 13 cifre esadecimali dell'hash a 16 cifre esadecimali usando hextonumber ed evitare qualsiasi domanda sui numeri in virgola mobile non validi. Ma 52 bit probabilmente non sono sufficienti.
Ho giocato anch'io in questo quartiere e ho trovato utile questa tabella di attacco di compleanno . Sto usando un hash di un file per creare una chiave univoca e volevo sapere quanti bit mi servirebbero. Dalla tabella, vedo che aggiungere 6.100.000 elementi in una tabella hash a 64 bit ha una probabilità di collisione di 1/1.000.000.
Per il mio caso d'uso, il disco del file server è 1e11 e un file tipico è 1e4, o un limite di 1e7 file. L'hash a 64 bit è troppo piccolo e 96 andrebbe bene. Ho scelto 256 ovviamente...

Re: Creazione di funzioni JSL implementate in PythonCiao Craig. Fantastico! Quale tipo di JMP stai usando per i tuoi valori a 256 bit?
A titolo di curiosità, sarebbe bello se a un certo punto gli sviluppatori aggiungessero a JSL una funzione hash più moderna di MD5.
Re: Creazione di funzioni JSL implementate in PythonEcco il mio ultimo tentativo, che sembra funzionare bene:
"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: Creazione di funzioni JSL implementate in PythonImmagino che Craige stia utilizzando una stringa di cifre esadecimali o cifre codificate in base64.
Una delle cose fantastiche dell'integrazione Python è che non hai bisogno di JMP per 'recuperare' e fornire funzioni hash più moderne. Hai il potere tramite Python. Uno dei motivi per cui menziono hashlib e base64 è che fanno parte della libreria standard Python, quindi tutti gli utenti JMP li avranno a disposizione senza dover installare un pacchetto esterno.
Re: Creazione di funzioni JSL implementate in 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.
Molto tempo fa sapevo come le stringhe JSL venivano mantenute in memoria; se non erro, c'era un cambiamento significativo nelle prestazioni intorno ai 24 caratteri. Potresti voler usare la versione a 96 bit e assicurarti che rimanga uguale o inferiore a 24 cifre esadecimali. Per 10.000.000 di stringhe lo spazio è 1/4 GB sulla tua macchina da 16 GB... e sei passato da 1e-6 a 1e-15 possibilità di imbatterti in un errore. Se non fosse per la sfortuna, non avrei affatto fortuna - 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: Creazione di funzioni JSL implementate in PythonDi nuovo, grazie Paul e Craige. Tra l'altro, l'integrazione di Python mi ha entusiasmato abbastanza da provare a imparare Python, dato che i miei colleghi IT qui vogliono capire parte del mio codice JSL e mi chiedono sempre quale sarebbe l'equivalente Python. Sono molto grato a Paul per tutto questo lavoro.
Devo anche correggere il calcolo del numero di valori hash nel mio script sopra: credo che sia più simile a 2^64 - 2^63, molto meno di quanto ho detto.
Per quanto riguarda la memorizzazione dei valori hash, mi hai convinto a non cercare di essere così sdolcinato. Penso che tu mi abbia convinto che alla fine sia meglio memorizzare solo i caratteri esadecimali. Dovrò sperimentare alcune unioni su tabelle enormi per vedere se fa davvero qualche differenza di velocità se unisco colonne di stringhe esadecimali o colonne di float; o se uso stringhe o float come ID di collegamento.
Se mai dovessi esportare le mie grandi tabelle JMP in un RDB, potrei convertirle in longint a quel punto.
Re: Creazione di funzioni JSL implementate in PythonCraige, a proposito del numero di valori hash, hai assolutamente ragione. Ero di nuovo confuso e non avevo letto il tuo post. 2^52 * 2046 è corretto.