Pythonで実装されたJSL関数の作成最近、JMP 社内で Python のヒントとコツについて講演しました。特に強力だと思うヒントが 1 つあるので、皆さんと共有したいと思います。それは、Python 関数の呼び出しをラップする JSL 関数の作成です。言い換えると、これは Python で記述できるユーティリティ コードですが、JSL インターフェイスが与えられます。説明を簡単にするために、2 つのオブジェクトを受け取ってそれらを加算し、その呼び出しを JSL 関数でラップする単純な Python 関数を実装しました。まず、3 つの異なるタイプのデータ (数値、文字列、リスト) と、2 つの 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
]\");
「+」演算子をサポートする任意の Python オブジェクトは、この 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);
結果:
result = plus(one,two)
/*:
1.0
2.0
result = 3;
ここからが面白くなります! 特に、Python で定義された plus() 関数をラップする JSL 関数です。
// 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) );
結果を示します:
//:*/
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};
単純な plus() 関数は数値の合計と文字列およびリストの連結を処理しましたが、これらはすべて JSL 関数として呼び出すことができます。残りの JSL コードは、実際に何が呼び出されたかが Python で発生したことをまったく認識していません。
完全なファイルを添付します。
JSL でコーディングを続けたいのですが、特定の関数については Python で簡単に記述できることがわかっています。では、その機能を Python で実装する JSL 関数を作成するにはどうすればよいでしょうか。
自動化とスクリプトRe: Python で実装された JSL 関数の作成Python サポートの改善に感謝します@Paul_Nelson !
Python Seleniumによるブラウザスクリプトでは、これが示されています
// 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)));
最後の 2 行は、ラッパー関数を介して呼び出し、Web ブラウザーを JMP Web サイトに移動させます。JSL 関数は、Paul の例とまったく同じように動作します。
Re: Python で実装された JSL 関数の作成複数の戻り値を持つグローバルな複数行の正規表現検索
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: Python で実装された JSL 関数の作成@hogiに関して注意: Function() には return(matches); ステートメントが必要であると考えています。
また、Python Execute( ) を使用すると、送信と取得を行う必要がなくなります。
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: Python で実装された JSL 関数の作成ああ、そうだ。Python Execute の方がずっといいよ : )
JSL 関数では、 return()で囲まなくても最後の式が戻り値になります。
Re: Python で実装された JSL 関数の作成私は Python に精通しているわけではありませんが、使用したい Python ライブラリ (xxhash など) がいくつかあります。
メモリの使用について質問があります。このように Python を JSL 関数でラップし、ラップされた Python コード内に Python オブジェクトが作成された場合、JSL ラッパー関数の呼び出しにより、関数が呼び出されるたびに *新しい* 永続的な Python オブジェクトが作成されますか (たとえば、関数を数千万回呼び出すとメモリが消費されますか)?
たとえば、jmpstart.jsl コード内で名前付き Python オブジェクトを 1 回作成し、そのオブジェクトをプロシージャ内で参照するとどうなりますか。その方がよいでしょうか。
以下は、xxhash で実行したい操作の例です。(jmpstart.jsl スクリプトで xxhash を Python 環境にインポートしていることに注意してください。JSL は 8 バイトの整数をサポートしていないため、この関数から JSL float を出力できません...)
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: Python で実装された JSL 関数の作成PS. 16 進ダイジェストが無効な IEEE 浮動小数点数になる確率が約 0.8% あり、その結果プロシージャが JSL 欠損値を返すため、これはひどいコードだと気づきました。とはいえ、示されているようにメモリ管理についてはまだ疑問が残ります。
Re: Python で実装された JSL 関数の作成hashlib と base64 はどちらも組み込みの Python ライブラリです。
昨日、特にこのケースを調べるつもりでした。最悪の場合でも、JMP の実行中ずっと digest への参照が 1 つ「リーク」するはずです。JMP がシャットダウンすると、Python のシャットダウン時にその変数が解放されます。Python Set() コードはdigest変数を作成し、それを Python 環境の globals() ディクショナリに配置します。これにより、Python Get() は名前で値を検索し、その値を JSL に返すことができます。Python Execute() は基本的に、Python Send/Set()、Python Submit()、Python Get() の便利な関数です。したがって、ディクショナリにはdigestへの参照がありますが、Python Execute() がスコープ外になると、ディクショナリだけが参照を保持しているはずです。2 回目のループでは、値が新しい値で上書きされるため、古い参照は解放され、ガベージ コレクションがそのメモリを返すことができるようになります。
Re: Python で実装された JSL 関数の作成ありがとう、ポール。メモリに関しては、それが私の希望でした。hashlib と base64 を調べてみます。
背景は次のとおりです。8 バイトのハッシュが必要だったので、JSL ネイティブ MD5 関数を使用して 8 バイトに切り捨てたハッシュを作成しましたが、文字列入力ではハッシュがクラスター化されてしまうようです。非常にランダムに分散されたハッシュ値を生成する xxhash64 を勧められたので、それを使用してみたところ、適切に分散されたハッシュが生成されたようです。
前述のように、xxhash64 は longint を出力できますが、それを JSL に渡すことはできません。結果を JSL BLOB として渡すことはできますが、JMP では BLOB を LINK ID として使用できません。ハッシュを使用する目的はこれです。元の質問とは少し異なりますが、何かご意見がありましたら、ぜひお聞かせください。
Re: Python で実装された JSL 関数の作成PS. 16 進数の文字列値をリンク ID として使用することもできますが、小さくて高速なものを求めています。約 8,000 万行のテーブルを扱っています。
Re: Python で実装された JSL 関数の作成@john_madden JMP の 64 ビット倍精度数は、-2^52 から 2^52 までの符号付き整数を保持できます。hextonumber を使用すると、16 桁の 16 進数ハッシュのうち 13 桁の 16 進数を保持でき、無効な浮動小数点数に関する質問を回避できると思います。ただし、52 ビットではおそらく十分ではありません。
私もこの界隈で遊んでいて、この誕生日攻撃表が便利だと思いました。ファイルのハッシュを使って一意のキーを作っているのですが、何ビット必要になるか知りたかったのです。表を見ると、6,100,000 個のアイテムを 64 ビットのハッシュ テーブルに追加すると、衝突の確率が 1/1,000,000 になることがわかります。
私の使用例では、ファイル サーバーのディスクは 1e11 で、一般的なファイルは 1e4、つまり 1e7 ファイルが上限です。64 ビット ハッシュは小さすぎるので、96 でも問題ありません。もちろん、私は 256 を選択しました...

