Lesson 2-1: R のベクトル

2018/10/15

前のステップ

Lesson 1-2: はじめてのRスクリプト では次のことを勉強しました。

このセッションの目標

オブジェクト

オブジェクトというのは数字,ベクトルや構造化されたデータなどの対象(= object)を指します。最初は数や文字,あるいはそれらの集まり=ベクトルから始めましょう。

例えば,数字

> 3
## [1] 3

TRUE, FALSE

> TRUE
## [1] TRUE

あるいは数字の集まりとしての「ベクトル」(ここには少し嘘があります)

> c(1, 3, 0.6)
## [1] 1.0 3.0 0.6

文字列の集まりとしての「ベクトル」

> c("Alpha", "Bravo", "Charlie")
## [1] "Alpha"   "Bravo"   "Charlie"

などはオブジェクトの例です。オブジェクトを構成する要素には「型」という類別を示す属性が与えられています。

> typeof(TRUE)
## [1] "logical"
> typeof("Hello")
## [1] "character"
> typeof(c("Alpha", "Bravo", "Charlie"))
## [1] "character"
> typeof(100)
## [1] "double"

"Hello"c("Alpha", "Bravo", "Charlie") も同じ character 型ですね。 R では単一の数字もベクトル(アトミック・ベクトル)として扱いますので,整合性が取れています。

typeof(100)double = 小数(みたいなもの。double は倍精度浮動小数点数 Double precision floating point number からきている)になるのは,意外かもしれません。整数を明示的に扱いたい場合は後ろに L を付けます。

> typeof(100L)
## [1] "integer"

問題 2.1.1: 次のコードはどのような型のオブジェクトを生成するか予想してください。その後,実際に作成・実行して予想が正しかったかどうかを typeof() 関数を使って検証してください。

> c(3L, 2L, 1L)
> c(1, 2 + 3i, 2 - 3i)
> exp
> c(3L, 2L, 1)

解説

> typeof(c(3L, 2L, 1L))
## [1] "integer"
> typeof(c(1, 2 + 3i, 2 - 3i))
## [1] "complex"
> typeof(c(3L, 2L, 1))  # 最後の1つだけ L がない
## [1] "double"

3L, 2L といった表現は整数を表す表現です。integer 型のオブジェクトを生成します。

2 + 3i などの表現は複素数を表します。complex 型のオブジェクトになります。アトミックベクトルは単一の型の集まりなので,11 + 0i という複素数に変換されます。

最後の例では,11L になることを期待するかもしれませんが,コンピュータに保存された小数には誤差がつきものなので,整数への変換を安全に行うことはできません。したがって,すべての数を double に変換します。

問題 2.1.2: 次のコードはどのような型のオブジェクトを生成するか―あるいは生成できないか―を予想してください。その後,実際にコマンドを実行して予想が正しかったかどうかを検証してください。

> c("Hello", 10)
> c(TRUE, FALSE, 2.3)
> c(TRUE, FALSE, 1L)
> c(TRUE, "Hello")

解説

> c("Hello", 10)
## [1] "Hello" "10"
> c(TRUE, FALSE, 2.3)
## [1] 1.0 0.0 2.3
> c(TRUE, FALSE, 1L)
## [1] 1 0 1
> c(TRUE, "Hello")
## [1] "TRUE"  "Hello"

アトミックベクトルは単一の型の集まりなので,異なる型を集めて c() で結合しようとすると,どれか1つの型に変換されます。

  • integerdouble に変換されます
  • TRUE, FALSE が数値に変換される場合はそれぞれ 10になります。
  • 困ったときは文字列 character にします。

であることを覚えておくと便利な事が多いです。

問題 2.1.3: 次のコードはそれぞれどのような型のオブジェクトを生成するかを予想してください。その後,実際にコマンドを実行して予想が正しかったかどうかを検証してください。

> double(10)
> integer(5)
> character(2)
> logical(3)
> 1:10
> seq(-2, 2, by = 0.5)
> seq(-2, 2, length.out = 5)

解説

double(), integer(), logical() などはゼロを要素としてもつベクトを生成します。double() の代わりに numeric() が使われることも多いですが,同じように振る舞います。

character() は空文字列を生成します。

