Rやstanをもっと使いこなすために,プログラミング技術の基礎について解説します。 プログラミングの技術は,言い換えるとアルゴリズムという考え方でもあります。アルゴリズムとは計算手続きのことであり,個別の算術をできるだけ一般化して表現することを考える必要があります。
計算はパソコンのCPUがするわけですが,CPUにわかる言葉で伝えて,人間にわかる言葉で結果を返してもらう必要があります(さもなくば0/1の数字がつらつらと流れてくるだけです)。この言葉=言語として,例えばFortran/Cobol/Pascal/BASIC/C/C++/Java/Rなどと呼ばれるものがあります。言語はそれぞれ違っていても,基本的な要素はそれほど多くありません。ここでは言語の基本的な要素として,
の三つの処理を,そしてコンピュータ計算に必要な考え方として
の五つのポイントに絞って解説します。
様々なプログラミング言語が有りますが,処理の基本は代入と反復と分岐の三つです。 ## 代入 代入はRでもごく初歩的な段階で使うと思いますが,これも言語の基本です。
a <- 1 # aに1を代入
b <- 2 # bに2を代入
a + b # 代入したもの同士で演算する
## [1] 3
a - b
## [1] -1
a <- 3 # aの値を上書き
a + b
## [1] 5
b <- b + 1 #上書きの仕方が特徴的
b
## [1] 3
分岐,あるいは条件分岐というのは,状態を評価して違う処理をすることです。代表的なものはif文です。
x <- 10
if(x>5){
print("5より大きい")
}else{
print("5より小さい")
}
## [1] "5より大きい"
一行でまとめて書くifelse文というのもありますね。
x <- 3
ifelse(x>5,"5より大きい","5より小さい")
## [1] "5より小さい"
条件によって実行されるコードは中かっこで括れば複数行書くことができますから,条件をネストさせていくこともできます。
x <-6
if(x > 5){
if(x > 7){
print("7よりも大きい")
}else{
print("7よりは小さいけど5よりは大きい")
}
}else{
print("5より小さい")
}
## [1] "7よりは小さいけど5よりは大きい"
書き方のコツとして,中かっこの対応がわかりやすくなるようにタブで位置を整えるといいでしょう。 ## 反復 コンピュータの計算は反復がお得意です。プログラムによって指示した通りに計算をし続け,疲れるということがありません。 反復させる方法はいろいろありますが,代表的な二つを紹介します。 ### for文 forをつかって繰り返す時は,繰り返す記号,開始・終了,を指定してやります。
for( i in 1:5 ){
print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
繰り返す記号を演算に組み込むこともできます。コードの意味をよく考えてみてね。
x <- 0 # 値を初期化しています
y <- 0
for(i in 1:10){
x <- x + i
y <- y + (i * 2)
}
x
## [1] 55
y
## [1] 110
開始・終了点の指示の仕方を工夫してみましょう。
x <- 0
y <- 0
for(i in c(1,3,5,7,9)){
x <- x + i
y <- y + (i * 2)
}
x
## [1] 25
y
## [1] 50
中かっこでくくった中を反復するのですが,一行しかない場合は中かっこを省略することができます。
x <- 0
for(i in -1:-5)
x <- x + i # ここが反復される中身。次の行はもう違う
x
## [1] -15
ネストすることもできます。実行する前に計算の中身を考えてみてね。
x <- 0
for(i in 1:5){
for(j in 1:5){
x <- x + (i*j)
}
}
x
## [1] 225
注意して欲しいのは,ネストする時の記号です。代入のところで説明したように,記号の値は上書きされていきますので,指定を間違えると無限ループに陥ることがあります。 ### while文 繰り返す回数がわかっていない時に使うのがwhile文です。whileの中身には条件文を記述します。
x <- 0
while(x < 10){
x <- x +1
print(x)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
最初から条件がFALSEであれば,始まりません。
x <- 0
while(x>10){
x <- x -1
print("一つ減らす")
}
複数の条件をAND/ORで繋げることもできます。
x <- 0
y <- 0
while(x < 5 && y < 10){
x <- x + 1
y <- x + y
print(paste(x,"と",y))
}
## [1] "1 と 1"
## [1] "2 と 3"
## [1] "3 と 6"
## [1] "4 と 10"
いわゆる「フラグを立てる」というのはここから来ています。
FLG <- TRUE
while(FLG){
x <- runif(1,min=0,max=1)
if(x > 0.3){
print(x)
}else{
print(x)
print("でたぁ")
FLG <- FALSE
}
}
## [1] 0.3278647
## [1] 0.870935
## [1] 0.6614102
## [1] 0.8958357
## [1] 0.9529923
## [1] 0.9456343
## [1] 0.3150261
## [1] 0.8771979
## [1] 0.8257172
## [1] 0.1041868
## [1] "でたぁ"
型,というのはオブジェクトの状態です。 文字が入っている変数なのか,整数が入っている変数なのか,実数が入っている変数なのか,虚数が入っている変数なのか。 Rでは特に宣言しなくても,自動的に適切な型(基本は実数型)に読み替えてくれますが,多くのプログラム言語(例えばCやjava)では事前に用意する変数がどういう型なのかを宣言します。この宣言がなければ受け入れてくれない言語も少なくありません。
Rの型は
の5種類あります。
x <- c(1:5)
mode(x)
## [1] "numeric"
型を変えることを型変換,あるいはキャストする,と言います。
x <- as.complex(x)
配列は言い換えるとデータの構造です。Rでは
が利用できます。 ベクトルが二次元配列になったものが行列,三次元以上は配列です。異なるデータをひとまとめにしてあるのがリスト型,リスト型が矩形になっているのがデータフレームです。因子型,順序付き因子型はデータの型に分類できるのかもしれません。
よくある例ですが,サイコロを振って丁半を判別してみましょう。
x <- runif(1,0,1) #0-1の乱数を一つ発生させる
x <- x * 6 +1
x <- as.integer(x)
x
## [1] 4
if(x %% 2){print("半")}else{print("丁")} # %%はあまりを算出する記号
## [1] "丁"
行列の掛け算をやってみましょう。ちなみに,ここでは反復の練習と考えてわざわざ書いてください。
x <- matrix(1:6,ncol=2)
y <- matrix(5:8,ncol=2)
x
## [,1] [,2]
## [1,] 1 4
## [2,] 2 5
## [3,] 3 6
y
## [,1] [,2]
## [1,] 5 7
## [2,] 6 8
z <- matrix(ncol=2,nrow=3)
for(i in 1:3){
for(j in 1:2){
z[i,j] <- x[i,1]*y[1,j]+x[i,2]*y[2,j]
}
}
z
## [,1] [,2]
## [1,] 29 39
## [2,] 40 54
## [3,] 51 69
x %*% y #検算
## [,1] [,2]
## [1,] 29 39
## [2,] 40 54
## [3,] 51 69
ちなみに,このままでは行列のサイズが変わると対応できなくなります。そこで,
関数は,一般化された手続きをいつでも呼び出して使えるようにするものです。
自分で作った手続き(=アルゴリズム)をひとまとめにして,ある作業をさせたり,その結果を返してもらったりします。必要な手続きをまとめて関数にすることを,カプセル化するということもあります。
関数には,ある数字・変数を与えて計算してもらいますが,与えるもののことを引数といいます。 関数を作るときは,function関数で作りますが,そのときに与える引数の名前を設定すると,その関数の中ではその引数を使った処理をします。変数の中で作った数字をreturn関数に入れると返すことができます。
tasu <- function(x,y){ #x と yという引数を与えてもらう
ret <- x + y # 足したものをretという変数に入れる
return(ret) # retをreturnで返す
}
tasu(2,3)
tasu(2,3,4) #渡す引数の数を間違えるとエラー
tasu(x=9,y=13) #引数を渡すときに指定してやっても良い
tasu(x=9,z=12) #使われていない引数を書くとエラー
x # 関数の中で使った変数は,関数の外では使えない
幾つかのTipsをここで。
enzan <- function(x=1,y=1,inv=FALSE){ #引数に特に指定がなければ,x=1,y=1として扱う
if(y==0){
stop("yを0にすることはできません") #関数を抜けだす
}
tasu <- x + y
hiku <- x - y
kake <- x * y
waru <- x / y
SQ <- sqrt(x) # 関数のネストも当然可能
return(list(plus=tasu,minus=hiku,prod=kake,dev=waru)) #リスト型で複数返す
}
enzan(1,2)
enzan()
enzan(3,0)
result <- enzan(12,4) #オブジェクトに返して
result$minus #要素だけ取り出すことができる
以上が関数作りの基本です。これができれば,あとは組み合わせで様々なことができると思います。また,今まで使ってきたR関数がどういう振る舞いをしているのか,どういうエラーが出ているのかについて,理解が進むということがあるのではないでしょうか。
関数を複数集めたもののことをパッケージと言います。 RにはCRANという共有ネットワークに,様々なパッケージがアップロードされています。パッケージは似たような用途に使うときの関数群なのです。
パッケージには,サンプルデータやマニュアルが含まれています。CRANにアップロードする都いうところまではいかなくても,自分で作った関数群やよく使うデータセットをまとめてパッケージ化しておくと,それを読み込むだけで使えるようになりますから,便利なこともあります。
パッケージは基本的にデータセット,関数,マニュアルなどの複数のファイルをまとめたもの。最初は空っぽの雛形=スケルトンを作るところから。 実際に作成する時は,package.skelton関数のlistにオブジェクトを幾つか引き渡します。
package.skeleton(name="SamplePKG") #SamplePKGという雛形ができる。何も渡してないので警告が多い
package.skeleton(name="ENZAN",list=c("enzan","tasu","x")) #演算関数,足す関数,データxを渡す
このコードを実行するとパッケージ名のフォルダが作られ,その中にdata,man,Rという下位のフォルダができていると思います。dataの中にはデータを(.Rdataの形式で)入れます。manの中にはマニュアルを入れます。help関数によって呼び出されるもので,関数一つにつき一つのファイルを用意します。これも今のワークスペースにあるファイルを基にした雛形ができているので,Rstudio上でみながら編集できるでしょう。中身はTeXに似たマークアップ言語です。 Rの中にはRのプログラムコード本体が入っています。 他に作られたDESCRIPTIONはパッケージの説明を,NAMESPACEには依存関係にあるパッケージなどを記載します(package.skeltonで作った場合は特に編集する必要はありません)。
そしてこれらのファイル,フォルダの準備ができれば,コマンドライン上での作業になります。 R CMD check hogeでパッケージの準備が十分できているかどうかのチェックができます。ここでエラーがなければ,R CMD build hogeでまとめた圧縮ファイルとしてパッケージの完成です。 ## Rstudioの力を借りる こうしたスケルトンの作成やマニュアル作りなど,最後はコマンドラインを使わないといけないのかー,とがっかりされた人もいるかもしれません。 が,大丈夫,最近はRStudioの力を借りれば,パッケージ作成プロジェクトとしてRstudio環境内でこれらの作業を進めることができます。 プロジェクトを置いておくところにGitを指定し,オンライン上で後悔しながら開発こともできます(version control)。
Enjoy!
参考サイト