裏 RjpWiki

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

汎用性のあるプログラム(関数)を書こうよ

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

毎度,おいしいテーマを提供いただき,ありがとうございます。

「全て異なる」という条件をPythonで実験する

ということですが,

要素数を無制限にした,以下のような回答があるかと思いきや。

a = [1,2,3,4,5]
all([a[i] != a[j] for i in range(len(a)-1) for j in range(i+1,len(a))]) # True

a = [1,2,1,4,5]
all([a[i] != a[j] for i in range(len(a)-1) for j in range(i+1,len(a))]) # False

残念でした。

Julia だと,

allequal(a) = all([a[i] != a[j] for j = 1:length(a), i = 1:length(a) if i < j])

という関数を作ればいつでも使えます。

a = [1,2,3,4,5]
allequal(a) # false
a = [1,2,1,4,5]
allequal(a) # true

いやいやそんな冗長な!!(^_^;)

length(Set(a)) == length(a)

 で,いいじゃん!!チャンチャン!

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

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

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Julia で R を使う(その1)

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

== RCall.jl

参照 URL https://juliainterop.github.io/RCall.jl/stable/

RCall は,Julia から R 環境(R パッケージ)を利用したり,R 環境での作業結果を Julia へ取り込んだりすることができる。

== インストール

まだ RCall をインストールしていない場合は,以下の 2 行を実行。

julia> using Pkg
julia> Pkg.add("RCall")

== さあ始めよう

まず,最初に RCall パッケージをロードしよう。
他のパッケージと同じで,あるパッケージを使いたいときにはそのセッションの最初に「using パッケージ名」と指定する。

julia> using RCall

== RCall の使い方

Julia と R の相互作用のために,RCall は複数の方法を提供する。

  • R の REPL モード
  • @rput と @get マクロ
  • R"文字列" マクロ
  • RCall API: reval, rcall, rcopy, robject など
  • @rlibrary と @rimport マクロ

== R の REPL モード

Julia のプロンプトが出ているときに $ を押すと,R の REPL モードに移行する(プロンプトが julia> から R> に変わる)。
R から Julia に戻るときは delete キー(Windows の場合は backspaceキー)を」押す(プロンプトがR> から julia> に戻る)。
R の REPL モードで Julia の変数(オブジエクト)を引用するときには「$変数名」,「$]式」のようにする。
julia の println() で,文字列中に $変数名 と書けばその値で置き換えられるのと同じような使い方である。

julia> foo = 1
1

$ キーを押す

R> x <- $foo

R> x
[1] 1

R> y = $(rand(10))

R> sum(y)
[1] 5.234343

delete キーを押す

julia>

== @rput マクロと @rget マクロ

これらのマクロは,Julia と R の間で変数を転送する。
コピーされた変数は元の変数と同じ名前になる。

julia> z = 1
1

julia> @rput z
1

R> z
[1] 1

R> r = 2

julia> @rget r
2.0

julia> r 2.0

一行で複数の変数をやり取りすることもできる。

julia> foo = 2
2

julia> bar = 4
4

julia> @rput foo bar
4

R> foo + bar
[1] 6

== @R_str 文字列マクロ

RCall の別の使い方は R"文字列" マクロである。
これは特にスクリプトファイルで有用である。

julia> R"rnorm(10)" # プロンプトは julia> の状態で使う
RObject{RealSxp}
 [1]  2.12249461  0.09807257 -2.28437327 ...
 [7] -0.49853088  1.16451855 -0.41658982 ...

このような使い方をすると,文字列を R の式とみなし,R での評価結果を Julia のラッパーでくるんだ RObject として返す。
R"文字列" マクロは変数代入(\latexmath:[$変数名)をサポートする。
aa\\$]bb のような R の式としては不適切な場合にさえも使うことができる。

julia> x = randn(10)
10-element Array{Float64,1}:
 -2.8182219563281503
  0.8334083608216333
 -1.2866572484420662
 -0.719998093988448
  1.3888582310416397
  0.45538366532280794
  0.26108314692800977
 -0.2748229904314617
 -0.9151614036341179
  1.1915882754899525

