Mukku John Blog

取り組んでいること を つらつら と

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で使える比較演算子はこちら。

  1. >
  2. >=
  3. <=
  4. ==
  5. !=
  6. %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で使えるブール演算子がこちら。

  1. &
  2. |
  3. xor
  4. !
  5. any
  6. 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

これで、ブラックジャック用のデッキができました。

今日はここまで。