JMP 17以前では、JSL関数のPython Set()およびPython Send()は、データテーブルのコピーを一時CSVファイルとして書き出した後、pandas.read_csv( temp_file.csv )を呼び出し、Pythonにpandas DataFrameとして渡していました。
JMP 18以降では、jmp.DataTable Pythonオブジェクトは、JMPデータテーブルのコピーではなく、データテーブルそのものへの参照となっています。このため、Pythonでテーブルの操作や編集を行った場合、そのテーブルを使用したJSLやJMPプラットフォームの結果に即座に反映されます。
Pythonに渡されるデータテーブルがpandas DataFrameオブジェクトであることを前提としたスクリプトのために、JMPのサンプルスクリプトのディレクトリにdt2pandas.pyが用意されています。このスクリプトは、jmpパッケージのrun_jsl()関数とpandasを利用した、to_pandas( data_table )という関数を提供しています。この関数は、CSVファイルをディスク上に一時的に書き出して、pandas.read_csv()でインポートするという動作を再現しています。
dt2pandas.pyスクリプトは、JMPのsite-packagesディレクトリにコピーして、インストール済みのパッケージと同じような感覚で使用することができます。dt2pandas.pyをJSLのスクリプトファイルと同じディレクトリに保存すれば、dt2pandasモジュールによって、次のようなJSLを実行できます。
Names Default to Here(1);
dt = open("$SAMPLE_DATA/Big Class.jmp");
Python Send(dt);
Python Submit("\[
import dt2pandas as j2pd
df = j2pd.to_pandas(dt);
print(df.head)
]\");
Pandas DataFrameは、データテーブルの列に対しループ処理を行って作成することもできます。この場合、データはファイルシステムにコピーされる代わりに、メモリ上に保持されるため、バイナリデータをテキストに変換して再びバイナリに戻すという処理を行わずに済み、精度が落ちるのを防げます。また、バイナリをテキストに書き出すと、データサイズが大幅に増加しますが、それを回避することもできます。
次の例では、JMPデータテーブルオブジェクトの列をループ処理することによって、これらの列に対応するPandas DataFrameの列を作成しています。
JMPのサンプルスクリプトのディレクトリにあるJMP2pandas.pyを開いて、このスクリプトを入手することもできます。
import jmp
import numpy as np
import pandas as pd
# JMPデータテーブルからPandas DataFrameを直接作成する
dt = jmp.open(jmp.SAMPLE_DATA + "Big Class.jmp")
df = pd.DataFrame()
for idx in range( len(dt) ):
print(dt[idx].name)
if dt[idx].dtype == jmp.DataType.Numeric:
# Pythonのバッファプロトコルを使って、数値列を直接作成する
col = np.array( dt[idx] )
df[ dt[idx].name ] = col.tolist() # Pandasのリストにする
elif dt[idx].dtype == jmp.DataType.Character:
# 値を繰り返し処理することによって、リストを作成する
col = list()
for i in range ( dt.nrows ):
col.append(dt[idx][i])
# リストから文字列の列を作成する
df[ dt[idx].name ] = col
else:
print( dt[idx].dtype )