R言語によるコンピュータプログラミング

今回の内容

R言語を用いたコンピュータプログラミングについて取り扱う。

目次

コンピュータプログラミング

R言語はコマンドラインからコマンドとして実行するだけでなく、コンピュータプログラミング言語としても利用することができる。

コンピュータプログラムを作成することにより、繰り返し行われる操作や長時間実行に要する計算をコンピュータに実行させることが可能となる。

ここでは、R言語によるコンピュータプログラミングを行う上で必要となる基本事項について紹介し、いくつかのコンピュータプログラミングの事例を紹介する。

R言語ではソースコードは拡張子.rまたは.Rとすることが慣わしである。以下ではコンピュータプログラミングに必要となる記法について代表的なものを解説する。

条件分岐

条件分岐により選択、場合に応じた処理が可能となる。条件分岐は条件が成立する場合と成立しない場合とで処理を分岐させることができる。

if ~ then ―
else ―

条件には真(True)/偽(False)が用いられる。条件の例としてR言語では以下のものがある。

記号 意味
== 等号
!= 不等号
<= 小なり又は等しい
>= 大なり又は等しい
小なり
大なり
関数 検査対象
is.null() NULL
is.na() NA
is.nan() NaN
is.finite() 有限であるか
is.infinite() 無限であるか
complete.cases() 欠損なし

変数Aと変数Bが一致しないことを調べる

A<-3
B<-5
if(A != B){
    cat(sprintf("A is not equal to B\n"))
}

繰り返し(ループ)

繰り返しは条件が成立するまでの間前段に戻る処理とみなすことができる。すなわち、繰り返しは条件分岐を使って記述することができる。

ステップ―から・・・までを~回繰り返す

for()を用いることで繰り返し処理を行うことができる。

for(変数 in ベクトル型データオブジェクト)

の形で用い、ベクトル型データオブジェクトの最初から最後まで順に変数に取り出しながら実行を繰り返す。典型的には

for(変数 in 始まりの数字:終わりの数字){ 繰り返し処理 }

の形で利用される。

繰り返しの例

1から10までの和を計算して出力することをR言語でどのように記述するかを確認してみよう。以下の0では1から10までの和を計算して出力している。

S<-0
for(i in 1:10){
  S<-S+i
}
cat(sprintf("%d\n",S))

関数

アルゴリズムを簡略化する方法として処理をモジュール化する方法がある。このようなモジュールを設定する手続きを、サブルーチンまたは関数と呼ぶ。関数の中で別の関数を呼び出すこともできる。また、ある関数の中で自分自身を呼び出すこともできる(再帰的アルゴリズム)。

関数定義の例

定義された関数は再利用が可能であり、変数の内部隠ぺいができるので便利である。関数内で定義された変数は基本的に関数の中でのみ有効(ローカル変数)である。関数が終了すると変数の内部状態も消失してしまう。

関数の外側で別途定義することでどの関数からも参照できる変数(グローバル変数)を定義することができる。

関数には引数と返り値がある。そのため、関数を処理の単位ととらえることができる。

関数定義にはfunction()関数を用いる

func <- function(x){
  x<-x*x+3
  return(x)
}
cat(sprintf("%d\n",func(4)))

再帰関数

再帰関数とは関数定義の中で自分自身を呼び出す関数である。終了条件を設定しないと無限ループになってしまうので利用には注意が必要である。

以下に1からnで指定された数までの和を再帰的に計算する関数の例を示す。この例ではrecsum(n)関数を定義している。この関数はnの値が0となるまで、順次nとrecsum(n-1)の和を計算する再帰呼び出しを行う。

recsum <- function(n){
  if(n<=0) return(n)
  else n + recsum(n-1)
}
cat(sprintf("%d\n",recsum(10)))

ローカル変数

関数内で定義した変数はローカル変数である。関数内のみで有効であり、関数実行後は変数は消滅する。

func1 <- function(x){
  y<-x+3
  z<-x+6+y
  return(z)
}
print(func1(7)) # 23
print(y) # error

グローバル変数

グローバル変数はどの関数からでも、参照、代入が可能な変数である。関数外でグローバル変数の値を参照、代入ができ、関数内でグローバル変数の値を参照、代入ができる。関数内でグローバル変数へ代入を行うには<<-を用いる。