julia> R"t.test($x)"
RObject{VecSxp}

 One Sample t-test

data:  `#JL`$x
t = -0.46172, df = 9, p-value = 0.6552
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 -1.1117626  0.7348546
sample estimates:
mean of x 
-0.188454 

Julia で評価される前の式を R に渡すこともできる。
このような場合には ( ) でくくる必要がある。
なお,以下の例では文字列をくくるのに二重引用符 " をつかっているが,その中の文字列中に出てくる文字列は一重引用符 ’を使う例を示している。
一重引用符の代わりに「エスケープ付の二重引用符 \"」を使ってもよい。
外側の引用符は二重引用符でなければならない(一重引用符を使うとエラーになる)。

julia> R"optim(0, $(x -> x-cos(x)), method='BFGS')"
RObject{VecSxp}
$par
[1] -1.56343

$value
[1] -1.570796

$counts
function gradient 
      14       13 

$convergence
[1] 0

$message
NULL

複数行にわたるような文字列スクリプトは,(Python と同じ)三重引用符(二重引用符を3個連続)を使う。

julia> y = 1
1

julia> R"""
       f <- function(x, y) x + y
       ret <- f(1, $y)
       """

RObject{RealSxp}
[1] 2

== RCall API

reval 関数は,入力された文字列を R コードとして評価する。
評価結果は RObject として返される。

julia> jmtcars = reval("mtcars");
julia> typeof(jmtcars)
RObject{VecSxp}

julia> names(jmtcars)
11-element Array{Symbol,1}:
 :mpg
 :cyl
 :disp
 :hp
 :drat
 :wt
 :qsec
 :vs
 :am
 :gear
 :carb

