裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

Julia で R を使う(その2)

2021年01月29日 | ブログラミング

== サポートされている変換

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

 

コメント    この記事についてブログを書く
  • Twitterでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« Julia で R を使う(その1) | トップ | 汎用性のあるプログラム(関... »
最新の画像もっと見る

コメントを投稿

ブログラミング」カテゴリの最新記事