JMP の Python: データフレーム列からデータテーブル列への変換のベストプラクティスと計算速度への影響背景
* JMP19 スクリプト環境で Python を使用する。
* 既存の JMP データテーブル (dt) から pandas のデータフレーム (df) を作成します。
* df入力を要求し、新しいdf結果を返す関数を実行します。これをdf_resultsと呼びます。
* 結果を JMP データテーブル列に転送します。
これはうまくいきましたが、2つの点から、正しく実行できていないのではないかと疑っています。1つ目は、ローカルログ出力に警告が表示されることです。2つ目は、非常に多くの行数で初めて実行した際に、非常に長い時間がかかることです。コードタイマーは10万行で25秒と表示していました。再実行すると、2回目は3秒かかりました。タイマーを設定して各ステップの詳細を表示することで、1行のコードにほとんどの時間がかかっているかどうかを確認できるようにしました。
疑問に思っているのは、この一行のコードです。10万行の処理で、最初に定数(またはnull - 両方試しました)の列に最初に値が入力されたときのみ、25秒かかりました。その後は、毎回ほぼ10倍の速度で処理が進みました。
dt[列名] = df_results[列名]
これは埋め込まれたログ内の警告メッセージです
今後の警告: Series.__getitem__ はキーを位置として扱うため非推奨です。将来のバージョンでは、整数キーは常にラベルとして扱われます(DataFrame の動作と一致します)。位置で値にアクセスするには、`ser.iloc[pos]` を使用してください。
また、2つの異なるデータフレームから合計4つの異なる列に対してこの処理を実行しました。すべて同じ動作(および警告)を示しました。最初の実行は遅いですが、繰り返すごとに高速化しました。列1、2、3、4の間での違いは無視できるほどでした。
これは130万行の大きなテーブルの一部に過ぎません。そのため、最初の試行で速度を上げる方法が見つからなければ、25秒×4列の計算時間は250秒以上×4列になってしまいます。
ありがとう
自動化とスクリプトRe: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響JMP 18では、データフレームから列ごとにデータテーブルを構築する必要がありました。JMP 19はポータブルデータフレームプロトコルをサポートしているため、メモリ内でdt -> dfとdf -> dtをそれぞれ1行のコードで実行できます。
また、JMPでPythonスクリプトを初めて実行する際は、Python自体を初期化する必要があることにも注意してください。パッケージを初めてインポートする際は、共有ライブラリや.pyファイルのバイトコードコンパイルなど、パッケージを読み込む必要があります。
この警告はpandasパッケージから発生しています。警告をクリアするには、推奨されているser.iloc[pos]を実行する必要があります。JMPデータテーブルdtは、0から始まる列インデックスまたは列名でインデックス付けできます。インデックスを使用すると文字列の比較が回避されるため、処理が高速になります。
ヘルプ -> スクリプト インデックスの「Python」カテゴリ、jmp.from_dataframe。「JMP から Pandas」の例。
import jmp
import jmputils
try:
if not jmputils.is_installed('pandas'):
jmputils.jpip('install', 'pandas', echo=False)
except Exception as e:
print(f'Install failed with exception: {e}')
import pandas as pd
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
pandas_df = (pd.api.interchange.from_dataframe(dt))
print(pandas_df)
...
# new dt_result data table from dataframe df_result
dt_result = jmp.from_dataframe( df_result )
# or continue to do the loop on columns if you need to update the original data table from the resulting dataframe.
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響また、Python から単一の JMP 列に新しい値を入力する最も速い方法は何ですか?
たとえば、10 万行のデータ テーブルの場合はどうでしょうか?
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響ありがとう、ポール。エラーを回避するために、jmp.from_dataframe を実装してデータテーブルに戻ります。
もう一つの疑問は、なぜこれほど速度差があるのかということでした。その答えを得るためには、計算の応答が遅いという状況を確実に再現する必要がありました。最初はそれができませんでした。JMPを開いてPythonをロードした直後の最初の実行を含め、すべての実行は十分に高速でした。しかし、ある決定的な違いがあるかもしれないことに気づきました。新しい列を追加(手動で、まだコードには追加していません)してコードを実行すると、その新しい列の更新時のみ、しかも最初の1回のみ遅くなるのです。
テストとして、既存の列3つと新規に作成した列1つを含む4つの列を更新しました。列1の10万行の更新には1秒、列2の更新にはさらに1秒、列3の更新にはさらに1秒、そして列4の更新には19秒かかりました。
別のテーブルと、新たに4列目を追加しました。新しい列を追加したデータテーブルを保存して閉じ、再度開き、コードを実行すると、新しい4列目は約1秒で更新されます。これは、##1、##2、##3と同じ速さです。ただし、保存のみを行い、新しい4列目を追加したデータテーブルを閉じて再度開き、コードを実行すると、4列目の更新に約19秒かかります。
これは、速度が JMP のメモリ管理の癖に関係していることを示しています。
これが解決可能な/解決しなければならない問題なのかは分かりませんが、もしかしたら「from_dataframe」ステップを使わない場合にのみ問題になるのかもしれません。ただ非常に奇妙な動作だったので、再現できて良かったです。これで、改善版のコードを試して、問題が解決するかどうかを確認できます。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響同僚の一人が、JSL には Start Data Update と End Data Update があることを指摘しました。
jmp.run_jsl( 'dt << Begin Data Update;' )
# your python code filling new column
...
jmp.run_jsl( 'dt << End Data Update;' )
おそらく、Pythonからこれをサポートすることになるでしょう。おそらく、多数の行を入力/更新する際にテーブルのUI更新を一時停止するために、withブロックを使用するでしょう。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響jsl Begin Data Updateでラップされたdt[index]を使用して列全体を設定します。
jmp.run_jsl( 'dt << Begin Data Update;' )
dt[ index ] = list_like_object # numpy array, list, dataframe column,...
jmp.run_jsl( 'dt << End Data Update;' )
または、テーブルが入力されるまでデータ テーブルを非表示にして、入力が完了したら表示するようにします。
import jmp
dt = jmp.DataTable('Powered By Python', 40, visibility='Invisible')
...
# fill table
...
jmp.globals['jdt'] = dt # set jdt in JSL to reference dt
jmp.run_jsl('::jdt << Show Window(1);')
オプションの可視性パラメータはJMP 19以降で使用できます。jmp.globals[ ]は19の機能です。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響jmp.run_jsl( 'dt << Begin Data Update;' ) は、dt がJSLのテーブル変数であると想定しており、Python側でも同様に動作します。これはJSLからのPython Send(dt); によって発生するものですが、テーブルがPythonから作成された場合は必ずしも同じ結果になるとは限りません。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響ご説明ありがとうございます。
つまり、コードは次のようになります。
dt[ index ] = list_like_object
パイソンでは列を段階的に入力しますか? 速度の問題の原因はどれですか?
似ているように見える
dt[ 0, columnname] = [values ...]
日本語では、列を 1 ステップで埋めるので、 dt << Begin/end Data Updateで囲む必要はありません。
参考になりました。詳しい情報についてはどこで確認できますか?
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響どれだけのユーザーが重要かは分かりませんが
dt << Begin Data Update;
は良いコードを書くためのものです。しかし、多くのユーザーは知らないと思います。
@bswedloveからのfor each row() に関する関連する要望:
各行(データ更新=1、...)
解決策を制度化するのは素晴らしいアイデアです。ユーザーにとって問題がなくなるようにします (解決策は常に存在します)。
JSL および Python に当てはまります。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響「 JMPデータテーブルdtは、0から始まる列インデックスまたは列名でインデックス付けできます。インデックスを使用すると文字列の比較が回避されるため、処理が高速になります。 」
インデックスを使用してコードを書き直そうとしていますが、データフレームの名前からデータテーブル列のインデックスを決定するための同等の構文が見つからないようです。
dfColumnIndex = df.get_loc['データフレーム列名']
dt列インデックス = ???
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響@jthi 、「存在しない問題を修正するためにコードを追加する」という作業の名前は何ですか?
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響df が dt から作成された場合、両者は同じインデックスを持つと想定されます。データフレームに列を追加した場合、または一方が他方の元でなかった場合は、dt['col_name'] = を使用するのがおそらく唯一の手段です。インデックスと比較するとそれほどコストはかからないはずですが、直接数値インデックスを使用するのに対し、文字列比較を行うことになります。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響これらはすべてPythonのプロトコルバッファの魔法によって実現され、データはメモリ内のサポートされているメモリブロック間にマッピングされます。Begin | End Data Update による囲みは必ずしも必要ではありませんが、列全体を一度にマッピングできない場合は、値を反復処理するフォールバック処理が行われるため、Data Update ステートメントによる囲み処理は優れた安全策となります。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響数多くの実験と様々な方法を試しました。警告が出るものもあれば、出ないものもありました。最もシンプルなのは、dfからdtを作成し、それを使ってデータを転送する方法のようでした。この方法ではエラーは発生しませんでした。
しかし、速度の問題に関しては…違いが出た唯一の点は、新しく作成された列に値を書き込もうとしていたのか、それともデータテーブルを開いた(または作成した)時点で既に存在していた列に値を書き込もうとしていたのかという点でした。新しく作成された列の更新は非常に遅く、既存の列の更新は十分に高速でした。
10 万行の最後のテキストの結果。このコードを実行する前に、4 つのパラメーターがすでに存在し、5 つの新しい列が追加されました。合計 9 列が更新されました。
#これらの「xfer」はすべてdt['colname'] = dt_results['colname']構文を使用しています。DFを使用してdt_resultsを作成した後、DataFrameは使用されていません。
ステップdf_results作成時間: 0.3434秒
ステップパラメータ1転送時間: 0.1795秒
ステップパラメータ2の転送時間: 0.1758秒
ステップ df_results 作成時間: 0.0231 秒 #これは、異なる df 関数の戻り値の新しい結果セットです。
ステップパラメータ3の転送時間: 0.1838秒
ステップパラメータ4 転送時間: 0.1890秒
#ここでのコードは次のとおりです: dt['NewCol1Test'] = df_results[stringnameofcolumn]
今後の警告: Series.__getitem__ はキーを位置として扱うため非推奨です。将来のバージョンでは、整数キーは常にラベルとして扱われます(DataFrame の動作と一致します)。位置で値にアクセスするには、`ser.iloc[pos]` を使用してください。
名前付きdfからdtへの転送時間: 15.8251秒
#ここでのコードは次のとおりです: dt['NewCol2Test'] = df_results.iloc[:,dfColumnIndex]
今後の警告: Series.__getitem__ はキーを位置として扱うため非推奨です。将来のバージョンでは、整数キーは常にラベルとして扱われます(DataFrame の動作と一致します)。位置で値にアクセスするには、`ser.iloc[pos]` を使用してください。
#iloc を使用したときになぜ警告がまだスローされるのか分かりません
iloc 転送時間を使用した df から dt への変換: 16.0874 秒
#ここでのコードは次のとおりです: dt['NewCol3Test'] = dt_results[dfColumnIndex]
dt インデックスから dt 名への転送時間: 14.5659 秒
#ここでのコードは次のとおりです: dt[NewColumnIndex] = dt_results[dfColumnIndex]
dtインデックスからdtインデックスへの転送時間: 14.4699秒
#ここでのコードは次のとおりです:
jmp.run_jsl('dt << データ更新開始;')
dt['NewCol5Test'] = dt_results[dfColumnIndex]
jmp.run_jsl('dt << データ更新終了;')
#警告/エラー結果:
「名前未解決: 'dt' のアクセスまたは評価で dt が見つかりました、dt/*###*/
1行目
名前未解決: 'dt' のアクセスまたは評価で dt が見つかりました、dt/*###*/
1行目
#この出力は、実際には 2 番目の「1 行目」エラー メッセージと同じ行にありました。
dt から dt w のインデックスは begin/end 更新転送時間でラップされています: 14.3627 秒
#10 万行の実行時間の最終要約。
合計実行時間: 77.0904秒
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響このコードを 2 回目に実行する前にデータ (*) を保存するか (**)、開始/終了データの更新を有効にしてください。
import jmp
import numpy as np
dt = jmp.open(jmp.SAMPLE_DATA + "Wafer Stacked.jmp")
jmp.globals['jdt'] = dt
jmp.run_jsl('Graph Builder( Variables( Y( :defects ) ), Elements( Bar( Y, Label( "Label by Value" ) ) ))');
# jmp.run_jsl('jdt << Begin Data Update;')
dt[ 5 ] = [np.random.randint(1000)]*177875
# jmp.run_jsl('jdt << End Data Update;')
(*) 冗談です。データが失われることはありません。コーヒーを飲んで、JMP が完了するまでお待ちください。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響
@hogi さんは書きました:
…もう一度(**)
(**)TS - 00251136
@Dahlia_Watkins は、速度の問題に直面するにはコードを 2 回実行する必要がある場合があることに気付きました。
タイミングの「問題」のようです。
おそらく、コードを初めて実行したとき、Python が行ごとのデータ更新を完了するまでプロットは作成されません。
編集:
実際には、プロットは、Python データの更新を遅くするインタラクティブな要素としては必要ありません。
インタラクティブな要素のないその他の例については、以下の投稿をご覧ください。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響注意: Jmp が予期しない動作をする箇所にトピックを追加しました
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響元の問題:「新しい列がある場合、データアクセスが遅くなる」
ここに説明的な例があります:
import jmp
dt = jmp.DataTable('dt_new', 50000)
col = dt.new_column("newCol")
# --------- cut here to make it slow - why? -------------
dt[ 0 ] = [1]*50000
2 つのステップに分割すると、JMP で 50,000 件のエントリを更新するのには数秒かかります。
不可解な:
両方のブロックを 1 回の実行で実行すると、JMP に問題は発生しません。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響@Paul_Nelsonの投稿で言及されている回避策の他に、次のような追加の回避策もあると思いました。
- JSLを使用して列/データテーブルを作成します
- JSLを使用して列にダミー値を入力します
- JSLを使用してデータテーブルを保存し、閉じて再度開きます
それらの多くは一見良さそうに見えますが、それは誤解を招きます。
速度が速いのは、同じ値が列に再度書き込まれ、JMP が「データ更新アラート」をトリガーしないからです。
例えば
jmp.globals['jdt'] = dt
jmp.run_jsl('''
nc = n cols(jdt);
Column(jdt,nc) << set each value(1)
''');
nc = dt.ncols
data = [1]*50000
dt[ nc - 1 ] =data
Python 経由でも同じ奇跡が起こります:
# 1st: slow
nc = dt.ncols
dt[ nc - 1 ] = range(50000)
# fast ! (same values!)
dt[ nc - 1 ] = range(50000)
# new values: slow again
dt[ nc - 1 ] = [5]*50000
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響JMP がデータが更新されたと判断したときに通知を受け取るトリガーを設定できます。
import jmp
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
jmp.run_jsl('''
New Column( "trigger",
Formula(
If( Row() == 1,
Try( Caption( Char( ::i=::i+1 ) ), ::i = 1 )
);
:age;i
)
)
''')
dt[ 1 ] = range(40)
dt[ 1 ] = range(40) # no data update !
dt[ 1 ] = [2]*40
# for loop without begin/end data update -> finished before JMP notices the data update
jmp.run_jsl('''
for each row(:age = row());
wait(5);
''')
# for loop without begin/end data update - and some delay to give JMP the time to notice:
jmp.run_jsl('''
for each row(:age = 2*row();wait(0))
''')
したがって、実際には、元の値と新しい値が同一である場合、「データの更新」は行われません。
無限ループに対抗するために実装されましたか? 素晴らしい!
面白い、
import jmp
dt = jmp.open(jmp.SAMPLE_DATA + "Wafer Stacked.jmp")
jmp.run_jsl('''
delete symbols(::i)
New Column( "trigger",
Formula(
If( Row() == 1,
Try( Caption( Char( ::i=::i+1 ) ), ::i = 1 )
);
:Y_Die;i
)
)
''')
nr = dt.nrows
dt[ 4 ] = range(nr)
1 分以上かかります - そして 2 まで数えます。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響
@Paul_Nelson wrote:
...
Most likely we will want to do support this from Python. Probably using a with block to suspend the UI updates of the table when filling / updating large number of rows.
素晴らしいアイデアですね!
Python が "with" ブロック内でエラーをスローした場合でも、インタラクティブ機能をオンに戻すように注意してください。
代替案として:
インタラクティビティが無効になっていることを示すインジケータをデータグリッドに追加してください。
終了データの更新なしでデータの更新を開始します。
インジケータ: このデータテーブルではインタラクティブ機能が無効になっています
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響前回の投稿の調査結果を要約すると次のようになります。
import jmp
# open data table and wait for the "Interactivity Manager" to get active
jmp.run_jsl('''
dt = new table("test", add rows(100000), new column ("col"));
Python send(dt);
wait(0) // disable the delay to make it fast
'''
);
nr=dt.nrows
# Python data update: slow!
dt[ 0 ] = range(nr)
# --- run step by step: -----
# same values: fast
dt[ 0 ] = range(nr)
#new values: slow
dt[ 0 ] = [1]*nr
Python 経由で JSL 列を更新すると非常に時間がかかります。
例外:
- データ テーブルを作成/開いた直後の書き込みアクセス (何らかの「インタラクション マネージャー」がアクティブになる前)
- まったく更新されない(値は変更されない)
@Paul_Nelsonの投稿で言及されている回避策:
- プライベートデータテーブル
- JSL データ更新の開始/終了()で囲まれた Python データ更新