> double(10)
##  [1] 0 0 0 0 0 0 0 0 0 0
> integer(5)
## [1] 0 0 0 0 0
> character(2)
## [1] "" ""
> logical(3)
## [1] FALSE FALSE FALSE

次のコマンドはよく利用されます。数字を変えて実験してみましょう。

> 1:10
##  [1]  1  2  3  4  5  6  7  8  9 10
> seq(-2, 2, by = 0.5)
## [1] -2.0 -1.5 -1.0 -0.5  0.0  0.5  1.0  1.5  2.0
> seq(-2, 2, length.out = 5)
## [1] -2 -1  0  1  2

名前

名付け

オブジェクトには「名前」をつけることができます。

> x <- 1:10

1:10 で生成されるオブジェクトに x という名前をつける(assign)というコマンドです。<- は「代入」と呼ぶことが多いです。x はオブジェクトと呼んだり,変数と呼んだりします。

オブジェクトの内容を表示するには名前を打ち込みます。

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

名前を付けるのと同時に表示をしたい場合は丸括弧でくくります。

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

コンソールの機能

コンソールで

> x

とすると,内容が表示されますが,スクリプト内にオブジェクト名だけを書いて source() してもオブジェクトの内容は表示されません。(echo = TRUE とすると表示される。)

これは,コンソールに名前を打ち込んで実行すると,print() 関数が(暗黙的に)呼び出されるからです。

> x

というのは,実は

> print(x)
##  [1]  1  2  3  4  5  6  7  8  9 10

と同じことをしているのです。print() はオブジェクトの内容を画面に表示する関数です。(プリントは Generic 関数なのでオブジェクトごとに振る舞いが変わります。前回のスライド を参照。)

スクリプトから変数の内容を表示したい場合には,print() を明示的に呼び出してください。

名前の規則

原則的に,名前は次の規則を守る必要があります。

  • 文字と数字と ., _ のみで構成されていること
  • 最初の文字は数字や _ であってはならない
  • TRUE/FALSE, NULL, NA, if, function などの予約語でないこと

問題 2.1.4: 次のコードはどのようなエラーを表示するだろうか?(そもそもエラーになるだろうか?)

> . <- 100
> _x <- 100
> 2b <- 1:10
> TRUE <- FALSE
> 大学 <- "大阪府立大学"

解説

> . <- 100

これは問題ありません。

> _x <- 100
Error: unexpected input in "_"

_ から始まる名前を付けることはできません。

> 2b <- 1:10
Error: unexpected symbol in "2b"

数字から始まる名前は使用できません。

> TRUE <- FALSE
Error in TRUE <- FALSE : invalid (do_set) left-hand side to assignment

は予約語=上書きできない名前です。予約語のリストを調べるには ?Reserved を実行してください。

名前は漢字(非ASCII文字)であっても問題ありません。(使用を推奨するものではありません)

> (大学 <- "大阪府立大学")
## [1] "大阪府立大学"

TRUE, FALSE の省略形として T, F が使われることがありますが,T, F の使用は推奨されません。これらは予約語ではなく,上書きができてしまうからです。

非構文的な名前

Excel ファイルのヘッダーが数字だけからなるようなケースがあります。データフレームを扱うときに明らかになりますが,このような場合には数字だけからなる名前を作る必要があります。