x<-3
funca <- function(a){
  return (a*a+4+x)
}
print(x) # 3
print(funca(6)) # 43 
x<-5
print(funca(6)) # 45

外部ソースコードファイルの読み込み

別のファイルで定義されたグローバル変数や関数を読み込んで使うことができる。

source(“ソースコードのファイル名”)

により、外部ソースコードファイルを読み込み、そのファイル内で定義されている関数やグローバル変数を利用することができる。

source1.r

x <- 3
funca <- function(a){
  return (a*a+4+x)
}

source2.r

source("source1.r")
res <- funca(7)
cat(sprintf("funca = %f\n",res))

その他

1次元配列

1~10までの一次元配列は

> x<-1:10
> x
 [1]  1  2  3  4  5  6  7  8  9 10

で作成ができる。

一次元配列の部分取り出しは配列添え字に一次元配列を指定する。

> x[1:5]
[1] 1 2 3 4 5
> x[5:10]
[1]  5  6  7  8  9 10

一次元配列の配列長を得るにはlength()関数を用いる。

> length(x)
[1] 10

条件を満足する配列添え字を検索するにはwhich()関数を用いる。which()関数の引数には論理値を用いる。

> which(x==7)
[1] 7
> which(x==3)
[1] 3

1次元配列に含まれる最小値、最大値、中央値、平均値、分散の計算はそれぞれ、min()関数、max()関数、median()関数、mean()関数、var()関数を使って計算できる。

> min(x)
[1] 1
> max(x)
[1] 10
> median(x)
[1] 5.5
> mean(x)
[1] 5.5
> var(x)
[1] 9.166667

事例

R言語を使った自動処理で有用なRソースコードの事例を紹介する。

ファイルリストを取得

指定されるディレクトリのファイル一覧を取得し、ファイルサイズを出力することを考えてみる。ディレクトリに存在する全てのファイルを自動的に処理する場合に有用なパターンである。

patに対象としたいファイル拡張子(この例では.csv)とpathにファイルを格納しているフォルダー名(この例では現在のワーキングディレクトリを指定)を指定する。この例では、dir()関数でpatで指定される拡張子を有するファイル名一覧をpathで指定されるフォルダーから取得している。更に、それぞれのファイル名filenameからファイル情報をfile.info()関数で取得してファイルサイズを表示している。

pat<-".csv"
path<-getwd()
ls<-dir(pattern=pat,full=T,path=path)
for(filename in ls){
  res<-file.info(filename)
  cat(sprintf("%s (size: %d)\n",filename, res$size))
}

データフレームのデータを検索

データフレームのデータからある条件に合致するデータを取得することにより、データの中から必要となる値のみを抽出することができる。

a<-data.frame(lat=c(34.4,35.5,35.7),long=c(134.1,132.6,137.3),name=c("building1","house1","school1"))
b<-a[a$lat<35,]
print(b)

データフレーム内の値を全て検査

以下の例ではフィールドlat, long, nameを有するデータフレームaを生成し、データフレームに格納されている全ての要素を表として出力している。

a<-data.frame(lat=c(34.4,35.5,35.7),long=c(134.1,132.6,137.3),name=c("building1","house1","school1"))
for(i in 1:nrow(a)){
  for(j in 1:ncol(a)){
     cat(sprintf("|%s",a[i,j]))
  }
  cat(sprintf("|\n"))
}

エクセルファイルから必要項目を取得

library(readxl)
getdata <- function(index,yyyymm){
  a<-read_excel("http://www.city.osaka.lg.jp/toshikeikaku/cmsfiles/contents/0000015/15211/nenrei201511_201610.xlsx
",sheet=index)
  name <- a[8,1] # 大阪市
  pop <- a[8,2] # 総数
  male <- a[8,3] # 男性
  female <- a[8,4] # 女性
  st<-sprintf("%s,%s,%s,%s,%s\n",yyyymm,name,pop,male,female)
  #  cat(st)
  return(st)
}

ofile<-"out.csv"
hh<-sprintf("yyyymm,name,pop,male,female\n")
cat(file=ofile,hh,append=F)
yyyymm <- c("2016.10","2016.9","2016.8","2016.7","2016.6","2016.5","2016.4","2016.3","2016.2","2016.1","2015.12","2015.11")
for(i in length(yyyymm):1){
  st<-getdata(i,yyyymm[i])
  cat(st)
  cat(file=ofile,st,append=T)
}