== サポートされている変換
RCall は最も基本的な Julia のデータ型や人気のある統計パッケージ例えば DataFrames, CategoricalArrays や AxisArrays との相互変換を支援する。
== Julia の基本データ型
julia> # Julia -> R
julia> a = robject(1)
RObject{IntSxp}
[1] 1
julia> # R -> Julia
julia> rcopy(a)
1
julia> # Julia -> R
julia> a = robject([1.0, 2.0])
RObject{RealSxp}
[1] 1 2
julia> # R -> Julia
julia> rcopy(a)
2-element Array{Float64,1}:
1.0
2.0
== 辞書型 Dictionaries
julia> # Julia -> R
julia> d = Dict(:a => 1, :b => [4, 5, 3])
julia> r = robject(d)
RObject{VecSxp}
$a
[1] 1
$b
[1] 4 5 3
julia> # R -> Julia
julia> rcopy(r)
OrderedCollections.OrderedDict{Symbol,Any} with 2 entries:
:a => 1
:b => [4, 5, 3]
== 日付 Date
julia> using Dates
julia> # Julia -> R
julia> d = Date(2012, 12, 12)
julia> r = robject(d)
RObject{RealSxp}
[1] "2012-12-12"
julia> # R -> Julia
julia> rcopy(r)
2012-12-12
== 日付と時間 DateTime
julia> # julia -> R
julia> d = DateTime(2012, 12, 12, 12, 12, 12)
julia> r = robject(d)
RObject{RealSxp}
[1] "2012-12-12 12:12:12 UTC"
julia> # R -> Julia
julia> rcopy(r)
2012-12-12T12:12:12
== データフレーム DataFrames
julia> using DataFrames
julia> d = DataFrame([[1.0, 4.5, 7.0]], [:x])
julia> # Julia -> R
julia> r = robject(d)
RObject{VecSxp}
x
1 1.0
2 4.5
3 7.0
julia> # R -> Julia
julia> rcopy(r)
3×1 DataFrame
Row │ x
│ Float64
──────┼─────────
1 │ 1.0
2 │ 4.5
3 │ 7.0
デフォルトでは,R のデータフレームの列名(変数名)は foo.bar が foo_bar になるような正規化が行われる。
julia> rcopy(R"data.frame(a.b = 1:3)")
3×1 DataFrame
Row │ a_b
│ Int64
──────┼───────
1 │ 1
2 │ 2
3 │ 3
正規化を避けるためには,normalizenames オプションを使う。
julia> rcopy(R"data.frame(a.b = 1:10)"; normalizenames = false)
10×1 DataFrame
Row │ a.b
│ Int64
──────┼───────
1 │ 1
2 │ 2
3 │ 3
4 │ 4
5 │ 5
6 │ 6
7 │ 7
8 │ 8
9 │ 9
10 │ 10
== 名前付き配列 AxisArrays
julia> using AxisArrays
julia> # Julia -> R
julia> aa = AxisArray([1,2,3], Axis{:id}(["a", "b", "c"]))
julia> r = robject(aa)
RObject{IntSxp}
id
a b c
1 2 3
julia> # R -> Julia
julia> rcopy(AxisArray, r)
1-dimensional AxisArray{Int64,1,...} with axes:
:id, ["a", "b", "c"]
And data, a 3-element Array{Int64,1}:
1
2
3
julia> bb = AxisArray([1 2 3;4 5 6;7 8 9], Axis{:id}(["a", "b", "c"]), Axis{:col}(["A1", "A2", "B"]))
julia> s = robject(bb)
RObject{IntSxp}
col
id A1 A2 B
a 1 2 3
b 4 5 6
c 7 8 9
julia> # R -> Julia
julia> rcopy(AxisArray, s)
2-dimensional AxisArray{Int64,2,...} with axes:
:id, ["a", "b", "c"]
:col, ["A1", "A2", "B"]
And data, a 3×3 Array{Int64,2}:
1 2 3
4 5 6
7 8 9
== 特別な変換
RCall はR と Julia のオブジェクトの暗黙的な変換を rcopy と robject で行うのをサポートするための API を提供する。
このアイデアを説明するために,以下のような Julia のデータ型を考える。
julia> mutable struct Foo
x::Float64
y::String
end
julia> foo = Foo(1.0, "hello")
julia> bar = R"""
bar <- list(x = 1, y = "hello")
class(bar) <- "Bar"
bar
"""
RObject{VecSxp}
$x
[1] 1
$y
[1] "hello"
attr(,"class")
[1] "Bar"
== R から Julia へ
rcopy 関数と rcopytype 関数はこの方向の変換を受け持つ。
julia> import RCall.rcopy
julia> function rcopy(::Type{Foo}, s::Ptr{VecSxp})
Foo(rcopy(Float64, s[:x]), rcopy(String, s[:y]))
end
rcopy (generic function with 92 methods)
convert 関数があると対応する rcopy 関数を起動する。
julia> rcopy(Foo, bar)
julia> convert(Foo, bar) # calls `rcopy`
Foo(1.0, "hello")
rcopy(var)が自動的に変換するのを許可するためには,R のクラス Barが登録されていなければならない。
julia> import RCall: RClass, rcopytype
julia> rcopytype(::Type{RClass{:Bar}}, s::Ptr{VecSxp}) = Foo
julia> foo2 = rcopy(bar)
Foo(1.0, "hello")
== Julia から R へ
Julia から R への変換を許可するためには RCall.sexp 関数は上書き(再定義)されなければならない。
sexp 関数は Julia オブジェクトを受け付け,SEXP オブジェクト( [Sxp] へのポインタ)を返す。
まず最初に,Julia 型の Foo から R のクラス Bar への明示的な変換法を定義する。
julia> import RCall: sexp, protect, unprotect, setclass!, RClass
julia> function sexp(::Type{RClass{:Bar}}, f::Foo)
r = protect(sexp(Dict(:x => f.x, :y => f.y)))
setclass!(r, sexp("Bar"))
unprotect(1)
r
end
julia> bar = robject(:Bar, foo)
RObject{VecSxp}
$y
[1] "hello"
$x
[1] 1
attr(,"class")
[1] "Bar"
注:SEXP がガーベージコレクションされないように保護するために RCall.protect と RCall.unprotect を使わなければならない。
robject(foo) を経由するデフォルトの変換を登録するために,sexpclass を定義する必要がある。
julia> import RCall.sexpclass
julia> sexpclass(f::Foo) = RClass{:Bar}
julia> bar = robject(foo)
RObject{VecSxp}
$y
[1] "hello"
$x
[1] 1
attr(,"class")
[1] "Bar"
== @rput と @rget をシームレスに使う
julia> foo2.x = 2.0
julia> @rput foo2
julia> R"""
foo2["x"]
"""
RObject{VecSxp}
$x
[1] 2
julia> R"""
foo2["x"] = 3.0
julia> """
julia> @rget foo2
julia> foo2.x
3.0
== 入れ子にされた変換
julia> l = R"list(foo2 = foo2, bar = $bar)"
RObject{VecSxp}
$foo2
$y
[1] "hello"
$x
[1] 3
attr(,"class")
[1] "Bar"
$bar
$y
[1] "hello"
$x
[1] 1
attr(,"class")
[1] "Bar"
julia> rcopy(l)
OrderedCollections.OrderedDict{Symbol,Any} with 2 entries:
:foo2 => Foo(3.0, "hello")
:bar => Foo(1.0, "hello")
== イベントループ
IJulia ではない対話セッションでは,R はデフォルトで新たな plot 表示ウインドウを開く。
プロットのサイズ変更などを対話的に行うために,R イベントループが自動的に開始される。
以下のようにすれば,手動で R のイベントループを開始できる。
julia> RCall.rgui_start()
true
これにより,プロットが変更されたかどうかをチェックするために R を頻繁に呼出すことになる。
終了するためには,以下のようにする。
julia> RCall.rgui_stop()
true