(前書き)PyObjectって何
JuliaはPyCall.jlパッケージを使うとPythonのパッケージを使用することができます。何がうれしいかというと、例えばこれはJuliaで数値計算を行い、プロットをPythonのパッケージを使用するといったことが可能になります。図表プロットの学習コストは意外と高いと思っている私としては、うれしい機能です。
さて、Julia側でPythonパッケージを呼び出すと、Juliaは、__PyObject__という型でそれらのデータを受け取ります。詳しくは、PyCall.jlのgithubにいろいろと書いてあります。私はドキュメントを読んで初めて、numpyで生成した配列はjuliaのVector型に変換してくれるのしりました。
using PyCall
@pyimport numpy as np
@test np.array( (2, 2) ) == [2, 2] # Test Passed
上の例では、numpyのarray型は自動変換されましたが、PyObjectで受け取るようなものにどのようなものがあるかを紹介します。今回は、時系列分析を行うときに使う、statsmodelsをJuliaでimportしてみようと思います。
using PyCall
using Random
# 自己回帰モデル
@pyimport statsmodels.tsa.ar_model as ar
x = rand(100)
model = ar.AutoReg(x, lags=1)
typeof(model) # PyObject
呼び出されたモジュールは、ar.AutReg(x, lags=1)のように「.」を使って関数や属性を参照できます。ただ、ほかにもアクセスする方法があって、例えば、PyObject[:attribute]、PyObject."attribute"のようなこともできます。
(本題)keys( PyObject )で使用可能な属性を見ることができる
私がstatsmodelsのtest_causalityをJuliaで呼び出したときのことです。summaryメソッドでグレンジャー因果性の検定結果を受け取ろうとすると次のようになりました。ここでres_grangerはtest_causalityの戻り値を格納した変数です。
res_granger.summary()
=> PyObject <statsmodels.iolib.table.Cell object at 0x00000000A9B24910> ...
いや、statsmodels.iolib.table.Cellって何って感じです。ドキュメント見てもさっぱりでした。そんな時はkeysを使って力業で行きます。
keys( res_granger.summary() )
=> CartesianIndex(1, 1) ...
CartesianIndicesはJuliaの配列の参照に使うもので今回はいったんおいておきます。summaryメソッドの戻り値に対してはインデックスしか返ってこなかったので、test_causalityの戻り値にkeysを行います。
keys( res_granger )
=> :__class__
:__delattr__
:__dict__
:__dir__
:__doc__
.
.
.
:pvalue
:signif
:signif_str
:summary
:test
:test_statistic
:title
なにかいろいろ出てきました。これらは配列として受け取れているので、for文を使えばすべて参照できます。
for i in keys( res_granger )
println( res_granger[i] )
end
=>
PyObject <class 'statsmodels.tsa.vector_ar.hypothesis_test_results.CausalityTestResults'>
PyObject <method-wrapper '__delattr__' of CausalityTestResults object at 0x00000000A98CE850>
Dict{Any, Any}("test_statistic" => 0.00020723966986146915, "method" => "F", "signif_str" => " at 5% significance level", "causing" => ["y2"], "caused" => ["y1"], "pvalue" => 0.9885243725190264, "df" => (1, 282), "crit_value" => 3.8746454100339562, "conclusion" => "fail to reject", "h0" => "H_0: y2 does not Granger-cause y1", "test" => "granger", "title" => "Granger causality F-test", "conclusion_str" => "Conclusion: fail to reject H_0", "signif" => 0.05)
PyObject <built-in method __dir__ of CausalityTestResults object at 0x00000000A98CE850>
Results class for Granger-causality and instantaneous causality.
Parameters
----------
causing : list of str
This list contains the potentially causing variables.
caused : list of str
This list contains the potentially caused variables.
test_statistic : float
crit_value : float
pvalue : float
df : int
Degrees of freedom.
signif : float
Significance level.
test : str {``"granger"``, ``"inst"``}, default: ``"granger"``
If ``"granger"``, Granger-causality has been tested. If ``"inst"``,
instantaneous causality has been tested.
method : str {``"f"``, ``"wald"``}
The kind of test. ``"f"`` indicates an F-test, ``"wald"`` indicates a
Wald-test.
PyObject <bound method CausalityTestResults.__eq__ of <statsmodels.tsa.vector_ar.hypothesis_test_results.CausalityTestResults object at 0x00000000A98CE850>>
PyObject <built-in method __format__ of CausalityTestResults object at 0x00000000A98CE850>
PyObject <method-wrapper '__ge__' of CausalityTestResults object at 0x00000000A98CE850>
PyObject <method-wrapper '__getattribute__' of CausalityTestResults object at 0x00000000A98CE850>
PyObject <method-wrapper '__gt__' of CausalityTestResults object at 0x00000000A98CE850>
nothing
PyObject <bound method CausalityTestResults.__init__ of <statsmodels.tsa.vector_ar.hypothesis_test_results.CausalityTestResults object at 0x00000000A98CE850>>
PyObject <built-in method __init_subclass__ of type object at 0x00000000A87BC0D0>
PyObject <method-wrapper '__le__' of CausalityTestResults object at 0x00000000A98CE850>
PyObject <method-wrapper '__lt__' of CausalityTestResults object at 0x00000000A98CE850>
statsmodels.tsa.vector_ar.hypothesis_test_results
PyObject <method-wrapper '__ne__' of CausalityTestResults object at 0x00000000A98CE850>
PyObject <built-in method __new__ of type object at 0x00007FFEC42FBB50>
PyObject <built-in method __reduce__ of CausalityTestResults object at 0x00000000A98CE850>
PyObject <built-in method __reduce_ex__ of CausalityTestResults object at 0x00000000A98CE850>
PyObject <method-wrapper '__repr__' of CausalityTestResults object at 0x00000000A98CE850>
PyObject <method-wrapper '__setattr__' of CausalityTestResults object at 0x00000000A98CE850>
PyObject <built-in method __sizeof__ of CausalityTestResults object at 0x00000000A98CE850>
PyObject <bound method HypothesisTestResults.__str__ of <statsmodels.tsa.vector_ar.hypothesis_test_results.CausalityTestResults object at 0x00000000A98CE850>>
PyObject <built-in method __subclasshook__ of type object at 0x00000000A87BC0D0>
nothing
["y1"]
["y2"]
fail to reject
Conclusion: fail to reject H_0
3.8746454100339562
(1, 282)
H_0: y2 does not Granger-cause y1
F
0.9885243725190264
0.05
at 5% significance level
PyObject <bound method HypothesisTestResults.summary of <statsmodels.tsa.vector_ar.hypothesis_test_results.CausalityTestResults object at 0x00000000A98CE850>>
granger
0.00020723966986146915
Granger causality F-test
省略せずにすべて載せましたが、下のほうに検定統計量やp値が載っています。よくよく見るとkeysで取り出した属性にもpvalueといったそれっぽいものがあるので、所望の値がどこに格納されているのかわかる場合はその属性だけを参照するほういいかもしれません。
結論
最悪keysを使えば何とかなりそう。ほかにいい方法知っているかたがいらっしゃれば教えてください。convertとか使えば変換できるっぽいのですが私はうまく使いこなせませんでした。
おまけ
Pythonはアンダースコアの数と位置によって役割が変わります。詳しくはここがわかりやすかったです。
__ dict _に所望のデータが全てありました。 dict __で保管されがちなのかな?
res_granger.__dict__
=> "test_statistic" => 0.00020724
"method" => "F"
"signif_str" => " at 5% significance level"
"causing" => ["y2"]
"caused" => ["y1"]
"pvalue" => 0.988524
"df" => (1, 282)
"crit_value" => 3.87465
"conclusion" => "fail to reject"
"h0" => "H_0: y2 does not Granger-cause y1"
"test" => "granger"
"title" => "Granger causality F-test"
"conclusion_str" => "Conclusion: fail to reject H_0"
"signif" => 0.05