Rを使ったプログラミング演習 1 :プログラミングの基礎 †
Rというソフトウェアを使いはじめて、すでに数回の講義が終了した。コマンドを入力するのはちょと難しいけど、エクセルみたいにマウスを使って面倒な操作をしなくても、一瞬にして統計解析をやってくれるし、グラフも簡単に描けてしまう優れもの。これなら、レポートや卒論のために、何度も解析をやりなおして、グラフを描き直すという作業も面倒ではないだろう。 受講生の皆さんは、だんだんとRの操作には慣れてきているし、興味も感じているよう。そこで、今回はRを使ったプログラミングに挑戦する。 アナウンス: 生物学セミナー・進路研究のメール †担当の朝川先生からのアナウンスです。進路研究で、先輩への質問に関するメールをチューターに提出することになっていたはずです。未提出者は、大至急、提出すること。未提出の場合、明日の授業でペナルティが科される可能性があります。 トピック: SNS利用の注意 †Facebookは、特に海外では非常によく使われている。研究者の世界で積極的に活用している人も多い。しかし、利用にあたっては、けっこう注意が必要だ。一昨年度、大学院生物学コースの学生向けに、次のような通知があった。
仲間内で使っているFaceBookで、仲間内の悪ふざけで口に出す汚い言葉を書き込んでしまったのことから始まった騒動だ。書き込みをした人は、軽い気持ちで、仲の良い友達同士のおふざけとしてやってしまったようだが、FaceBookやMixi、Twitter等、インターネットメディアへの書き込みは、日常会話の延長では無いことを十分に認識しておかなければならない。これらの書き込みは、ネット上の不特定多数に対して、意見を公開することとになるからだ(事実、上の事例でも、非道い書き込みをされた人は、そんな書き込みを家族や共同研究者に見せることで、不快感を抱かせてしまうことを心配していた)。
第10回授業の獲得目標: †
演習:1. プログラミング的思考練習 †4X4のマス目の中に、何個かの黒石がランダムに置かれているとする。このマス目から黒石を全て取り去りたいのだが、自分で全てをやる時間が無いので、ロボットに命令して、石を取り除かせることにした。このロボットは次の限定された動きを英文字1文字の命令で実行する(これ以外の動きはできない) 前に1つ進む F 右に向きを変える R 左に向きを変える L その場で停止する S 進んだ先に石があった場合は自動的に取り除く(命令不要) 数字と組み合わせることで、それぞれの動きが何回起こるかを決められる。~ 例えば、 2F2R2F という命令を与えると 進行方向に2つ進んで、回れ右して、2つ戻ってくる また、自分のいる場所がマス目の端(つまり前に進めない)かどうかを判断できる IF(端){命令1} ELSE {命令2} 例: IF(端){1R1F1R} ELSE {1F} 説明: 自分の場所が端なら一つ右に移動して方向転換、端でなかったら1つ進む 動きを何回繰り返すかを 数字{ 命令 } で指定できる。 例: 3{2F2R2F} 説明: 2つ進んで2つ戻ってくる動作を3回繰り返す 繰り返しの回数の指定に変数(x, y, z...) を使える for(x in 1:10) { xF2R1F } ^^^^^^^^^ この部分で、xが1から10まで、毎回1つずつ増えながら繰り返される ことを示している。なので、 1回目は、 1F2R1F 2回目は、 2F2R2F ..... 10回目は 10F2R2F ロボットは最初、左下の角で上向きに置かれている場合、上の条件で、4x4のマス目の中にある黒石を全て取り去るには、ロボットにどのような命令を与えれば良いか? 発展演習: Scratchを使ったプログラミング演習 †Scratchはプログラミング教育のために、MITで開発されたプログラミング環境だ。 君は、ネコを踊らせることができるか!? プログラミングって何? †プログラミングっていったい何をすることだろうか?IT用語辞典で調べてみると、、、
と書かれている。 つまり、プログラミングとは、簡単に言うと、「言葉を使ってコンピュータが理解できる命令を作ること」。 そこでこの授業では、Rを使ってプログラミングの練習を行う。プログラミングというと何かとても難しいことをやるように思うかもしれないが、「コンピュータにやらせる内容を、順序よく、コンピュータに分かる言葉でやさしく教えてあげる」というものだ。このクラスでもBasicやC言語を使ってプログラミングを経験している。 コンピュータにどんな命令ができるのか? or どんな命令がしたいか? † 「Wordを立ち上げて文章を作成し、印刷する」とか、「Excelで家計簿を管理する」とかは、市販のソフトウェアを使った作業。いずれも、非常に高度で複雑な命令が、使用者からコンピュータに向かって発せられている。例えば、 でも、本当に市販のソフトウェアを使うだけでいいのだろうか?良くないという点が、すぐに2つ思く: 1. 自分の目的にあったソフトウェアがいつも存在するというわけではない 2. ソフトが存在する場合、目的の数だけ、ソフトの使い方を覚えなければならない 特に1番目は致命的。生物学の研究で、何かの処理をしたいと考えたとき、誰かがソフトウェアを作ってくれていないと目的が達成できないということになってしまう。例えば、次の授業で話に出てくる「遺伝的浮動」の解析ソフトなんていうのは、パソコンショップに行っても売っていない。 そこで、この授業で次の3つを目的としてプログラミングを勉強する。
実を言うと、生物学の研究者でも、プログラミングなどを経験したことの無い人は大勢いる。今や、DNAシーケンスの決定や整列、系統樹作成にだって、専用のソフトウェアがある。。。しかし、そういうソフトウェアも、多くの場合は研究者自身が作成したもの(学生が作ったものもたくさんある)。この授業では、頭の柔らかい1年生のうちにプログラミングについての動機付けを行うことで、このクラスの中から将来は新しいソフトウェアを開発できる人が出ることも期待している。。 初めて(?)のプログラミング:"Hello World!" †「Hello World!」だなんて、なんか変なタイトル。誰が使い始めたのかは知らないが、多くのプログラミング言語の教科書で、最初に出てくるのがこのプログラム(プログラムを勉強したことのある人なら、誰でも知っている)。では、早速やってみよう。Rを立ち上げて、次のように入力。 print("Hello World!") うまくご挨拶できただろうか? ここでやったのは、print()という関数<注:命令とほとんど同じ意味>を使って"Hello World!"と画面に表示させただけだが、これも 画面にHello Worldと表示させる ということを目的とした立派なプログラムだ。 「えっ?それなら、Rの最初の授業でやったオブジェクトの内容を画面に出すのもプログラミング?」と聞かれるかもしれませんが、その通り。次の囲みの中を全てコピーして、Rのコンソールにペーストしてみよう。 x=c(1,2,3,4,5,6,7,8,9,10) sum(x) 上でやったプログラミングは、 画面に1から10までの整数の合計を表示させる というもの。先週やったRを使った操作も、何らかの処理の結果を画面に表示させるプログラミングだったわけだ。なんとなく、プログラミングが身近になっただろうか? 複数行の命令文 †上でやった2つのプログラミングは、見比べてみると違いがある。"Hello World!"の方は1行だけの命令だが、sumの方は2行になっている。プログラムは通常、いろんな処理を組み合わせて作るので、複数行になることが多い。 q=c(10,15,20,25,30,35,40,45,50,55,60,65,70,80,100) y=c(2,3,4,5,6,7,8,9,10,11,10,11,9,10,12) plot(log="xy",log10(q^2), log10(y)) 複数行の命令文:スクリプトエディタの利用 †上の複数行の命令をRのコンソールにペーストすると、全てが一度に実行されてしまう。ところどころに修正を加えたいとき、それでは不便だ。そこで、スクリプトエディタを利用する(プログラムのことをスクリプトとも言いう)。 Rのファイルメニュー > 「新しいスクリプト」 を選ぶと、「スクリプトエディター」というエディターが開く。スクリプトエディターでスクリプトを書いてから、 マウスで選択して右クリック すると、選択中のスクリプト(命令、コマンドとかコードとも呼ばれる)が実行できる。 さて、スクリプトエディタを開いて、上の3行の命令文をペーストしてみると、 q=c(10,15,20,25,30,35,40,45,50,55,60,65,70,80,100) y=c(2,3,4,5,6,7,8,9,10,11,10,11,9,10,12) plot(log="xy",log10(q^2), log10(y)) このプログラムは、 2つの数値ベクトルについて計算を行って、両対数グラフを描く というものであることがわかる。使ったデータは、何年か前の生物学科1年生がコドラート調査でとったもので、面積が広くなると種数がどう増えるかを示したものだ。Rを使えば対数グラフ用紙を使って手作業で面倒なプロットをする必要もなくなる。 プログラミングの基礎: 繰り返しと代入と条件分岐 †上の例で、紹介したプログラムは、個々の命令を順番に並べて、一度にペーストしただけだった。こんなことなら、Rに命令を1行ずつ与えるのと、ぜんぜん変わらない。そこで、もっとプログラムらしい命令をRに与えてみよう。それは、繰り返しと代入と条件分岐というもので、プログラミングの基本中の基本。 繰り返し †人が不得意でコンピュータが得意なことの一つは、単純な繰り返し作業を際限なく(文句も言わずに)行うことだろう。つまり、プログラミングの一つの目的は、面倒な繰り返し計算をコンピュータにやらせることだと言っても言い過ぎではない。では、何回繰り返すかという命令をどうやってコンピュータに与えるかというと、 for という命令を使う。
なお、1:10 は 1 2 3 4 5 6 7 8 9 10 という数字の集まりを示す。次の囲みの中を1行ずつRに実行させてみよう。 1:10 x=c(1:10) x 練習問題: 2から18までの偶数を全て表示させるプログラムをfor命令を使って作りなさい 解答例:(穴埋め形式) for(i in _:_){ # _ のところには数字が入る print(i*_) } 代入 †それでは、次の演習をやってみよう。
上の for(i in 1:10) { print(i) } というプログラムでは、iの値は繰り返しの度に違う値になる。 1回目 iの値は1 2回目 iの値は2 3回目 iの値は3 .... ........ 10回目 iの値は10 では、一つ前の回のiの値を使いたい場合はどうすれば良いだろうか?例えば、1から10までの合計をfor命令を使って計算することを考えると、 現在のiの値に、1つ前のiの値の合計を加えて表示させたい 1回目 iの値は1 1つ前までの合計は0 1 を表示 2回目 iの値は2 1つ前までの合計は1 3 を表示 3回目 iの値は3 1つ前までの合計は3 6 を表示 .... ........ 10回目 iの値は10 1つ前までの合計は45 55 を表示 こういう場合に、代入という命令を使う。代入は、前回すでにオブジェクトへの数値やベクトルの代入で学んだように、 = を使う。我々が通常行う計算では、'='は「3+5=8」というように、「左辺の計算結果が右辺に等しくなる」というる意味で用いられるが、プログラミングにおいては、 左辺の変数に右辺の計算結果を代入する という意味で用いられる。上の合計の計算プログラムは次のようになる。 goukei=0 #goukeiという変数に初期値0を代入 for(i in 1:10) { #以下を10回繰り返す goukei=goukei+i #goukeiに前回までのgoukeiの値にiの値を足したもの代入 print(goukei) #goukeiの内容を画面に表示 } では、上の囲みの中をRに実行させてみよう。 上の命令の中には2箇所、代入が使われている。最初の行では、goukeiという変数に0を代入するという単純なもので、goukeiの初期値を決めている。 次の、goukei=goukei+iという命令文では、右辺の「goukei+i」を先に計算して左辺の「goukei」に代入している。 今の goukei の値に 1 を加えたものを、goukei に代入する(これまでのgoukeiは上書きされる) という意味だ。 練習問題: 1から10までの数字を1つずつ順々にかけ合わせたた結果を全て表示させるプログラムを作りなさい (1x1=1, 1x2=2, 2x3=6, 6x4=24, 24x5=...... と計算してゆくということ) 以下、穴埋め形式でヒントを書いておく。_の部分(1文字とは限らない)を補ってプログラムを完成しなさい。 seki=_ #sekiという変数に初期値1を代入(かけ算だから) for(i in 1:10) { #以下を10回繰り返す _=_*_ #sekiに前回までのsekiの値にiの値を掛けたもの代入 print(seki) #sekiの内容を画面に表示 } うまくできただろうか? 縦に長くプリントされるのは格好悪いので、毎回の計算結果をオブジェクトにベクトルとして代入してから表示させてみよう。 seki=1 #sekiという変数に初期値1を代入 kekka=c() #kekkaというオブジェクトに空ベクトルを初期値として代入 for(i in 1:10) { #以下を10回繰り返す seki=seki*i #sekiに前回までのsekiの値にiの値を掛けたもの代入 kekka=c(kekka,seki) #kekkaというベクトルにsekiの値を要素として追加 } print(kekka) #kekkaの内容を画面に表示 ここでは、計算結果を順々に保存しておく入れ物を、kekkaという名前で作成し、初期値に空ベクトル(データは無いが、ベクトル)を作っている。ベクトルの作成は、前回の授業でやったように c() という関数を使う。 また、ベクトルに要素を追加するにも、同じ関数 c() を使っている。 では、上の囲みの中をRに実行させてみよう。
matrix()という関数はベクトルを行列に変換する働きがある。例えば、 x=c(1, 2, 3, 4, 5, 6, 7, 8, 9) というベクトルを、3 x 3の行列に変換したいときは、 matrix(x, nrow=3, ncol=3) #nrow= は行の数、 ncol= は列の数を指定 matrix(x, 3, 3) #nrow=, ncol=を省略して、こういう書き方もできる #演習:九九の表をmatrixという関数を使って表示さなさい。_を埋めなさい result=c() #resultに空ベクトルを初期値として代入 for(i in 1:9) { #iの値を1から9まで1ずつ変化させる for(j in 1:9){ #jの値を1から9まで1ずつ変化させる result=c(_, _*_) #iとjを掛けたものをresultの要素として追加 } } print(result) print(matrix(result, nrow=i, ncol=j)) #iとjはそれぞれ9で終わっているので、9x9のマスになる
条件分岐 †プログラミングの基本技の最後は条件分岐。if()を使って表現する。 i=4 #iに4を代入 if(i==3){ #iの値が3ならば print("三") # 三 と表示する } else { #iの値が3では無ければ print("三以外") # 三以外と表示する } if命令の()の中の式を条件式といい、iの値を評価している。評価に使われるのは比較演算子というもので、 == 等しければ != 等しく無ければ >, >= 左辺が右辺より大きいなら、左辺が右辺以上ならば <, <= 左辺が右辺より小さいなら、左辺が右辺以下ならば ということを意味している
''比較演算子''である''=='' に対して、代入の時に使った ''='' は''代入演算子''といいう。 上のif命令では、次のような条件分岐が行われている。 if(i==3){ print("三") # ・もしiの値が3ならば、画面に三を表示。 } else { #もしiの値が3では無ければ print("三以外") # 三以外と表示する }
繰り返しと条件分岐 †では、上の繰り返しと条件分岐を組み合わせて使ってみよう 練習問題:for命令を使って1から10までの数字を表示させなさい、5だけは"five"と英単語で表示させなさい
ユーザー定義関数 †「ユーザー定義関数」なんていうと難しそうだが、ようするに、先ほどまでに作ったプログラムに名前をつけて、いろいろと数値を変えて解析できるようにしようというものだ。こういうときに関数を定義する関数、function()を使う。 例えば、円の面積を計算する関数を作るなら、 menseki=function(r){r*r*pi} とすれば良い。これで自分独自の関数menseki()ができた。 では、実行してみよう。 menseki(10) #好きな数字を入れて円の面積を計算 演習 1: 1から入力した数値までの全てを横一列に表示させるプログラムを作りdisplayという名前の関数として定義しなさい display=____(a){ #関数定義の始まり kekka=c() #kekkaに空ベクトルを代入して初期化 for(i in 1:a){ #a回(iの値を1からaまで変化させる間)繰り返し kekka=c(kekka,i) #kekkaというベクトルにiを要素として代入 } print(kekka) #kekkaの内容を表示 } #関数定義の終わり ↑を使って何回も実行してみると、結果がいろいろ変わるのがわかる。例えば10を入れて実行するには、 display(10) 演習2: 上の関数の定義方法に従って、入力した数までの合計値を計算するsumupという名前の関数を作成する。下の囲みの中の_の部分(1文字に対応するとは限らない)を埋めて、プログラムを完成しなさい。 sumup = ______ #関数sumpuを定義 kotae = _ #kotaeを初期化 _ (i in ___){ #1からaまで繰り返し kotae = _____ #kotaeにiの値を足したものとkotaeに代入 } #繰り返し終了 _____ #kotaeを表示 } #関数定義の終了 乱数の利用 †最後に、乱数を使ったプログラムを作ってみよう。乱数とは、ランダム(無作為)に得られる数値のことで、次にどんな数字が現れるのか予測できないような数字の集合のことを言う。Rでは、 runif() という関数を使って、好きな数だけ乱数を得ることができる。 runif(5) と入力すれば、乱数が5個得られる。
plot(runif(1000)) #枠内に、1000個の点がランダムにプロットされる hist(runif(1000) #発生させた1000個の乱数のヒストグラム; 1000個ぐらいだと、けっこうばらつく 同じことを、100個、1000個、10000個、100000個についてもやってみよう。バラツキがだんだんと無くなってきたのがわかるだろうか。 #数を増やすと、バラツキが小さくなるのが分かるだろうか?(これを大数の法則という) 乱数をプログラム中で利用することによって、自然界でランダムにおこる事象の模擬実験(シミュレーション)を行うことができる。 乱数を使った条件分岐の演習 †#穴埋め式練習問題 if (runif(1) >= _ ) { #条件式0.5以上ならば print("0.5以上") } _ { print("0.5より小さい") } これでは、どの乱数が発生したのか分からないので、一度別の変数に代入しておいて、判定基準と一緒に表示させてみよう。 #穴埋め式練習問題: _の部分に適当な文字を入力して実行しよう x=runif(1) #乱数を1つ発生させてxに代入 kekka=c() #結果をいれておく空ベクトルを作る if (_>= 0.5 ) { #条件式0.5以上ならば kekka=c(kekka, x) kekka=c(kekka, "は0.5以上") } else { kekka=c(kekka, x) kekka=c(kekka, "は0.5より小さい") } print(kekka) 発展演習:簡単なシミュレーション: 繰り返し・条件分岐・代入でランダムウォークを表現 †これまでやってきたプログラミングだと、まだ、「コンピュータに何かをさせている」ということが、あまり実感できないかもしれない。そこで、次回から本格的にとりくむシミュレーションのさわりを、時間の許す限り解説しておこう。とりあげるのは「ランダムウォーク」で、次に移動する位置が確率的に無作為に決まるというものだ。 rwalk=function(x){ frame() #グラフをクリア kaisu=0 #回数の初期値を0にする point=c(0,0) #原点を示すベクトルをpointという名前で作成 move=floor(runif(x, 0, 4)) #0-3の範囲でx個の整数乱数を発生させる #floor(x)はx以上では無い最大の整数を返す関数 for (i in 1:x) { if (move[i] == 0) { point[1] = point[1]+1 } #0なら右 if (move[i] == 1) { point[2] = point[2]-1 } #1なら下 if (move[i] == 2) { point[1] = point[1]-1 } #2なら左 if (move[i] == 3) { point[2] = point[2]+1 } #3なら上 if ((point[1] > 30) || (point[2] > 30) || (point[1] < -30) || (point[2] < -30)){ break #描画範囲の上限を超えたら止める } par(new=T) #複数のグラフを重ね合わせる plot(point[1], point[2], ylim=c(-30,30), xlim=c(-30,30)) #次の移動位置(pointの場所)をプロット } } 実行するには、 > rwalk(100) と入力する。動かす回数は()内の数値で指定する. 60 x 60の正方形の空間をはみ出すには、何回ぐらいかかるかを予想してからやってみると面白い。 授業で使ったRの基本関数 †→授業/H20/情報処理/R関数一覧 これまでに学んだRのいろんな関数を一覧できる。 第10回授業の課題 †
|