Rプログラミング入門 11回目
過去記事で散々使い倒してきた、「トランプのデッキ」オブジェクトを利用して、
オブジェクト内の値の書き換え方法を学んでいきます。
下準備
この記事で保存した、card.csvファイルを、read.csv関数を利用して、
deckオブジェクトにロードしてください。
mukkujohn.hatenablog.com
deckオブジェクトはこのようなデータになっています。
> head(deck) face suit value 1 king spades 13 2 queen spades 12 3 jack spades 11 4 ten spades 10 5 nine spades 9 6 eight spades 8
> tail(deck) face suit value 47 six hearts 6 48 five hearts 5 49 four hearts 4 50 three hearts 3 51 two hearts 2 52 ace hearts 1
値の書き換え
実際に書き換える前に、書き換えてもすぐ戻せる様に、
deckオブジェクトをコピーしておきます。
deck2 <- deck
"ace"のカードの値を書き換える事がゴールです。
ベクトルの要素の書き換え
こんなvecオブジェクトがあります。
> vec <- c(0,0,0,0,0,0) > vec [1] 0 0 0 0 0 0
1個目の要素の値を変更するには、値を選択して割り当てます。
> vec[1] <- 1000 > vec [1] 1000 0 0 0 0 0
さらに、選択している要素数と、割り当てる値の個数が同じであれば、
複数の要素をまとめて、書き換える事が可能です。
> vec[c(1,3,5)] <- c(1,1,1) > vec [1] 1 0 1 0 1 0
> vec[4:6] <- vec[4:6]+1 > vec [1] 1 0 1 1 2 1
さらに、さらに、まだ要素が存在しない箇所に、割り当てる事もできます。
> vec[7] <- 0 > vec [1] 1 0 1 1 2 1 0
これには、驚きました。普通エラーになりますよね。
(まぁ、Rを使う人には興味ないんだろうなぁ。。。)
これを利用して、データセットに新しい変数を追加できます。
> deck2$new <- 1:52 > head(deck2) face suit value new 1 king spades 13 1 2 queen spades 12 2 3 jack spades 11 3 4 ten spades 10 4 5 nine spades 9 5 6 eight spades 8 6
割り当て解除は、こちら。
> deck2$new <- NULL > head(deck2) face suit value 1 king spades 13 2 queen spades 12 3 jack spades 11 4 ten spades 10 5 nine spades 9 6 eight spades 8
というのを踏まえ、aceのvalueを変更するには、
aceの行である13,26,39,52行目を選択して、
> deck2[c(13,26,39,52), ] face suit value 13 ace spades 1 26 ace clubs 1 39 ace diamonds 1 52 ace hearts 1
4要素あるため、14を4個割り当てます。
> deck2$value[c(13,26,39,52)] <- c(14,14,14,14) > head(deck2, 13) face suit value 1 king spades 13 2 queen spades 12 3 jack spades 11 4 ten spades 10 5 nine spades 9 6 eight spades 8 7 seven spades 7 8 six spades 6 9 five spades 5 10 four spades 4 11 three spades 3 12 two spades 2 13 ace spades 14
ですが、毎度毎度、aceの行が13,26,39,52行目にあると限りません。
そこで、論理添字です。
論理添字
添え字に論理式が使えます。
なので、下記のような式が、添え字に使えます。
deck2$face == "king"
比較演算子
まずは、式を構成する、Rで使える比較演算子はこちら。
- >
- >=
- <=
- ==
- !=
- %in%
%in%以外は、おなじみですね。
> 1 > 2 [1] FALSE > 1 > c(0,1,2) [1] TRUE FALSE FALSE > c(1,2,3) == c(3,2,1) [1] FALSE TRUE FALSE
%in%はこんな挙動をします。
> 1 %in% c(3,4,5) [1] FALSE > c(1,2) %in% c(3,4,5) [1] FALSE FALSE > c(1,2,3) %in% c(3,4,5) [1] FALSE FALSE TRUE > c(1,2,3,4) %in% c(3,4,5) [1] FALSE FALSE TRUE TRUE
左辺が右辺に含まれていると、
左辺の要素に対して、TRUEを返すような挙動ですね。
例えば、この記事で
mukkujohn.hatenablog.com
作成したshuffle関数でdeckオブジェクトがshuffleされていたとします。
> deck3 <- shuffle(deck) > head(deck3) face suit value 15 queen clubs 12 43 ten hearts 10 22 five clubs 5 51 two hearts 2 37 three diamonds 3 40 king hearts 13
aceの行が分からなくとも、上記の論理添字を活用すれば、
この様に特定し、値を変更する事が可能ですね。
> faceEqAce <- deck3$face == "ace" > deck3$value[faceEqAce] [1] 1 1 1 1 > deck3$value[faceEqAce] <- 14 > deck3[faceEqAce, ] face suit value 13 ace spades 14 39 ace diamonds 14 52 ace hearts 14 26 ace clubs 14
この例では、単純に"ace"のカードだけが条件でしたが、
"spades"の"queen"を特定するにはどうすればよいでしょうか?
ブール演算子
もう予想がつくと思いますが、"spades"の"queen"を特定するためには、
suit == "spades"とface == "queen"をAND条件で結合します。
論理式を結合するのが、ブール演算子です。
Rで使えるブール演算子がこちら。
- &
- |
- xor
- !
- any
- all
各ブール演算子を試すために、下記のa,b,cオブジェクトを用意します。
> a <- c(1,2,3) > b <- c(1,2,3) > c <- c(1,2,4)
全てのブール演算子を試した結果が、こちら。
> a == b & b == c [1] TRUE TRUE FALSE > a == b | b == c [1] TRUE TRUE TRUE > xor(a==b, b==c) [1] FALSE FALSE TRUE > !a==b [1] FALSE FALSE FALSE > any(a==b, b==c) [1] TRUE > all(a==b, b==c) [1] FALSE
わかりづらいのが、"xor"(排他的論理和)ですかね。
左辺式と右辺式のうち、1つだけが真になっているかなので
左辺式:a == b: (1==1, 2==2, 3==3)
右辺式:b == c: (1==1, 2==2, 3==4)
1個目の要素は、左辺:1==1,右辺:1==1 → 左辺:TRUE,右辺:TRUE → FALSE
2個目の要素は、左辺:2==2,右辺:2==2 → 左辺:TRUE,右辺:TRUE → FALSE
3個目の要素は、左辺:3==3,右辺:3==4 → 左辺:TRUE,右辺:FALSE → TRUE
となります。
なので、ブール演算子を使う事で、
"spades"の"queen"はこのように特定する事ができます。
> queenOfSpades <- deck3$suit == "spades" & deck3$face == "queen" > deck3[queenOfSpades, ] face suit value 2 queen spades 12
ここから発展して、ブラックジャック用のデッキを作成します。
ブラックジャック用のデッキ
"king","queen","jack"は10とするので、さくっと値を変更します。
> deck5 <- deck > faceCard <- deck5$face %in% c("king","queen","jack") > deck5$value[faceCard] <- 10
ここで、問題が出てきます。"ace"の扱いです。
"ace"を手札に加えた際、手札の合計が21以上なら1、それ以外には11とします。
つまり、今の状態では決定できる状態ではないのです。
この状態を欠損情報があるといいます。
欠損情報
欠損情報の表現はこの様に記載します。
> NA [1] NA
Not Availableです。
欠損情報をどう扱おうが、答えは欠損情報です。
> 1 + NA [1] NA > NA == 1 [1] NA
これは当然ですね。
分からないから欠損情報にしているので、Rが勝手に特定したら、驚きです。
欠損情報を削除する
上記の様に、計算式に欠損情報が含まれている場合、どうしようもなくなります。
> c(NA,1:50) [1] NA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [25] 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 [49] 48 49 50 > mean(c(NA,1:50)) [1] NA
ですが、欠損情報だけを無視して、計算をしたくなる場合もあります。
そんな時は、na.rmオプション引数を使います。
ほとんどのR関数が、na.rmオプション引数をサポートしているようです。
> mean(c(NA, 1:50), na.rm = TRUE) [1] 25.5
欠損情報を特定する
NAかどうかを知りたい時に、論理式を書いてみますが、
当然ながら、下記の式は、NAを返します。
> NA == NA [1] NA > c(1,2,3,NA) == NA [1] NA NA NA NA
NAかどうかを知りたい時は、特別にis.na関数を利用します。
[1] TRUE > vec <- c(1,2,3,NA) > is.na(vec) [1] FALSE FALSE FALSE TRUE
このあたりは、C#のNULLオブジェクトと扱いが似てますね。
ブラックジャック用のデッキの話に戻ります。
"ace"の値が、デッキ中には決定できないので、欠損情報を割り当てます。
> deck5$value[deck5$face == "ace"] <- NA > head(deck5, 13) face suit value 1 king spades 10 2 queen spades 10 3 jack spades 10 4 ten spades 10 5 nine spades 9 6 eight spades 8 7 seven spades 7 8 six spades 6 9 five spades 5 10 four spades 4 11 three spades 3 12 two spades 2 13 ace spades NA
これで、ブラックジャック用のデッキができました。
今日はここまで。