Re: Python で実装された JSL 関数の作成こんにちは、クレイグ。これは素晴らしいですね。256 ビット値にどの JMP タイプを使用していますか?
余談ですが、開発者がいつか MD5 よりも新しいハッシュ関数を JSL に追加してくれると嬉しいです。
Re: Python で実装された JSL 関数の作成これが私の最新の試みですが、うまく機能しているようです。
"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: Python で実装された JSL 関数の作成私の推測では、Craige は 16 進数の文字列または base64 でエンコードされた数字を使用していると思われます。
Python 統合の優れた点の 1 つは、JMP が最新のハッシュ関数に追いついて提供する必要がないことです。Python を介してその機能を使用できます。hashlib と base64 について言及した理由の 1 つは、これらが Python 標準ライブラリの一部であるため、すべての JMP ユーザーが外部パッケージをインストールしなくても使用できるからです。
Re: Python で実装された JSL 関数の作成ジョン・マデン - 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.
かなり昔に、JSL 文字列がメモリにどのように保存されるかを知っていました。記憶している限り、24 文字あたりでパフォーマンスが大幅に変化しました。96 ビット バージョンを使用して、16 進数が 24 桁以下に抑えることをお勧めします。10,000,000 個の文字列の場合、スペースは 16 GB のマシンで 1/4 GB になります...エラーが発生する可能性は 1e-6 から 1e-15 に増加しました。運が悪かったのでなければ、私はまったく運がなかったでしょう - 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: Python で実装された JSL 関数の作成もう一度、ポールとクレイグに感謝します。ちなみに、Python の統合によって、私は Python を試して学ぶ意欲が湧いてきました。ここにいる IT の同僚たちは私の JSL コードを理解したがっていて、いつも Python で同等のものは何かと聞いてくるからです。このすべての作業に対して、ポールにとても感謝しています。
また、上記のスクリプト内のハッシュ値の数の計算を修正する必要があります。2^64 - 2^63 程度で、私が言ったよりもずっと少ないと思います。
ハッシュ値の保存に関しては、あまり気取ったことはやめるように説得されました。結局は 16 進文字を保存する方がよいと納得させられたと思います。16 進文字列の列を結合するか浮動小数点数の列を結合するか、またはリンク ID として文字列を使用するか浮動小数点数を使用するかによって速度に本当に違いがあるかどうかを確認するには、巨大なテーブルで結合をいくつか実行して実験する必要があります。
大きな JMP テーブルを RDB にエクスポートする場合は、その時点でそれらを longint に変換できます。
Re: Python で実装された JSL 関数の作成Craige さん、ハッシュ値の数に関してですが、あなたはまったく正しいです。私はまた混乱してしまい、あなたの投稿を読んでいませんでした。2^52 * 2046 が正しいです。