Pythonと.NETの表データ交換
そもそもPython -> .NETでデータフレームを交換する場合、.NET側はDataTableという標準ライブラリのクラスがあるので、それが適しています。決してPandasやdplyrのような高機能なデータ処理能力は無いのですが、とりあえずマッピングすることができます。
BayesServerでPythonからも呼びたい場合は、以下にあるようにJPypeを使ったJavaライブラリへのアクセスを推奨されているようです。
一方で、pythonnetでもいいよ、と一言書いてあり、何で.NETネイティブなのに、Javaラッパーを通さないといけないのかよくわからないので、そちらで行くことにしました。RのラッパーもJavaラッパーを通します。これだと、APIリファレンスを2種類見ないといけない羽目になります。(どっちみち.NETライブラリに改造を加えて、再ラップするので)
最初からPythonにしておけば良かった。。。
pythonnet
pythonnet はPython for .NETと呼ばれる、Pythonから.NETのアクセスライブラリです。
http://pythonnet.github.io/
Pythonから.NETを呼び出す場合、.NETからPythonを呼び出す場合の双方で使えますね。
Python + .NETですと、IronPythonを思い出す人もいるのでしょうけども、そちらは.NET上で動くCLI言語です。これだと、データサイエンスなどで定評のあるライブラリも対応は限られます。というかnumpyが動きません。
pythonnetは言語間の接続を行うだけなので、両者の良いところを残したまま使えます。最新の.NET Coreなどにも対応します。
感覚的に言うと、
- Pythonから、.NETのライブラリを using ではなく importで使える。
import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Form
import clrというのが趣があります。
- .NETから、Pythonのライブラリを .NET 上でimportできる
dynamic np = Py.Import("numpy");
var x = np.cos(np.pi * 2);
こっち方向は、 @hogegex さんの記事があります。
用途としては、Pythonから.NET独自のライブラリ(ビジネス系など)を呼び出したり、.NETから数値演算のタスクを投げたりなどが考えられます。Python上で.NETのGUIライブラリを使って、アプリを作れるのは中々良さそうですね。
ただ、私の用途はあくまでBayesServerのライブラリをPythonから呼ぶだけなので、以下のことができれば、おおむね完了です。
Python DataFrame -> .NET DataTable変換
BayesServerのリファレンスにはヘルパークラスとして、jpype1を使ったデータフレームのマッピングヘルパー関数が用意されています。
Pandas DataFrame helper functions
おそらく、jpype1を使った変換は癖があるので、こういったものを用意してくれていると思うのですが、pythonnetだともっとストレートに書けます。
ほぼ、コピペで.NET向けに書き直してみました。
# %%
import numpy as np
import pandas as pd
import clr
from System.Data import *
# %%
def _to_net_class(data_type):
"""
Converts numpy data type to equivalent .NET class
:param data_type: the numpy data type
:return: The Net Class
"""
if data_type == np.int32:
return clr.GetClrType(Int32)
if data_type == np.int64:
return clr.GetClrType(Int64)
if data_type == np.float32:
return clr.GetClrType(Single)
if data_type == np.float64:
return clr.GetClrType(Double)
if data_type == np.bool:
return clr.GetClrType(Boolean)
if data_type == np.object:
return clr.GetClrType(Object)
raise ValueError('dtype [{}] not currently supported'.format(data_type))
# %%
def to_data_table(df):
data_table = DataTable()
for name, data_type in df.dtypes.iteritems():
net_class = _to_net_class(data_type)
data_table.Columns.Add(str(name),net_class)
for index, row in df.iterrows():
xs = [None if pd.isnull(x) else x for x in row]
data_table.Rows.Add(xs)
return data_table
これで、pandasのデータをするっと.NETライブラリに持ち込めるので、いわゆるExcel表のようなGUIや、データベースとの交換も楽になるかもです。
clr.GetClrType()というのがtypeof(やObject.GetType())の代わりみたいなものだと思ってください。