講義資料(R入門)
ここではRおよびRstudioの初心者を対象にRstudioを使ってRプログラミングを行う上での基本操作(初級から中級)について以下の順に説明します。Google Chromeでこのページを開くとレイアウトが一番美しいと思われます。
0)そもそも統計解析したいだけなのになぜプログラミングが必要なのか?
1)Rstudio (desktop)の外見(GUI)の説明
2)スクリプトファイルにおける基本操作1(スクリプトの実行、簡単な計算・ライブラリの読み込み・データの読み込み・表示)
3)スクリプトファイルにおける基本操作2(データの加工、グラフの表示、データフレームとリスト)
4)スクリプトファイルにおける「プログラミング」(条件分岐・繰り返し、関数、メモリ開放)
ここでは以下の3つのファイルを使うのでダウンロードします。
0)なぜプログラミングが必要か?
エクセルやその他マウスのクリックで事足りる統計ソフトが世の中にはたくさんあるのに、なんぜわざわざ呪文みたいなコマンドを書き連ねてプログラミングしないといけないのでしょうか? 二つだけ大きな理由を挙げます。
[1] 必要性
観測や実験に再現性を担保するためにプロトコルを用意したり、数値シミュレーションにも方程式やプログラミングコードのように再現性を保障する枠組みがあるように、生データを変換したり統計処理したりするプロセスにも再現性が求められます。エクセルのデータをそのつど関数を使って変換したり、マウスで何回もクリックしないと結果が得られない統計処理はその作業プロセスの再現性が高いとはいえません。すべての作業プロセスをスクリプトとして保存することで、再現性を保障することができるのです。
[2] 利便性
最近の研究ではひとつのプロジェクトからも大量のデータが得られ、そのそれぞれに同じような処理を繰り返し行う必要がよくあります。このような処理をプログラミング無しで済ませようとすると、何十回も何百回もマウスをクリックしたりエクセルの一部をコピペしたり値の変換をしたり際限がありません。複雑で繰り返しの多い処理もプログラミングを用いれば全部一度に効率的に済ませることができます。新たなデータが出てきても、プログラミングへの入力をやり直せばいいだけです。
1)Rstudioの外見(GUI)の説明
Rstudioのウィンドウは複数のウィンドウから構成されます。ウィンドウの最も上部(Macの場合はディスプレイの最も上部)には全体の設定などを行うツールバーがあります[1]. [2]の画面では、Rのプログラミングコード(スクリプト)が書かれているファイルを編集してコードを追加・修正したり、読み込んだ数値データを確認したりするためのエディターです。[3]の画面はConsoleウィンドウであり、[2]に書き込まれたコードを実行した結果やエラーメッセージが表示されます。また、一度限りしか実行する必要のない簡単なコードはこのConsoleに直接書いて実行することもできます。[4]の画面にはRのプログラムを実行している最中に生成される変数・ベクトル・行列やデータ(=オブジェクトと言います)の一覧が表示され、その内容もダブルクリックにより[2]の画面に表示させて確認することが可能です。[5] の画面には良く使う複数の機能があります。今開いているフォルダ上のファイル一覧を確認したり(Files)、解析結果のグラフを表示したり(Plots),プログラミングで使うパッケージや関数のヘルプファイルを確認して使い方を調べることもできます(Help)。
RstudioでRスクリプトファイル(拡張子がRのファイル)を開くには、[1]のツールバーの左端から, File>Open File...を選ぶと、通常のフォルダ画面(左図)が表示されるので、開きたいファイル(ここではfor_intro_cer.R)を選びます。
スクリプトが開かれると[2]のエディター画面に表示されます。ファイルはタブ形式で開かれるので複数のファイルを並べて開くこともできます。何も設定を変えていない状態では、コードの背景は白、文字の色は黒や緑などの配色がされているはずです。
RstudioはR(やC)の言語規則を自動認識し、プログラミング過程を円滑にするために、文字の色などを自動的に変えて表示します。[1]のツールバーのTools>Global Options...を選ぶと、その後表示されるウィンドウ(Options)からこの設定を変えることができます。
Optionsの画面からは各種設定が可能ですが、プログラミングコードの外見を変更するには、Appearanceを選び、Editor themeの中からテーマを選ぶと下図のように表示を変えることができます。
もう1ステップ、新しいスクリプトファイルを開くたびにすべきことがあります。スクリプトからはコンピュータの中のファイルにアクセスしてデータの読み込みや書き出しが可能です。そのためには、コンピュータの中の現在位置をRに知らせて適切にファイルにアクセスするためには、[1]のツールバーにおいて、Session>Set Working Directory>To Source File Locationというのをマウスで選択する必要があります。これによりスクリプトファイル(ここでは"Source File"と書かれています)が置いてあるフォルダからの相対パスでファイルにアクセス可能となります。
2)スクリプトファイルにおける基本操作1
スクリプトの実行・簡単な計算
スクリプトの外見についてですが、左端には行番号が表示されています。#から始まる行は「コメント」と呼ばれ、スクリプト実行時にはRには認識されません。コメントはスクリプトを書いた人間向けのメッセージです。たとえ他人と共有するスクリプトでなくとも、こまめにコメントを残しましょう。コメントは、後日同じスクリプトを開くかもしれない、未来の自分のためのメッセージです。
4行目に書いてあるのは1+1という計算式です。これを実行するには...4行目にカーソルを合わせてハイライトにし(下図参照)、キーボードからWindowsの場合はコントロール・キー(Cntl)とエンター・キー(Enter)を同時に、Macの場合はコマンド・キーとエンター・キーを同時に押します。このとき、失敗してスクリプトをハイライトにしたまま、エンター・キーだけ押すとその部分が消滅します。
スクリプトの実行・簡単な計算(続き)
このキー操作によって、この部分だけ("1+1")だけがコンソール画面に自動的にコピーされ、その結果が"[1] 2" として表示されます(下図)。
もしも複数の行に書かれたスクリプトを上から順番に一度の動作で実行したい時は、たとえば、4行目と5行目を両方ともハイライトにし、実行のためのキーを押します。するとコンソールに順番に実行結果が表示されます(このときコメント行[3行目]をハイライトに含めても結果は変わりません)(下図参照)。
コンソール画面に表示される実行結果は上のような感じになります。以後、毎回Rのエディター画面やコンソール画面を画像キャプチャーで表示するのは煩雑ですので、上図の結果は下図のようにgoogle siteの機能を使って単純に表記する場合もありますが、R Studio上でのフォントや色との違いに注意します。
> #Simple calculation
> 1+1
[1] 2
> exp(-2.0)
[1] 0.1353353
>
ライブラリの読み込み
この講習会で使うさまざまな統計手法については"vegan"という生態学用のライブラリに組み込まれています。それらの統計手法を呼び出して使うためには、このライブラリを読み込む必要があります(インストールは「R環境の準備」のところで説明したとおり済んでいるとします)。読み込むにはスクリプトの以下の行を実行するだけです。
library(vegan)
データの読み込み
まずRに読み込ませたいファイルとして以下のファイルをダウンロードして、for_intro_cer.Rが保存されているのと同じフォルダに置いてください。
このcsv形式のファイルの内容をRに読み込ませるには以下のスクリプトを実行します。
data_test<-read.csv("./data_sample.csv", header=TRUE)
これは、read.csvという関数を使って,相対パスを使ってファイルを指定し("./data_sample.csv")、csvファイルの1行目をデータの一部ではなくヘッダとして読み込み(header=TRUE)、その内容を"data_test"という「ハコ」に読み込むという命令になります。Rにおいてデータなどを保存しておく「ハコ」はオブジェクト(object)と呼ばれます。
データの中身を確認:この「ハコ」の中身を見るには、Viewという関数を使って以下を実行すれば、Rエディター上で確認できます。さきほどheader=TRUEでcsvファイルを読み込んだため、csvファイルの一行目が、「列名」("data_time_sample", "place_sample", "measured_time")としてデータフレームに入っているのが見て取れます。
View(data_test)
data_testの中身を見るには、上のようにスクリプトを実行しなくても、Rstudioの[4]の画面に、生成されたオブジェクトの一覧が表示されているので、見たいオブジェクトをダブルクリックすれば、Rエディター上で確認できます。
データの削除:また、この画面の「ほうき」みたいなアイコンをクリックすれば、Rで現在生成されているオブジェクトを一発ですべて削除し、それらのオブジェクトに消費されていたメモリーを解放することができます。
オブジェクトを個別に削除するには、Rエディターに、以下のスクリプトを書いて実行します。
rm(data_test)
データ種別の確認:ちなみに、R上で使うことができる「ハコ」(オブジェクト)やその中に入れる値にはいろいろな種類があります。よく使うものの一つはデータフレーム(data.frame)とよばれ、csvファイルにまとめられているような多変量データを保存しておくために使います。オブジェクトをたくさん作っていくとそれぞれのオブジェクトのタイプ(クラス)かだんだん分からなくなってきます。オブジェクトのタイプを確認するには以下のスクリプトを実行します。
#load (again) the data set
data_test<-read.csv("./data_sample.csv", header=TRUE)
#Check the type (class) of the object
class(data_test)
3)スクリプトファイルにおける基本操作2
以下の内容は基本操作のごく一部です。ほとんどすべての情報は、以下のサイトとその内容をまとめた書籍に掲載されていますので自習することができます。
データの加工
データフレームdata_testに保存されているデータの一部(ここではplace_sampleという名前の列)には以下のようなスクリプトでアクセスできます。
$Check the part of the data set
data_test$place_sample
RstudioのRエディターでは、data_test$まで書き込むとそれ以降の候補を自動表示してくれるので便利です。
あるいは、以下のようにすると各行、各列、特定の行-列の値にそれぞれアクセスできます。
data_test[1, ]
data_test[, 1]
data_test[3,2]
次に、data_test$place_sampleの内容を別の「ハコ」(オブジェクト)にコピーしてちょっと加工してみます。sample_testという新たなオブジェクトにdata_test$place_sampleの値をコピーするには、"<-"という記号を使って以下のようにします。
#Copy the part of the data set into a new object
sample_test<-data_test$place_sample
#Check the data
sample_test
ただしこのままだと新しいオブジェクトはデータフレームではなくなってしまうので、as.data.frameという関数を間に入れて、以下のようにします。
#Copy the part of the data set into a new data.frame
sample_test<-as.data.frame(data_test$place_sample)
class(sample_test)
View(sample_test)
このようにデータフレームを作り直すと列名が自動的に付きます。
colnames(sample_test)
別の名前に変えるには、以下のようにします。
colnames(sample_test)<-"ID"
さて、元のデータフレームdata_testに戻ります。subset(データフレーム名、条件式)という形式を使うとデータの一部を抽出できます。ここで等号は”==”です。
subset(data_test, place_sample=="higashihori")
上のスクリプトではコンソール上に結果が表示されるだけなので、上で使った形式で新たなデータフレームに代入(<-)します。
data_test01<-subset(data_test, place_sample=="higashihori")
class(data_test01)
今回はas.data.frame()を使わなくてもデータフレームになっています(複数列を含むデータのため)。このへんがRのよく言えば柔軟性が高い、悪く言えば一貫性が無い点です。エラーが出たときなどはclass()関数でそのつどデータのタイプ(型)を確認することが大事です。同様にhonmachiという条件にあったデータをまた別のデータフレームに代入します。
data_test02<-subset(data_test, place_sample=="honmachi")
こうして作ったデータフレーム等の内容やサイズはRstudioの[4]の画面のEnvironmentのところに表示されています。たとえばdata_test01は3つのvariableからなる89個のobservationから構成されています。これは、89行3列のデータであると読み替えることもできます。ここで注意したいのは、Rでは伝統的に、一行一行が異なるサンプルからの「観測」結果で、それぞれの列に異なる「変数」の値を載せていくというスタイルになっているということです。エクセルやcsvファイルで生データを保存するとき、このスタイルを意識することが重要です。スクリプトからは以下のように行数と列数にそれぞれアクセスできます。ちなみに私はいつまでたっても行列の縦と横のどちらが列か覚えられません。覚え方はこちら。
#Check the size
#Row size = length of the column vector
length(data_test01[,1])
#Column size = length of the row vector
length(data_test01[1,])
さて、一度二つのデータフレームdata_test01, data_test02に分離させたデータですが、結合させることができます。行数と列数のどちらが同一かに注意しないといけませんが、今回は列数がいっしょなので、rbind()という関数を使って、行(row)方向(=縦方向)に結合することができます。
#data binding
data_test03<-rbind(data_test01, data_test02)
View(data_test03)
ためしに列(column)方向(=横方向)に結合しようとすると、cbind()という関数を使って
data_test04<-cbind(data_test01, data_test02)
行数(number of row)が一致しないため、コンソールに以下のようにエラーメッセージが表示されます。
> data_test04<-cbind(data_test01, data_test02)
Error in data.frame(..., check.names = FALSE) :
arguments imply differing number of rows: 89, 56
グラフの表示
グラフについてはいろいろグラフィックのオプションがありますので、http://cse.naro.affrc.go.jp/takezawa/r-tips/r/48.htmlで自習できます。ここではごく単純な例だけ説明します。
新しいサンプルデータをダウンロードしてRスプリプトと同じフォルダに保存します。そのcsvファイル読み込んで、plot()関数で横軸にとる値と縦軸にとる値を指定すれば、二次元の散布図が簡単に描けます。
#For sample plot
#load data
data_plot<-read.csv("./data_sample2.csv", header=TRUE)
#plot x vs y
plot(data_plot$x, data_plot$y)
#plot y vs x
plot(data_plot$y, data_plot$z, col=4,cex=2)
プロットの結果はRstudioの[5]の画面に表示されます(下図参照)。
データフレームとリスト
データフレームは行名や列名を持ったデータを保存するのに適したオブジェクトです。それに対してリストは、複数のオブジェクト(たとえば複数のデータフレーム、データフレームとベクトル等)を集めてひとつのオブジェクトとして扱える仕組みです。複数の「ハコ」を集めたオブジェクトです。空のリストは、list()によって生成することができ、それぞれの「ハコ」には二重括弧[[ ]]の中に数字を入れた形でアクセス(オブジェクトのコピー、呼び出し)ができます。
#data.frame and list
#generate an empty list
test_list<-list()
#check object type
class(test_list)
class(data_test01)
class(data_test02)
#assign object
test_list[[1]]<-data_test01 #assign data.frame
test_list[[2]]<-data_test02 #assign data.frame
test_list[[3]]<-c(1,0,2) #assign numerical values
リストの中身はすべて一度に見ることもできるし、その一部を指定して見ることもできます。
#check all objects in the list
test_list
#check the specific item in the list
test_list[[1]]
リストの各項目は、代入されたオブジェクトとして振舞うので、たとえば、test_list[[1]]はデータフレームとして振る舞い、その中身の一部にアクセスするのに、次のようなスクリプトも使えます。
#acting as data.frame
test_list[[1]]$place_sample[3]
リストの要素がそれぞれどんなクラスに属しているかは以下のスクリプトを順に実行すると確かめることができます。
#check the class
class(test_list[[1]])
class(test_list[[1]]$place_sample)
class(test_list[[3]])
スクリプトファイルにおける「プログラミング」
この講習でエコプレートから得られた値を自動変換したり統計処理したりするために最小限の知識として、条件分岐・繰り返し(ループ)・関数の基礎を説明します。使いこなすには慣れが必要ですし、さまざまなオプションについては上で挙げている参考サイトや書籍で自習することをお勧めします。
条件分岐(if/else)
以下のものが簡単なスクリプトです。ちなみにcat()は文字列等を表示するための関数です。
#if statement
a <- 3
if(a > 3) { #check the condition
cat("a is greated than 3", "\n") #this is excuted when the condition is true
} else{
cat("a is less than or equal to 3", "\n") #this ix excuted when the condition is not true
}
このとき、細かいことですがelseの前で改行するとエラーが出ます。しかし、改行したほうが見た目がよいので、if文の開始と終了地点を括弧をつかって明示すればよいです。
{
if(a > 3) { #check the condition
cat("a is greater than 3", "\n") #this is excuted when the condition is true
}
else{
cat("a is less than or equal to 3", "\n") #this ix excuted when the condition is not true
}
}
一般にプログラムが複雑になってくると、入れ子上の構造がたくさん出てきますので、各部分の開始部分と終了部分に気をつける必要があります。
繰り返し・ループ(for loop)
以下がfor loopを使ったスクリプトです。1から10までの値を持つベクトルxをつくって、ループの始まりと終わりを指定します(start, end)。sum<-sum+x[i]によってsumという変数に順次x[1]からx[10]まで加算されていきます。ここでループ部分はfor(i in 1:10)のように直接数字でループの始まりと終わりを指定することも可能ですが、プログラミング一般の作法として可読性を高めるためには、条件分岐や繰り返しなどの制御構造の中に突然数字を代入することは避けたほうがよいです。
#loop structure
x<-c(1,2,3,4,5,6,7,8,9,10) #prepare a vector
x[3]
start<-1 #start of loop
end<-10 #end of loop
sum<-0 #prepare the variable to calculate the sum of the vector,initialized as zero
for(i in start:end) {
sum<-sum+x[i] #add each element of vector x
}
sum
ちなみにR言語にはC言語系にあるような, incremental assignment (+= )のような演算子は用意されていないので、sum+=x[i]のようにはできません。
関数(function)
さて、上で扱った条件分岐と繰り返しの例を使って関数を作ってみます。関数を作るには以下のような構造にします。
関数名 <- function(パラメータリスト) {
関数の定義
}
具体的には上で扱ったif/elseの計算を関数として定義すると、以下のようになります。関数の定義の部分は上で扱ったスクリプトとほぼ同じです。check_3が関数の名前、inputが関数のパラメータで、input=2によってデフォルトのパラメータ値を指定できます。指定しなくてもかまいません。
#function example 01
check_3<-function(input=2){
if(input > 3) { #check the condition
cat("Your input is greater than 3", "\n") #this is excuted when the condition is true
}
else{
cat("Your input is less than or equal to 3", "\n") #this ix excuted when the condition is not true
}
}
この関数を呼び出して実行するには、以下の感じです。パラメータの値を2、4にする場合の例です。
#exclute the function
check_3(input=2)
check_3(4)
パラメータの値に何も指定せずに関数を呼び出すとデフォルトの値が入力されて結果が出ます。
check_3()
パラメータは複数個指定できるので、上で扱った繰り返し計算の例を関数にすると、以下のようにできます。今回パラメータは、vec, start, endの3つです。vecは要素を加算したいベクトルに対応し、start, endはそれぞれ繰り返しの開始と終了ポイントです。
#function example 02
sum_vector<-function(vec, start, end) {
sum2<-0
for(i in start:end) {
sum2<-sum2+vec[i] #add each element of vector x
}
print(sum2)
}
さきほど作ったベクトルxを使って要素1から5まで加算したければ以下のように関数を呼び出して実行します。
sum_vector(x, 1,5)
メモリ開放
数多くのベクトル、データフレーム、リスト等を使った複雑なスクリプトを実行していくとメモリがどんどん消費されていきます。空いているメモリを開放するには以下のスクリプトを二回続けて実行するとよいでしょう。
gc(T,T)
(以下は追加情報です)
関数における代入について注意(<-, <<-)
さて、最後になりますが関数まで使えるようになると、気をつけないと大きな計算ミスにつながる重要なルールがあります。まず、先ほど作ったsum_vector関数を以下のように実行するとともに関数内で定義された変数sum2の値を確認してみましょう。
sum_vector(x,1,10)
sum2
このとき、コンソール上では以下のようにエラーメッセージが出るはずです。
> sum_vector(x,1,10)
[1] 55
> sum2
Error: object 'sum2' not found
>
sum2が見つからないというわけです。なぜなら、sum2は関数内で定義されているので関数内でのみ有効で関数の外からはアクセスできないのです。
では、以下のようにsum2を関数を呼び出す前(もしくは定義する前)に定義し値を代入してから関数を実行してみるとどうでしょうか?
sum2<-3
sum_vector(x,1,10)
sum2
こんどはsum2は存在しますが、関数を呼び出したあとでも,値に変化がありません。この方法では、関数内のsum2と関数外のsum2は別物として扱われます。別の言い方をすると
関数内部から関数外部で定義された変数の値を変更することは不可能 (上の例、sum2)
ただし、
関数内部から関数外部で定義された変数の値を読み込むことは可能
下の関数sum_vector2内で、関数外で定義されたend2という変数を使っています。
#function example 03
end2<-3
sum_vector2<-function(vec, start) {
sum3<-0
for(i in start:end2) { #using the value of end2
sum3<-sum3+vec[i] #add each element of vector x
}
print(sum3)
}
sum_vector2(x,2)
ここで注意したいのは、上のようなルールがあるにしても、可読性の高いスクリプトを書くには、1)関数内と関数外で同じ変数名はできるだけ使わない、2)関数内部から関数外部の変数値にアクセスするときは、sum_vector2のようにするのではなく、関数外部の変数値を関数のパラメータに代入する形で行うことが大事です。2)については以下のようにすることが奨励されます。
#use parameters
st<-1
ende<-10
sum_vector(x,st,ende)
関数の実行結果を別の変数で使いたければどうすればよいか? 二つの解決策がありますが、一つめは簡単で、関数の実行結果を別の変数に代入すればよいだけです。
#assign the value of the function into another variable
summ<-sum_vector(x,st,ende)
summ
二つめの解決策は、別の代入演算子(<<-)を使って関数外部の変数値を変える方法です。以下のようにできます。これも必要な場面が出てくるかもしれません。
#function example 04
sum3<-3
sum_vector3<-function(vec, start, end) {
sum3<<-0
for(i in start:end) {
sum3<<-sum3+vec[i] #add each element of vector x
}
print(sum3)
}
sum_vector3(x, 1,10)
sum3