R ではバックティック( `)で囲むことでこのような名前を作ることができます。

> `2010` <- c(1093, 1402)
> `2011` <- c(892, 1045)
> `TRUE` <- c(2010, 2011)

ベクトルに対する計算

四則演算

ベクトルの四則演算は成分ごとに実行されます。

> x <- 0:10
> y <- seq(0, 1, by = 0.1)
> x + y # 加
##  [1]  0.0  1.1  2.2  3.3  4.4  5.5  6.6  7.7  8.8  9.9 11.0
> x - y # 減
> x * y # 乗
> x / y # 除

2項演算で一方の数が足りない場合には,リサイクル規則が適用されます。

> x * 10
##  [1]   0  10  20  30  40  50  60  70  80  90 100

これは,振る舞いだけ見れば rep() を使った次のコードと同じです。

> x * rep(10, length.out = length(x))
##  [1]   0  10  20  30  40  50  60  70  80  90 100

問題 2.1.5: 次のコードがどのような結果を返すかを予想しなさい。実行して結果を確認してください。また,表示される警告メッセージを解釈してください。

> 1:10 + 1:3

問題 2.1.6: rep() の使用方法について調べなさい。

ベクトル化関数

数学的な関数

R の多くの関数はベクトルの入力を受け取って,ベクトルを出力するように作られています。

> x
##  [1]  0  1  2  3  4  5  6  7  8  9 10
> y
##  [1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
> exp(x)
##  [1]     1.000000     2.718282     7.389056    20.085537    54.598150
##  [6]   148.413159   403.428793  1096.633158  2980.957987  8103.083928
## [11] 22026.465795
> log(x)
##  [1]      -Inf 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595
##  [8] 1.9459101 2.0794415 2.1972246 2.3025851

リサイクル規則と組み合わせると,次のような式が実行できます。(y を平均とする正規分布の密度関数を x の各点で評価したもの)

> exp(- (x - y) ^ 2 / 2) / sqrt(2 * pi)
##  [1] 3.989423e-01 2.660852e-01 7.895016e-02 1.042093e-02 6.119019e-04
##  [6] 1.598374e-05 1.857362e-07 9.601433e-10 2.207990e-12 2.258809e-15
## [11] 1.027977e-18

比較

等号,不等号の比較もベクトル化されています。結果は真偽値からなるベクトルです。

> x < 5
##  [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE

AND/OR で複数の条件を結合する場合は &/| を使います。

> x < 3 | x > 6

結果

##  [1]  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE

> x < 3 & x > 6

結果

##  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

文字列関数

文字列のベクトルも同様にベクトル化されます。

2-1:

  • paste0() は文字列を連結する関数。
  • LETTERS はアルファベットの大文字からなる定義済みのベクトル。

これらを使えば

> paste0("Group-", LETTERS)

結果

##  [1] "Group-A" "Group-B" "Group-C" "Group-D" "Group-E" "Group-F" "Group-G"
##  [8] "Group-H" "Group-I" "Group-J" "Group-K" "Group-L" "Group-M" "Group-N"
## [15] "Group-O" "Group-P" "Group-Q" "Group-R" "Group-S" "Group-T" "Group-U"
## [22] "Group-V" "Group-W" "Group-X" "Group-Y" "Group-Z"

2-2: stringr::fruit はフルーツの名前を集めたベクトルで,パッケージstringrに含まれます。

> (fruit <- stringr::fruit)
##  [1] "apple"             "apricot"           "avocado"          
##  [4] "banana"            "bell pepper"       "bilberry"         
##  [7] "blackberry"        "blackcurrant"      "blood orange"     
## [10] "blueberry"         "boysenberry"       "breadfruit"       
## [13] "canary melon"      "cantaloupe"        "cherimoya"        
## [16] "cherry"            "chili pepper"      "clementine"       
## [19] "cloudberry"        "coconut"           "cranberry"        
## [22] "cucumber"          "currant"           "damson"           
## [25] "date"              "dragonfruit"       "durian"           
## [28] "eggplant"          "elderberry"        "feijoa"           
## [31] "fig"               "goji berry"        "gooseberry"       
## [34] "grape"             "grapefruit"        "guava"            
## [37] "honeydew"          "huckleberry"       "jackfruit"        
## [40] "jambul"            "jujube"            "kiwi fruit"       
## [43] "kumquat"           "lemon"             "lime"             
## [46] "loquat"            "lychee"            "mandarine"        
## [49] "mango"             "mulberry"          "nectarine"        
## [52] "nut"               "olive"             "orange"           
## [55] "pamelo"            "papaya"            "passionfruit"     
## [58] "peach"             "pear"              "persimmon"        
## [61] "physalis"          "pineapple"         "plum"             
## [64] "pomegranate"       "pomelo"            "purple mangosteen"
## [67] "quince"            "raisin"            "rambutan"         
## [70] "raspberry"         "redcurrant"        "rock melon"       
## [73] "salal berry"       "satsuma"           "star fruit"       
## [76] "strawberry"        "tamarillo"         "tangerine"        
## [79] "ugli fruit"        "watermelon"

ベリー類を探すには次のようにします。

> (is_berry <- endsWith(fruit, "berry"))

結果

##  [1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE
## [12] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE
## [23] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE
## [34] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE
## [45] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
## [56] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [67] FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE
## [78] FALSE FALSE FALSE

おっと,これではどれが正解かが分かりませんね。次のようにして名前を表示できます。

> fruit[is_berry]

結果

##  [1] "bilberry"    "blackberry"  "blueberry"   "boysenberry" "cloudberry" 
##  [6] "cranberry"   "elderberry"  "goji berry"  "gooseberry"  "huckleberry"
## [11] "mulberry"    "raspberry"   "salal berry" "strawberry"

このコードが何をしているかは条件による抽出で説明します。

集約

ベクトルの情報から単一の情報を抽出したいケースが頻繁にあります。例えば次のようなケースです。

> sum(x)      # 合計

結果

## [1] 55

> mean(x)     # 平均

結果

## [1] 5

> median(x)   # 中央値

結果

## [1] 5

> var(x)      # 不偏分散

結果

## [1] 11

> sd(x)       # 不偏標準偏差

結果

## [1] 3.316625

個数・割合

TRUE/FALSE が数値的には 1 / 0 であることを思い出すと,

  • sum() を使って条件に当てはまる要素の個数,
  • mean() を使って条件に当てはまる要素の割合

を計算することができます。

> sum(is_berry)
## [1] 14
> mean(is_berry)
## [1] 0.175

ベクトルの要素の抽出

位置による抽出

ベクトルの各要素は 1 から順に整数のインデックスがついています。 このインデックスを使って要素を抽出することができます。

> x <- c(9.1, 0.2, 2.3, 6.4, 8.5)
> x[1]

結果

## [1] 9.1

> x[3]

結果

## [1] 2.3

ベクトルから特定の要素を除いたベクトルを作るにはマイナスの演算子を使って実現できます。

> x[-1]  # 1番目の要素を除く

結果

## [1] 0.2 2.3 6.4 8.5

問題 2.1.7: 次のコードが何を表示するかを予想してください。その後,実行して予想が正しかったかどうかを確認してください。

> x[-c(2, 3)]     # x <- c(9.1, 0.2, 2.3, 6.4, 8.5)

結果

## [1] 9.1 6.4 8.5

問題 2.1.8: ベクトルの最後の要素を抽出したい場合があります。どのようにすればよいでしょうか?

解説 length() と組み合わせれば実現できます。

> x[length(x)]
## [1] 8.5

条件による抽出

基本のルール

まずは,小さいベクトルから始めましょう。小数第1位がインデックスになっていることに注意してください。

> x <- c(0.1, 3.2, 5.3, 8.4, 9.5)

ベクトルと同じ長さの真偽値からなるベクトルを作ります。

> extract <- c(TRUE, FALSE, TRUE, TRUE, FALSE)

TRUE である要素は 1番目,3番目,4番目です。これを x[][] の中に入れます。

> x[extract]
## [1] 0.1 5.3 8.4

extract がTRUEである位置にあるx` の要素が抽出されていることを確認してください。 これと一緒に不等式などの条件付けを使うことで,「4以上の要素をすべて抽出する」といった操作ができるようになります。

問題 2.1.9: x から 4以上の要素をすべて抽出してください。

解説

以上は >= を使って判定します。

> x[x >= 4]
## [1] 5.3 8.4 9.5

リサイクル

抽出元のベクトル(上の例では x)が,抽出位置を表す真偽値ベクトル(上の例では extract)より長い場合は,真偽値ベクトルはリサイクルされます。次の例を確認してください。

> x[c(TRUE, FALSE)]
## [1] 0.1 5.3 9.5

問題 2.1.10: 真偽値ベクトルの方が長い場合はどうなるか?

解説

TRUE に対応する位置にデータがない場合には「NA」= 欠損値が出力されます。FALSE の部分は出力に影響しません。

> y <- 3
> y[c(TRUE, TRUE, FALSE)]
## [1]  3 NA

名前による抽出

ベクトルの要素には名前を付けることができます。

> students <- c("Alice", "Bob", "Charlie")
> math_scores <- c(100, 90, 60)
> names(math_scores) <- students
> math_scores
##   Alice     Bob Charlie 
##     100      90      60

names() <- は各要素に名前を付けるためのコマンドです。このように名前付きのベクトルは,名前でもインデックスでも要素抽出できます。

> math_scores[c("Alice", "Bob")]
## Alice   Bob 
##   100    90
> math_scores[c(1, 2)] * 0.1
## Alice   Bob 
##    10     9

オブジェクトの保存方法

前回,設定で「.RData」の保存をしないようにすることをオススメしました。これには,

という2つの意味が込められています。

オブジェクトを明示的に保存する方法を確認しておきましょう。

saveRDS() を使う

オブジェクト x をバイナリ形式のファイル x.rds に保存する関数は saveRDS() です。

> saveRDS(x, file = "Data/x.rds")

読み込むときは次のようにします。オブジェクト名は同じ名前であっても異なる名前であってもかまいません。

> x <- readRDS("Data/x.rds")

問題 2.1.11: save() という関数もあります。使い方を調べてみましょう。

dump() を使う

テキスト形式で保存することもできます。dump() は実行可能なRスクリプトを生成するので, source() を用いて読み込むことができます。

> dump("x", 
+      file = "Data/dumpdata.R",
+      control = c("all", "hexNumeric"))
> source("Data/dumpdata.R")

統計量と可視化

ベクトルの作り方と使い方がわかったところでデータの可視化の方法を学びましょう。

> set.seed(940)             # 乱数の種。テスト用に固定しておく
> 
> N <- 100                  # ベクトルのサイズ
> xdta <- runif(N, -1, 1)   # 一様分布に従うランダムなベクトル
> ydta <- 1 + 3 * xdta + rnorm(N)   # xdta の1次式 + 正規分布する誤差項

記述統計

記述統計量を表示するための基本関数は summary() という関数です。

> summary(xdta)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -0.97526 -0.42584  0.03486  0.02180  0.50708  0.99345
> summary(ydta)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## -4.3788 -0.6059  0.9370  1.0022  2.5890  5.2566

データ数が含まれていませんので,確認しておきましょう。

> length(xdta)
## [1] 100
> length(ydta)
## [1] 100

分散は次のように計算できます。

> var(xdta)
## [1] 0.3131923
> var(ydta)
## [1] 4.125499

基本的な統計量の確認

ベクトル \[ x = (x_1, x_2, \dots, x_n) \] に対して次の基本統計量がよく使われる。

名称 定義 R コード
平均 \[\bar{x} = \frac{1}{n}\sum_{k = 1}^{n} x_k\] mean(x)
分散 \[\sigma^2 = \sum_{k = 1}^{n} (x_N - \bar{x})^2\] var(x) * (n - 1) / n
標準偏差 \[\sqrt{\sigma^2}\] sd(x) * sqrt((n - 1) / n)
不偏分散 \[s^2 = \frac{1}{n-1}\sum_{k = 1}^{n} (x_N - \bar{x})^2\] var(x)
不偏標準偏差 \[\sqrt{s^2}\] sd(x)
最大値 max(x)
最小値 min(x)
中央値 小さい順(大きい順)に並べて中央。 \(n\) が偶数の場合は中央2つの平均を取る meadian(x)
四分位数 下から 0%, 25%, 50%, 75%, 100% の点 quantile(x)

複数のベクトルに対する共分散は cov(), 相関係数は cor() で計算できる。こちらはまた改めて紹介することにしよう。

散布図

2つの変数の関係を見るための基本的な道具は散布図です。これは次のように簡単に作ることができます。

> plot(xdta, ydta)

箱ひげ図

単一のデータに対する分布を見るために箱ひげ図が役にたちます。描く方法を覚えていますか?R では次のようにします。

> boxplot(xdta, ydta)

ヒストグラム

分布の形状を見る他の方法はヒストグラムを描くことです。hist() は横軸を適当な区間に分割してその区間に入る数字の数を示す棒グラフを描きます。

> hist(ydta)

あるいは確率分布を計算するならば,freq = FALSE を渡します。同じ形で高さだけが違うことがわかります。

> hist(ydta, freq = FALSE)