高速な Python データ アクセス - JSL のサポートのみ ; )
これらの問題は、次の「.x」リリースで解決されるほど重大なものだと思いますが...
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響実際には、2番目のケースでは、visibility='Private' のプライベートテーブルにする必要があります。「Invisible」テーブルは非表示になっているにもかかわらずGUIが残っているため、セルを編集するたびにデータテーブルでGUIが更新されるため、依然として遅延が発生します。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響はい、これは重要です。実際に修正を検討しています。Pythonのパフォーマンスを、ユーザーがPythonから実行したくなるようなレベルにまで向上させ、パフォーマンス向上のためにバックドアのマジックメソッドを使う必要がないようにしたいと考えています。
JMP 18やJMP 19 EAのテストでこの問題が見つからなかったのは驚きです。jmp.DataTableオブジェクトの中心となるのはJSLデータテーブルです。Pythonオブジェクトは基盤となるJMPデータテーブルへの参照に過ぎず、JMPデータテーブルの内部メソッドを呼び出しているため、run_jsl( 'dt << Begin Data Update')が重要な意味を持ちます。jmp.run_jsl(''' code '''')の目的は、まだ実装していない機能のためにPythonからJSLを呼び出せるようにすること、あるいは今回のケースのように列更新のパフォーマンスに関する見落としを修正することです。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響問題が解決したと聞いて嬉しいです。
なぜこれまで検出されなかったのでしょうか?
多くのユーザーは、JMPにデータを転送する前のデータのインポートやデータキュレーションにPythonを使用していると思います。JMPではPython経由でデータを操作することはありません。Pythonのget(dt)[JSL]やfrom_dataframe() [Python]を使用すれば、この問題は発生しません。
注意: JMP 18 での Python 統合の開始方法と同様に、列ごとに転送します。
ここではデータテーブルのサイズに依存します。テーブルが十分に小さい場合、「インタラクティビティマネージャー」がアクティブ化される前に新しい列が追加されます。
当社側で問題を検出したとき、JMP19EA にアクセスし、 from_dataframe()を介してテーブル生成を高速化できること、さらにコードがはるかに簡単になるという追加の利点があることを確認して満足しました。
一方、 JMPyFacade: Bridging JMP and Python for Seamless Engaging Analysisのように、既存のJMPデータテーブルの個々の列をPythonで更新すると、この問題が発生します。JSLのプロである@jthiさんは、あまり迷うことなく、必要なプライベートテーブルマージデータ回避策を確実に追加しました。
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響今日の Scripters Club で共有された情報:
速度の問題
dt[ index ] = list_like_object
JMP 19.0.3 で修正されます:)
Re: JMP の Python: データフレーム列からデータテーブル列へのベストプラクティスと計算速度への影響今すぐ入手可能:
