「大人の教養・知識・気付き」を伸ばすブログ

一流の大人(ビジネスマン、政治家、リーダー…)として知っておきたい、教養・社会動向を意外なところから取り上げ学ぶことで“気付く力”を伸ばすブログです。データ分析・語学に力点を置いています。 →現在、コンサルタントの雛になるべく、少しずつ勉強中です(※2024年1月21日改訂)。

MENU

プログラムとしてのRを学ぶ(その01/16)

 \mathrm{R}をプログラムとして見たときに注意・検討すべきところを学んでおきたい、ということで

を読んでいく。

1. はじめに

 \mathrm{R}は統計データの操作や分析を行うためのスクリプト言語である。\mathrm{AT\&T}が開発した統計言語\mathrm{S}からアイディアを得ており、互換性がある。
 本書は\mathrm{R}でソフトウェアを開発したい人物を対象とする。

1.1 Rのメリット

  • \mathrm{R}は広く認められた統計言語\mathrm{S}パブリックドメイン実装であり、\mathrm{R/S}プラットフォームは統計学者の間でデファクトスタンダードになりつつある。
  • \mathrm{R}は利用可能な操作の多様性、プログラマビリティおよびグラフィックスなどの殆どの重要な感t年において商用製品に引けを取らないどころか、優っている部分がある。
  • \mathrm{R}\mathrm{Windows,\ Max,\ Linux}\mathrm{OS}で利用できる。
  • \mathrm{R}は汎用的な言語であるから、統計的演算に加え、分析の自動化や既存言語の機能を拡張する新たな関数を作成できる。
  • \mathrm{R}オブジェクト指向言語かつ関数型プログラミング言語の特性を備えている。
  • \mathrm{R}システムはセッション間でデータセットを保存するため、毎回データをリロードする必要が無く、またコマンド履歴も保存する。
  • \mathrm{R}オープンソースソフトウェアであるため、ユーザコミュニティからのサポートが受けやすい。

1.2 オブジェクト指向プログラミング

 \mathrm{R}オブジェクト指向プログラム言語である。データのアクセス方法がある程度統一されているため、プログラミングがかなり容易である。この統一性は\mathrm{R}が多態的(polymorphic)であることに起因する。多態的とは1つの関数を様々な種類の入力に適用でき、関数が異なる種類の入力を適切に処理する。このような関数をジェネリック関数という。

1.3 関数型プログラミング

 \mathrm{R}は関数型プログラム言語である。そのため関数プログラミング言語に共通したテーマである明示的な反復を避けることを目指している。

関数型プログラム言語であることのメリット

  • 明確でコンパクトなコードを書ける。
  • 実行速度が大幅に高速化する可能性がある。
  • コードが簡潔であることによるデバッグ量が減少し得る。
  • 並列プログラミングへの移行が容易である。

2. Rを始める

 \mathrm{R}を実行する過程におけるポイントを述べる*1

2.1 Rの実行方法

 \mathrm{R}

# sample.Rという名前で保存したRコマンドを読み込む
source("sample.R")
2.1.1 バッチモード

 \mathrm{R}セッションを自動化すると便利な場合がある。これを\mathrm{R}コンソールを開くことなく自動的に処理させたい場合、\mathrm{R}をバッチモードで実行させればよい。
 たとえば\mathrm{z.R}コマンドプロンプトなどで実行させるには、

R CMD BATCH z.R

2.2 最初のRセッション

 ベクトルにおいて特定の成分を出力するには

x[3]

といった具合に出力したい成分を[]で囲んで指定する。このセレクタ(上の3)はインデックスまたはサブスクリプトと呼ぶ。

1.3 関数の概要

 関数は以下のように書く。\mathrm{return()}で返り値を宣言しない場合、最後に計算した結果を暗黙的に返すものの、予想外の返り値を得る可能性があるために明示した方がよい。

# 関数名を宣言の上、関数をプログラミングする
# 最後はreturn()で返り値を言明する
vc_top <- function(vc){
  return(vc[1])
}

 \mathrm{R}のコンセプト上、可能な限りループを避け、避けられないのであれば可能な限り簡潔にする。

x <- 1:50

# C, C++ユーザーは以下のように書きたくなるかもしれない
for(i in 1:length(x)){
  if(x[i] %% 2 ==1) k <- k+1
}

## しかし、たとえば以下のように簡潔にする
#      length()関数を用いず配列インデックスを使わない分、こちらの方が簡潔である
for(i in x){
  if(x %% 2 == 1) k <- k+1
}
1.3.1 変数のスコープ

 関数内で定義した変数はローカルであり、関数が返り値を返すと消えるため、関数外では利用できない。これに対して関数外で定義した変数はグローバルである*2

1.3.2 デフォルト引数

 \mathrm{R}では関数の引数としてデフォルト引数を指定することもできる。

g <- function(x, y = 2, z = T)

上記の例であれば、y,zの指定を省略することができ、その場合はy2に, zにはデフォルト値TRUEだと見なされる*3

1.4 重要なRデータ構造

1.4.1 ベクトル

 \mathrm{R}において中核的なデータ構造である。ベクトルは全要素同じモード*4でなければならない。詳細は別章にて述べる。なおスカラは成分が1つしかないベクトルで、スカラという固有のデータ構造があるわけではない。

1.4.2 文字列

 \mathrm{R}において文字列(\mathrm{character})は文字モードを持つベクトルである。

y <- "abc"
length(y)
is.character(y) # 文字モードか否かを返す
文字列の長さ \mathrm{nchar} 文字列の長さ(数値)を返す。
大文字・小文字 \mathrm{toupper} 小文字をすべて大文字に変換する。
\mathrm{tolower} 大文字をすべて小文字に変換する。
パターンマッチ \mathrm{grep} マッチしたかどうかを返す。
  \mathrm{regexpr} マッチした位置やマッチした長さなどを返す。
文字列一致 \mathrm{match} 文字列の完全一致によるマッチを行う。
\mathrm{charmatch} 部分文字列の完全一致によるマッチを行う。第1引数の対応する成分の文字列が第2引数の対応する文字列に部分一致するかを返す。
文字列の切り出し \mathrm{substr} 指定した文字列について、同じく指定した開始位置および終了位置までの部分文字列を返す。
\mathrm{substring} 同上
文字列の置換 \mathrm{sub} 指定した文字列内にある、条件に合ったすべての部分文字列のうち一番最初にあるものをすべて置換する。
\mathrm{gsub} 指定した文字列内にある、条件に合ったすべての部分文字列をすべて置換する。
\mathrm{chartr} 指定した2つの文字列についてそれぞれに対応する1文字ずつのペアを置換前・置換後の文字列として、指定した文字列内にある該当ペアを置換する。
文字列の結合 \mathrm{paste} 指定した複数の文字列を結合する。結合子はデフォルト引数として" "(半角空欄)が使われている。
\mathrm{paste0} 指定した複数の文字列を結合する。文字列間に何も結合子を入れない(\mathrm{paste(...,sep="")}と同じ。)。
文字列の分割 \mathrm{strsplit} 指定した区切り文字で対象文字列を該当するすべての区切り文字で切断する。返り値はリスト型で返す。
### テスト用文字列

vc_str1 <- c("abc","cde","efg","hij")
vc_str2 <- c("abcd","bcde","defg","fghijk")

vc_str3 <- vc_str1


### 文字列の長さ
nchar(vc_str1)


### 大文字・小文字
toupper(vc_str1)
tolower(toupper(vc_str1))


### パターンマッチ
grep(pattern = "ab",x = vc_str1)
regexpr(pattern = "ab",text = vc_str1)


### 文字列一致
match(x = vc_str1,table = "ab") # vc_Str1の中で"ab"に完全一致するものがあるか
match(x = vc_str1,table = "abc") # vc_Str1の中で"abc"に完全一致するものがあるか
charmatch(x = vc_str1,table = vc_str2) # vc_str2の対応する成分が、vc_str1の対応する成分内に部分一致するか


### 文字列の切り出し
substr(x = vc_str1, start = 2, stop = 3)
substring(text = vc_str1, first = 2, last = 4)
substr(x = vc_str3, start = 2, stop = 3) <- "kkk"
substring(text = vc_str3, first = 2, last = 5) <- "jjj"

vc_str3


### 文字列の置換
sub(pattern = "ab",replacement = "x",x = "abCab")
gsub(pattern = "ab",replacement = "x",x = "abCab")

chartr(old = "abc",new = "xyz",x = "abCab")