julia> jmtcars[:mpg]
RObject{RealSxp}
 [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 ...
[16] 10.4 14.7 32.4 30.4 33.9 21.5 15.5 15.2 ...
[31] 15.0 21.4

julia> df = rcopy(jmtcars); # データフレームに変換
julia> first(df, 5)
5×11 DataFrame
 Row │ mpg      cyl      disp     hp       drat ...
     │ Float64  Float64  Float64  Float64  Float64 ...
─────┼──────────────────────────────────────────────────
   1 │    21.0      6.0    160.0    110.0     3.9 ...
   2 │    21.0      6.0    160.0    110.0     3.9 ...
   3 │    22.8      4.0    108.0     93.0     3.85 ...
   4 │    21.4      6.0    258.0    110.0     3.08 ...
   5 │    18.7      8.0    360.0    175.0     3.15 ...

rcall 関数は関数呼び出しのために使用される。

jmtcars の次元サイズを求め RObject に変換する
julia> rcall(:dim, jmtcars)
RObject{IntSxp}
[1] 32 11

引数は暗黙的に評価され RObject に変換される。

ベクトルの和を求め RObject に変換する
julia> rcall(:sum, Float64[1.0, 4.0, 6.0])
RObject{RealSxp}
[1] 11

rcopy 関数はRObject を Julia のオブジェクトに変換する。
豊富な発見的手法に基づいて,もっとも適切な Julia の型に変換する。

julia> rcopy(R"c(1)")
1.0

julia> rcopy(R"c(1, 2)")
2-element Array{Float64,1}:
 1.0
 2.0

julia> rcopy(R"list(1, 'zz')")
2-element Array{Any,1}:
 1.0
 "zz"

julia> rcopy(R"list(a = 1, b= 'zz')")
OrderedCollections.OrderedDict{Symbol,Any} with 2 entries:
  :a => 1.0
  :b => "zz"

特定の型変換をするために,第 1 引数として型を指定することができる。

julia> rcopy(Array{Int}, R"c(1, 2)")
2-element Array{Int64,1}:
 1
 2

robject 関数はあらゆる Julia オブジェクトを RObject に変換する。

julia> robject(1)
RObject{IntSxp}
[1] 1

julia> robject(Dict(:a => 1, :b => 2))
RObject{VecSxp}
$a
[1] 1

$b
[1] 2

== @rlibrary マクロと @import マクロ

このマクロは,全てのエクスポートされた R パッケージ中の関数およびオブジェクトを現在のモジュールにロードする。

julia> @rlibrary boot
julia> city = rcopy(R"boot::city")  # get some data
10×2 DataFrame
│ Row  │ u      │ x      │
│      │ Float64│ Float64│
├──────┼────────┼────────┤
│ 1    │ 138.0  │ 143.0  │
│ 2    │ 93.0   │ 104.0  │
│ 3    │ 61.0   │ 69.0   │
│ 4    │ 179.0  │ 260.0  │
│ 5    │ 48.0   │ 75.0   │
│ 6    │ 37.0   │ 63.0   │
│ 7    │ 29.0   │ 50.0   │
│ 8    │ 23.0   │ 48.0   │
│ 9    │ 30.0   │ 111.0  │
│ 10   │ 2.0    │ 50.0   │

julia> ratio(d, w) = sum(d[!, :x] .* w)/sum(d[!, :u] .* w)
ratio (generic function with 1 method)

julia> b = boot(city, ratio, R = 100, stype = "w");
julia> rcall(:summary, b[:t])
RObject{StrSxp}
       V1       
 Min.   :1.274  
 1st Qu.:1.449  
 Median :1.543  
 Mean   :1.572  
 3rd Qu.:1.638  
 Max.   :2.178  

当然ではあるが,データは R と Julia の間で何回もコピーされるので,余りにも非効率である。
効率性の面からは,R"文字列" マクロを使うことをお勧めする。
いくつかの R 関数はドットを含むキーワード引数を持っている。
RCall はこれらのキーワードをエスケープするために,文字列マクロ「var"ドットを含む引き数名"」を提供する。

julia> @rimport base as rbase
julia> rbase.sum([1, 2, 3], var"rm.na" = true)
RObject{IntSxp}
[1] 7

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

MATLAB プログラムを Julia に書き換え

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

細かいところは最終的には頭を使った手作業になるが,エディタレベルでの作業についてまとめておく。

  • 文末が ; であるが,Julia では問題ないのでそのままにしておく。
  • if 文,while 文 の論理式は ( )  で囲まれているが,Julia では問題ないのでそのままにしておく。
  • 関数が function [戻り値リスト] = 関数名(入力引数)になっているが,"[戻り値リスト] =" を取り除く。
  • 注釈行は % で始まる % を # に全置換する。
  • プログラミングスタイルにもよるが,関数名と ( の間の空白は許されないので "関数名(" のようにする。
  • persistent 変数は必要ないので,persistent 文をコメントアウトする。
  • 文字列は ' ではなく" でくくるので,全置換する(' のままのものもあるので,確認しつつ置換する)。
  • 論理式の否定演算子が ~ なので,! に全置換する。
  • 出力文が fprintf() なので,@printf() に置換する。  "fprintf ( 1, "を "@printf(" に全置換する。先頭に "using Printf"を加えておく。println() に置換してもよいが format 付の場合には @printf() が妥当。
  • zeros(), ones() は,ベクトルであっても,二次元目が 1 になっているので,zeros(n, 1) のようなものを zeros(1),さらに型を考えて zeros(Int64, n), ones(Float64, n) のように直す。
  • ベクトル,配列は V(i),A(i, j) のようになっているので,V[i], A[i, j] にする。全置換と手作業の組み合わせ。
  • 継続行は ... で終わるが,これは単に ... を消去するだけでよい。
  • 論理値を 0/1 で表すので,false/true に変換。
  • 一様乱数の使用は [ r, seed ] = r8_uniform_01 ( seed ) のようになっているので,r = rand() のようにする。
  • 戻り値がちゃんと返されるように,必要なら return 文で指定する。
  • この辺りまで来たら,REPL で関数全体をコンパイルし,エラーメッセージに従ってプログラムを修正する。
  • 別の記事にも書いたが,Julia での for ループ,while ループに変数スコープがあるために,"変数名 not defined" に対応する。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村