### 文字列の結合
paste("abc","def","efg",sep = ",")
paste("abc","def","efg",sep = "")
paste0("abc","def","efg")

### 文字列の分割
strsplit(x = "abc,def,ghi",split = ",")
1.4.3 行列

 \mathrm{R}の行列は数学の行列と同じ概念(数値の長方形配列)に相当する。内部的にはベクトルの一種として扱われる*5\mathrm{R}で便利なのは、部分行列や列ベクトル、行ベクトルの抽出が容易なことである。なお\mathrm{R}では、\mathrm{C,C++}\mathrm{Python}とは異なり、インデックスは1から始まる。

### 行列のいろいろな定式化
mat1 <- matrix(c(1:9), nrow = 3, ncol = 3)
mat2 <- rbind(c(1,3),c(2,4))
mat3 <- cbind(c(1,2),c(3,4))

mat1[1,1]

mat1[1,]
mat1[,1]

mat1[1:2,1:2]
1.4.4 リスト

 リスト型は\mathrm{C}言語の構造体のように異なるデータ型の値を格納できる。リストの成分を指定して抽出したい場合は、$+成分名ないし[]+番号または[]+成分名で指定する。\mathrm{str}関数または\mathrm{summary}関数で内容(の情報)を一部見ることができる。

ls <- list(u = 2, v = c("abc","def"))

ls$u

ls[[1]]
ls[["u"]]
1.4.5 データフレーム

 各成分が行列データの列に相当するベクトルをデータフレームという*6

1.4.6 クラス

 ここまでで検討したデータ型をより抽象化したものとして、クラスがある。オブジェクトはクラスのインスタンスである。以下、回帰分析のクラス\mathrm{lm}を用いて実例を見る。

### 例:回帰分析結果のクラス
n <- 100
vc_x <- c(1:n)/sum(c(1:n))
vc_y <- rnorm(n,0,1)

df <- data.frame(x = vc_x, y = vc_y)

class(df) # data.frame

head(df) # 指定した行列・データフレームの先頭部分のみを出力

lma <- lm(formula = formula(y ~ x),data = df)
lma <- lm(df$y ~ df$x)
lma <- lm(df[,2] ~ df[,1]) # 以上3つはすべて同じ

# lmaにはlmクラスのインスタンスを格納
attributes(lma) # attributesはリストの各成分を列挙
str(lma)

lma$coefficients

lma # これにより出力するのはprint(lma)で、lmaはジェネリック関数として、lmクラスのオブジェクトを出力するprint.lm()に処理を渡す

summary(lma) # ここではジェネリック関数としてsummary()はsummary.lm()を呼び出す

1.5 ヘルプ

# seq関数のヘルプを表示する
help(seq)
?seq # help()の短縮形が?である
?"<" # 特殊文字や一部の予約語は""で囲む

help.search(seq) # help内に該当するものがあるか検索する
??"seq" # help.search()の短縮形が??である

# 登録されたその関数の実例を表示する
example(seq)
1.5.1 バッチモードのヘルプ

 バッチモードでは以下のように書くことでヘルプを表示できる。

R CMD (command) --help
1.5.2 インターネット上のヘルプ

 インターネット上には\mathrm{R}を扱った多数のサイトがある。なお汎用検索エンジン\mathrm{R}を検索すると、1文字のアルファベットなので関係ないサイトが多数引っかかる。そのため、たとえばfiltype:R permutations -rebolと検索すると、拡張子が.Rであるようなものという検索ができる。

*1:個人的に既知のことが多いため、自分にとって確認として有益な事のみをメモする。

*2:関数内でのスコープ基礎は後の章で議論する。

*3:Rでは論理値の\mathrm{TRUE}, \mathrm{FALSE}をそれぞれ\mathrm{T,F}と省略可能である。\mathrm{T,F}を変数として用いている場合は、混同しないために略記しない方が良い。Rは大文字・小文字を区別するためにフルで書く方が面倒なので、個人的にはそのような変数の名前を避ければよいだけだと考えるが。

*4:データ型のこと。

*5:そのため、すべての成分でデータ型は一致しなければならない。ここがデータフレームと相違する。

*6:列ごとに異なる型のデータを持つことができる行列と思うとより分かりやすい。

プライバシーポリシー お問い合わせ