列選択

data.frameから列を選択します。ここではdplyrパッケージにあるselect関数を使用します。

select関数とは

使い方と簡単な例

select(.data, ...)

試しに、irisデータよりSepal.LengthSpeciesを取り出してみましょう:

library(dplyr)

select(.data = iris, Sepal.Length, Species)
## # A tibble: 150 x 2
##    Sepal.Length Species
##           <dbl> <fct>  
##  1         5.10 setosa 
##  2         4.90 setosa 
##  3         4.70 setosa 
##  4         4.60 setosa 
##  5         5.00 setosa 
##  6         5.40 setosa 
##  7         4.60 setosa 
##  8         5.00 setosa 
##  9         4.40 setosa 
## 10         4.90 setosa 
## # ... with 140 more rows

このように、引数の...にピックアップしたい引数を指定すると持ってきてくれます。

また、-を変数名の頭につけると、その変数を除外します:

select(iris, -Species)
## # A tibble: 150 x 4
##    Sepal.Length Sepal.Width Petal.Length Petal.Width
##           <dbl>       <dbl>        <dbl>       <dbl>
##  1         5.10        3.50         1.40       0.200
##  2         4.90        3.00         1.40       0.200
##  3         4.70        3.20         1.30       0.200
##  4         4.60        3.10         1.50       0.200
##  5         5.00        3.60         1.40       0.200
##  6         5.40        3.90         1.70       0.400
##  7         4.60        3.40         1.40       0.300
##  8         5.00        3.40         1.50       0.200
##  9         4.40        2.90         1.40       0.200
## 10         4.90        3.10         1.50       0.100
## # ... with 140 more rows
select(iris, -c(Sepal.Length, Petal.Length))
## # A tibble: 150 x 3
##    Sepal.Width Petal.Width Species
##          <dbl>       <dbl> <fct>  
##  1        3.50       0.200 setosa 
##  2        3.00       0.200 setosa 
##  3        3.20       0.200 setosa 
##  4        3.10       0.200 setosa 
##  5        3.60       0.200 setosa 
##  6        3.90       0.400 setosa 
##  7        3.40       0.300 setosa 
##  8        3.40       0.200 setosa 
##  9        2.90       0.200 setosa 
## 10        3.10       0.100 setosa 
## # ... with 140 more rows

列選択のための便利関数

dplyrには、この列選択のための便利関数がいくつも準備されています:

  • starts_with()
  • ends_with()
  • contains()
  • matches()
  • num_range()
  • one_of()
  • everything()

これらを使えば、かなり思うように列を選択できるようになります。

列名の前方一致 starts_with()

列名を前方一致で持ってきます:

select(iris, starts_with("petal"))
## # A tibble: 150 x 2
##    Petal.Length Petal.Width
##           <dbl>       <dbl>
##  1         1.40       0.200
##  2         1.40       0.200
##  3         1.30       0.200
##  4         1.50       0.200
##  5         1.40       0.200
##  6         1.70       0.400
##  7         1.40       0.300
##  8         1.50       0.200
##  9         1.40       0.200
## 10         1.50       0.100
## # ... with 140 more rows

なお、上記の通りデフォルトでは大文字小文字の区別はしません。区別させたければ、引数としてignore.case = FALSEを指定してください:

select(iris, starts_with("petal", ignore.case = FALSE))
## # A tibble: 150 x 0
select(iris, starts_with("Petal", ignore.case = FALSE))
## # A tibble: 150 x 2
##    Petal.Length Petal.Width
##           <dbl>       <dbl>
##  1         1.40       0.200
##  2         1.40       0.200
##  3         1.30       0.200
##  4         1.50       0.200
##  5         1.40       0.200
##  6         1.70       0.400
##  7         1.40       0.300
##  8         1.50       0.200
##  9         1.40       0.200
## 10         1.50       0.100
## # ... with 140 more rows

列名の後方一致 ends_with()

列名を後方一致で持ってきます:

select(iris, ends_with("Width"))
## # A tibble: 150 x 2
##    Sepal.Width Petal.Width
##          <dbl>       <dbl>
##  1        3.50       0.200
##  2        3.00       0.200
##  3        3.20       0.200
##  4        3.10       0.200
##  5        3.60       0.200
##  6        3.90       0.400
##  7        3.40       0.300
##  8        3.40       0.200
##  9        2.90       0.200
## 10        3.10       0.100
## # ... with 140 more rows

上述のstarts_with()の後方一致版ですので、大文字小文字の扱いも含め同一です。

列名の部分一致 contains()

列名を部分一致で持ってきます:

select(iris, contains("etal"))
## # A tibble: 150 x 2
##    Petal.Length Petal.Width
##           <dbl>       <dbl>
##  1         1.40       0.200
##  2         1.40       0.200
##  3         1.30       0.200
##  4         1.50       0.200
##  5         1.40       0.200
##  6         1.70       0.400
##  7         1.40       0.300
##  8         1.50       0.200
##  9         1.40       0.200
## 10         1.50       0.100
## # ... with 140 more rows

指定した文字列を含む列をすべて選択します。大文字小文字の扱いは上記と同一です。

列名の正規表現による一致 matches()

正規表現を用いてmatchした列名を全て持ってきます:

select(iris, matches(".t."))
## # A tibble: 150 x 4
##    Sepal.Length Sepal.Width Petal.Length Petal.Width
##           <dbl>       <dbl>        <dbl>       <dbl>
##  1         5.10        3.50         1.40       0.200
##  2         4.90        3.00         1.40       0.200
##  3         4.70        3.20         1.30       0.200
##  4         4.60        3.10         1.50       0.200
##  5         5.00        3.60         1.40       0.200
##  6         5.40        3.90         1.70       0.400
##  7         4.60        3.40         1.40       0.300
##  8         5.00        3.40         1.50       0.200
##  9         4.40        2.90         1.40       0.200
## 10         4.90        3.10         1.50       0.100
## # ... with 140 more rows

正規表現については省略しますが、少しでも使えるようになると文字列操作が格段に楽になりますのでぜひ機会を見つけて練習してみてください。

列名の通し番号を利用した選択 num_renge()

よく列名にx1, x2, x3, ...というのがあると思います。これを簡単に選択するためのものです。これはirisデータセットではできないのでサンプルデータを準備します:

df <- as.data.frame(matrix(1:30, nrow = 3, ncol = 10))
colnames(df) <- c(paste0("beer", 1:5), paste0("sake0", 1:5))
ls(df)
##  [1] "beer1"  "beer2"  "beer3"  "beer4"  "beer5"  "sake01" "sake02"
##  [8] "sake03" "sake04" "sake05"

たとえば、ここで「beer1からbeer3までを選択したい」と思ったとします。このような場合には以下のようにします:

select(df, num_range(prefix = "beer", range = 1:3, width = 1))
## # A tibble: 3 x 3
##   beer1 beer2 beer3
##   <int> <int> <int>
## 1     1     4     7
## 2     2     5     8
## 3     3     6     9

引数の意味は以下のとおりです;

prefix

連番変数の根っこ部分。文字列で指定。

range

連番の範囲。数値ベクトルで指定。

width

連番の桁数を指定

ポイントは引数widthです。これがあるおかげで、以下のようなことが可能です:

select(df, num_range("sake", 2:4, 2))
## # A tibble: 3 x 3
##   sake02 sake03 sake04
##    <int>  <int>  <int>
## 1     19     22     25
## 2     20     23     26
## 3     21     24     27

このように、かなり面倒な「01, 02, 03…」といった連番でも指定することが可能です。これらをbase関数群で実現しようとするとかなり苦労しますがこれなら一発です。

行選択

行(レコード)を条件によりフィルタリングして、必要な行(レコード)を残します。ここではdplyrパッケージにあるfilter関数を利用します。

filter関数とは

使い方と簡単な例

filter(.data, ...)

試しに、irisデータよりSepal.Length > 6を満たすレコードを抽出してみます:

library(dplyr)
filter(iris, Sepal.Length > 6)
## # A tibble: 61 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species   
##           <dbl>       <dbl>        <dbl>       <dbl> <fct>     
##  1         7.00        3.20         4.70        1.40 versicolor
##  2         6.40        3.20         4.50        1.50 versicolor
##  3         6.90        3.10         4.90        1.50 versicolor
##  4         6.50        2.80         4.60        1.50 versicolor
##  5         6.30        3.30         4.70        1.60 versicolor
##  6         6.60        2.90         4.60        1.30 versicolor
##  7         6.10        2.90         4.70        1.40 versicolor
##  8         6.70        3.10         4.40        1.40 versicolor
##  9         6.20        2.20         4.50        1.50 versicolor
## 10         6.10        2.80         4.00        1.30 versicolor
## # ... with 51 more rows

このように、引数の...にピックアップしたい引数を指定すると持ってきてくれます。また、複数指定することもできます:

filter(iris, Sepal.Length > 6, Species == "versicolor")
## # A tibble: 20 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species   
##           <dbl>       <dbl>        <dbl>       <dbl> <fct>     
##  1         7.00        3.20         4.70        1.40 versicolor
##  2         6.40        3.20         4.50        1.50 versicolor
##  3         6.90        3.10         4.90        1.50 versicolor
##  4         6.50        2.80         4.60        1.50 versicolor
##  5         6.30        3.30         4.70        1.60 versicolor
##  6         6.60        2.90         4.60        1.30 versicolor
##  7         6.10        2.90         4.70        1.40 versicolor
##  8         6.70        3.10         4.40        1.40 versicolor
##  9         6.20        2.20         4.50        1.50 versicolor
## 10         6.10        2.80         4.00        1.30 versicolor
## 11         6.30        2.50         4.90        1.50 versicolor
## 12         6.10        2.80         4.70        1.20 versicolor
## 13         6.40        2.90         4.30        1.30 versicolor
## 14         6.60        3.00         4.40        1.40 versicolor
## 15         6.80        2.80         4.80        1.40 versicolor
## 16         6.70        3.00         5.00        1.70 versicolor
## 17         6.70        3.10         4.70        1.50 versicolor
## 18         6.30        2.30         4.40        1.30 versicolor
## 19         6.10        3.00         4.60        1.40 versicolor
## 20         6.20        2.90         4.30        1.30 versicolor

出力から、このように続けて指定するとANDになるのがわかりますね。

実例

ここからは、以下のコードで作成したデータを用います:

df <- data.frame(x0 = 1:10,
                x1 = 11:20,
                s = sample(c("kosaki", "chitoge", "marika"), 10,
                           replace = TRUE),
                s2 = sample(c("kosaki", "chitoge", NA), 10,
                            replace = TRUE),
                row.names = letters[1:10])
df
## # A tibble: 10 x 4
##       x0    x1 s       s2     
##  * <int> <int> <fct>   <fct>  
##  1     1    11 marika  chitoge
##  2     2    12 marika  chitoge
##  3     3    13 kosaki  kosaki 
##  4     4    14 marika  chitoge
##  5     5    15 marika  kosaki 
##  6     6    16 marika  chitoge
##  7     7    17 chitoge kosaki 
##  8     8    18 chitoge kosaki 
##  9     9    19 chitoge <NA>   
## 10    10    20 chitoge chitoge

数値フィルタ

まずは単一条件についてです:

# 「*に等しい」
filter(df, x0 == 4)
## # A tibble: 1 x 4
##      x0    x1 s      s2     
##   <int> <int> <fct>  <fct>  
## 1     4    14 marika chitoge
# 「*と等しくない」
filter(df, x0 != 4)
## # A tibble: 9 x 4
##      x0    x1 s       s2     
##   <int> <int> <fct>   <fct>  
## 1     1    11 marika  chitoge
## 2     2    12 marika  chitoge
## 3     3    13 kosaki  kosaki 
## 4     5    15 marika  kosaki 
## 5     6    16 marika  chitoge
## 6     7    17 chitoge kosaki 
## 7     8    18 chitoge kosaki 
## 8     9    19 chitoge <NA>   
## 9    10    20 chitoge chitoge
# 「*より小さい」
filter(df, x0 < 4)
## # A tibble: 3 x 4
##      x0    x1 s      s2     
##   <int> <int> <fct>  <fct>  
## 1     1    11 marika chitoge
## 2     2    12 marika chitoge
## 3     3    13 kosaki kosaki
# 「*以上」
filter(df, x0 >= 4)
## # A tibble: 7 x 4
##      x0    x1 s       s2     
##   <int> <int> <fct>   <fct>  
## 1     4    14 marika  chitoge
## 2     5    15 marika  kosaki 
## 3     6    16 marika  chitoge
## 4     7    17 chitoge kosaki 
## 5     8    18 chitoge kosaki 
## 6     9    19 chitoge <NA>   
## 7    10    20 chitoge chitoge

このあたりは出力と比較すればすぐにわかると思います。

次に複数条件についてです:

# 「AかつB」(AND)
filter(df, x0 < 8 & x1 > 12)
## # A tibble: 5 x 4
##      x0    x1 s       s2     
##   <int> <int> <fct>   <fct>  
## 1     3    13 kosaki  kosaki 
## 2     4    14 marika  chitoge
## 3     5    15 marika  kosaki 
## 4     6    16 marika  chitoge
## 5     7    17 chitoge kosaki
# 「AまたはB」(OR)
filter(df, x0 > 8 | x1 < 15)
## # A tibble: 6 x 4
##      x0    x1 s       s2     
##   <int> <int> <fct>   <fct>  
## 1     1    11 marika  chitoge
## 2     2    12 marika  chitoge
## 3     3    13 kosaki  kosaki 
## 4     4    14 marika  chitoge
## 5     9    19 chitoge <NA>   
## 6    10    20 chitoge chitoge
# 「AかつB」の余事象
filter(df, xor(x0 < 8, x1 > 12))
## # A tibble: 5 x 4
##      x0    x1 s       s2     
##   <int> <int> <fct>   <fct>  
## 1     1    11 marika  chitoge
## 2     2    12 marika  chitoge
## 3     8    18 chitoge kosaki 
## 4     9    19 chitoge <NA>   
## 5    10    20 chitoge chitoge
# 「AからB」
filter(df, dplyr::between(x0, 3, 7))
## # A tibble: 5 x 4
##      x0    x1 s       s2     
##   <int> <int> <fct>   <fct>  
## 1     3    13 kosaki  kosaki 
## 2     4    14 marika  chitoge
## 3     5    15 marika  kosaki 
## 4     6    16 marika  chitoge
## 5     7    17 chitoge kosaki

論理積(AND)は、上述のように,で繋いでいっても反応するのですが、明示的にしたほうがいいでしょう。betweenはdplyrに含まれる関数で、その名の通り区間を指定します。なお指定した範囲の値も含みます。

文字列フィルタ

まずは完全一致についてです:

# 「*と一致する」
filter(df, s == "kosaki")
## # A tibble: 1 x 4
##      x0    x1 s      s2    
##   <int> <int> <fct>  <fct> 
## 1     3    13 kosaki kosaki
# 「*と一致しない」
filter(df, s != "chitoge")
## # A tibble: 6 x 4
##      x0    x1 s      s2     
##   <int> <int> <fct>  <fct>  
## 1     1    11 marika chitoge
## 2     2    12 marika chitoge
## 3     3    13 kosaki kosaki 
## 4     4    14 marika chitoge
## 5     5    15 marika kosaki 
## 6     6    16 marika chitoge

このあたりは数値フィルタと同一です。

次に、「複数の要素のどれかと一致する」ものを抽出する場合です:

filter(df, s %in% c("kosaki", "chitoge"))
## # A tibble: 5 x 4
##      x0    x1 s       s2     
##   <int> <int> <fct>   <fct>  
## 1     3    13 kosaki  kosaki 
## 2     7    17 chitoge kosaki 
## 3     8    18 chitoge kosaki 
## 4     9    19 chitoge <NA>   
## 5    10    20 chitoge chitoge

この%in%はbaseにある演算子で、上記のように「Bの要素のどれか1つにマッチしたらTRUE、どれにもマッチしなければFALSE」を返してくれます。Rを使うなら絶対に覚えたほうがいいでしょう。

なお、この余事象である「複数の要素のどれとも一致しない」ものを抽出する場合です:

filter(df, !s %in% c("marika", "chitoge"))
## # A tibble: 1 x 4
##      x0    x1 s      s2    
##   <int> <int> <fct>  <fct> 
## 1     3    13 kosaki kosaki

非常に残念ながら%nin%みたいな演算子はbaseには入っていません。そのためそのような演算子を自分で定義するか、それを含むパッケージを読み込む、あるいは上記のコードのように頭に!をつけて逆にするあたりが妥当なところでしょう。

次に部分一致についてです。Rのbaseにはgrep系の関数もあるのですが、ここではtidyverseパッケージ群のひとつであるstringrパッケージから、str_detect関数を使います:

library(stringr)

# "k"を含む文字列で抽出
filter(df, str_detect(s, "k"))
## # A tibble: 6 x 4
##      x0    x1 s      s2     
##   <int> <int> <fct>  <fct>  
## 1     1    11 marika chitoge
## 2     2    12 marika chitoge
## 3     3    13 kosaki kosaki 
## 4     4    14 marika chitoge
## 5     5    15 marika kosaki 
## 6     6    16 marika chitoge
# 先頭に"k"を含む文字列で抽出(正規表現によるパターン記述)
filter(df, str_detect(s, "^k"))
## # A tibble: 1 x 4
##      x0    x1 s      s2    
##   <int> <int> <fct>  <fct> 
## 1     3    13 kosaki kosaki

stringr::str_detect(string, pattern)はstringで指定した文字列ベクトルから正規表現でpatternと一致するものをTRUE,それ以外をFALSEで返してきます。greplを使うよりも簡単でかつ高速に処理する(はず)なのでこちらをおすすめします。

NA処理

is.na()関数を利用すると簡単に除去/抽出できます:

# s2にNAが含まれているレコードを抽出
filter(df, is.na(s2))
## # A tibble: 1 x 4
##      x0    x1 s       s2   
##   <int> <int> <fct>   <fct>
## 1     9    19 chitoge <NA>
# s2にNAが含まれていないレコードを抽出
filter(df, !is.na(s2))
## # A tibble: 9 x 4
##      x0    x1 s       s2     
##   <int> <int> <fct>   <fct>  
## 1     1    11 marika  chitoge
## 2     2    12 marika  chitoge
## 3     3    13 kosaki  kosaki 
## 4     4    14 marika  chitoge
## 5     5    15 marika  kosaki 
## 6     6    16 marika  chitoge
## 7     7    17 chitoge kosaki 
## 8     8    18 chitoge kosaki 
## 9    10    20 chitoge chitoge

is.naは「NAか否か」を判定してTRUEあるいはFALSEを返してきます。なのでこの場合s2にNAがあるレコードを抽出してきます。そのため,!is.na(s2)と否定することでs2がNAではないレコードを抽出してくるようになります。

データ結合

データセットを結合する関数として、*_join関数群があります。SQLに慣れている方は、イメージしやすいかと思います。

実例

まず、実例に使うデータセットを準備します:

a <- data.frame(x1=c("A","B","C"),x2=1:3)
b <- data.frame(x1=c("A","B","D"),x3=c(TRUE, FALSE, TRUE))
y <- data.frame(x1=c("A","B","C"),x2=1:3)
z <- data.frame(x1=c("B","C","D"),x2=2:4)

aとbを結合する場合、共通する変数はx1ですので、x1をキーにしてmergeします。 yとzを結合する場合、変数は共通です。行を追加してまとめる、あるいは列を追加してまとめます。

full_join

library(dplyr)
full_join(a, b, by = "x1")
## Warning: Column `x1` joining factors with different levels, coercing to
## character vector
## # A tibble: 4 x 3
##   x1       x2 x3   
##   <chr> <int> <lgl>
## 1 A         1 T    
## 2 B         2 F    
## 3 C         3 NA   
## 4 D        NA T

全ての行と列を結合します。該当するものがない場合、NAがはいります。また、byでキー変数を指定しますが、変数名は文字列(ダブルクォーテーションで挟む)にしてください。

inner_join

inner_join(a, b, by = "x1")
## Warning: Column `x1` joining factors with different levels, coercing to
## character vector
## # A tibble: 2 x 3
##   x1       x2 x3   
##   <chr> <int> <lgl>
## 1 A         1 T    
## 2 B         2 F

両方のデータセットで、共通して存在するもののみを結合します。

left_join

left_join(a, b, by = "x1")
## Warning: Column `x1` joining factors with different levels, coercing to
## character vector
## # A tibble: 3 x 3
##   x1       x2 x3   
##   <chr> <int> <lgl>
## 1 A         1 T    
## 2 B         2 F    
## 3 C         3 NA

左側(第一引数)を優先して結合します。なお右側優先で結合する関数(right_join)もありますが省略します。

bind_cols

bind_cols(y, z)
## # A tibble: 3 x 4
##   x1       x2 x11     x21
##   <fct> <int> <fct> <int>
## 1 A         1 B         2
## 2 B         2 C         3
## 3 C         3 D         4

左側のデータセットに右側の列を追加します。同一名の変数がある場合、被らないように修正されます。また、行数が一致しないとエラーとなります

bind_rows

bind_rows(y, z)
## Warning in bind_rows_(x, .id): Unequal factor levels: coercing to character
## Warning in bind_rows_(x, .id): binding character and factor vector,
## coercing into character vector

## Warning in bind_rows_(x, .id): binding character and factor vector,
## coercing into character vector
## # A tibble: 6 x 2
##   x1       x2
##   <chr> <int>
## 1 A         1
## 2 B         2
## 3 C         3
## 4 B         2
## 5 C         3
## 6 D         4
bind_rows(y, z, .id = "table_id")
## Warning in bind_rows_(x, .id): Unequal factor levels: coercing to character

## Warning in bind_rows_(x, .id): binding character and factor vector,
## coercing into character vector

## Warning in bind_rows_(x, .id): binding character and factor vector,
## coercing into character vector
## # A tibble: 6 x 3
##   table_id x1       x2
##   <chr>    <chr> <int>
## 1 1        A         1
## 2 1        B         2
## 3 1        C         3
## 4 2        B         2
## 5 2        C         3
## 6 2        D         4

左側のデータセットに右側の行を追加します。列数が一致しないとエラーとなります。また、.id引数を使うと、上述の例のように「どのデータセットから持ってきたか」というのを識別できるような変数を自動で作成します。

新しい変数の生成・値の再割り当て

新たに変数を生成したり、既存の変数に対して値を再割り当てするには、dplyrmutate関数を使用します。

mutate関数とは

データセットに新たに変数を追加する関数です。また、既存の変数の書き換えも可能です。dplyrパッケージでも高頻度で利用される基本的な関数の一つです。

使い方と例

mutate(.data, ...)

試しに、irisデータのSepal.Lengthの値を2倍した変数としてkosakiという変数を作成してみます:

library(dplyr)
mutate(iris, kosaki = Sepal.Length * 2)
## # A tibble: 150 x 6
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species kosaki
##           <dbl>       <dbl>        <dbl>       <dbl> <fct>    <dbl>
##  1         5.10        3.50         1.40       0.200 setosa   10.2 
##  2         4.90        3.00         1.40       0.200 setosa    9.80
##  3         4.70        3.20         1.30       0.200 setosa    9.40
##  4         4.60        3.10         1.50       0.200 setosa    9.20
##  5         5.00        3.60         1.40       0.200 setosa   10.0 
##  6         5.40        3.90         1.70       0.400 setosa   10.8 
##  7         4.60        3.40         1.40       0.300 setosa    9.20
##  8         5.00        3.40         1.50       0.200 setosa   10.0 
##  9         4.40        2.90         1.40       0.200 setosa    8.80
## 10         4.90        3.10         1.50       0.100 setosa    9.80
## # ... with 140 more rows

このように、引数の...で受け取った内容を評価し、.dataで指定したデータセットの最後に追加します。また、変数名として既存の名前を与えると、その変数が上書きされます:

mutate(iris, Sepal.Length = Sepal.Width * 3)
## # A tibble: 150 x 5
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
##           <dbl>       <dbl>        <dbl>       <dbl> <fct>  
##  1        10.5         3.50         1.40       0.200 setosa 
##  2         9.00        3.00         1.40       0.200 setosa 
##  3         9.60        3.20         1.30       0.200 setosa 
##  4         9.30        3.10         1.50       0.200 setosa 
##  5        10.8         3.60         1.40       0.200 setosa 
##  6        11.7         3.90         1.70       0.400 setosa 
##  7        10.2         3.40         1.40       0.300 setosa 
##  8        10.2         3.40         1.50       0.200 setosa 
##  9         8.70        2.90         1.40       0.200 setosa 
## 10         9.30        3.10         1.50       0.100 setosa 
## # ... with 140 more rows

実例

単純な加工なら、上記のような計算式を書けばOKです。また、データベース処理としてよく使われる窓(window)関数がありますが、基本的なものはdplyrパッケージに組み込まれています。このwindow関数を含め、いくつかピックアップします。また、説明用に以下のデータセットを使います:

df <- data.frame(
  no = 1:7,
  x1 = c(sample(1:6), NA),
  s1 = c(LETTERS[1:4], NA, LETTERS[6:7])
)
df
## # A tibble: 7 x 3
##      no    x1 s1   
##   <int> <int> <fct>
## 1     1     6 A    
## 2     2     3 B    
## 3     3     5 C    
## 4     4     4 D    
## 5     5     2 <NA> 
## 6     6     1 F    
## 7     7    NA G

オフセット関数

要するに「*個ずらす」という関数です。差分を出したい時によく使います:

# 1個ずらした変数を作成
mutate(df, x2 = lag(x1))
## # A tibble: 7 x 4
##      no    x1 s1       x2
##   <int> <int> <fct> <int>
## 1     1     6 A        NA
## 2     2     3 B         6
## 3     3     5 C         3
## 4     4     4 D         5
## 5     5     2 <NA>      4
## 6     6     1 F         2
## 7     7    NA G         1
# 3個ずらした変数を作成
mutate(df, x2 = lag(x1, 3))
## # A tibble: 7 x 4
##      no    x1 s1       x2
##   <int> <int> <fct> <int>
## 1     1     6 A        NA
## 2     2     3 B        NA
## 3     3     5 C        NA
## 4     4     4 D         6
## 5     5     2 <NA>      3
## 6     6     1 F         5
## 7     7    NA G         4
# ずらした空欄に値を指定
mutate(df, x2 = lag(x1, 2, default = 0))
## # A tibble: 7 x 4
##      no    x1 s1       x2
##   <int> <int> <fct> <int>
## 1     1     6 A         0
## 2     2     3 B         0
## 3     3     5 C         6
## 4     4     4 D         3
## 5     5     2 <NA>      5
## 6     6     1 F         4
## 7     7    NA G         2

また、逆に上へずらす関数もあります:

# 1個ずらした変数を作成
mutate(df, x2 = lead(x1))
## # A tibble: 7 x 4
##      no    x1 s1       x2
##   <int> <int> <fct> <int>
## 1     1     6 A         3
## 2     2     3 B         5
## 3     3     5 C         4
## 4     4     4 D         2
## 5     5     2 <NA>      1
## 6     6     1 F        NA
## 7     7    NA G        NA
# 3個ずらした変数を作成
mutate(df, x2 = lead(x1, 3))
## # A tibble: 7 x 4
##      no    x1 s1       x2
##   <int> <int> <fct> <int>
## 1     1     6 A         4
## 2     2     3 B         2
## 3     3     5 C         1
## 4     4     4 D        NA
## 5     5     2 <NA>     NA
## 6     6     1 F        NA
## 7     7    NA G        NA
# ずらした空欄に値を指定
mutate(df, x2 = lead(x1, 2, default = 0))
## # A tibble: 7 x 4
##      no    x1 s1       x2
##   <int> <int> <fct> <int>
## 1     1     6 A         5
## 2     2     3 B         4
## 3     3     5 C         2
## 4     4     4 D         1
## 5     5     2 <NA>     NA
## 6     6     1 F         0
## 7     7    NA G         0

累積関数

累積に関するものです:

# 累積和を計算
mutate(df, x2 = cumsum(x1))
## # A tibble: 7 x 4
##      no    x1 s1       x2
##   <int> <int> <fct> <int>
## 1     1     6 A         6
## 2     2     3 B         9
## 3     3     5 C        14
## 4     4     4 D        18
## 5     5     2 <NA>     20
## 6     6     1 F        21
## 7     7    NA G        NA

NAが入るとNAになるので注意してください。なお累積関数には他にもいろいろありますので、このページ最後の資料をぜひ参照してみてください。

条件による値指定

dplyrにはif_elseという関数があり、条件を指定して値を指定していくことができます:

mutate(df, x2 = if_else(condition = x1 > 3, true = TRUE, false = FALSE))
## # A tibble: 7 x 4
##      no    x1 s1    x2   
##   <int> <int> <fct> <lgl>
## 1     1     6 A     T    
## 2     2     3 B     F    
## 3     3     5 C     T    
## 4     4     4 D     T    
## 5     5     2 <NA>  F    
## 6     6     1 F     F    
## 7     7    NA G     NA

baseにもifelse()があるのですが、こちらのほうがより厳密で、trueの内容とfalseの内容の処理内容をチェックして、同じ型で同じclassかどうかをみるようです。また、引数にmissingがあり、欠損値への処理ができるようになっています:

mutate(df, x2 = if_else(x1 > 4, "high", "low", ""))
## # A tibble: 7 x 4
##      no    x1 s1    x2   
##   <int> <int> <fct> <chr>
## 1     1     6 A     high 
## 2     2     3 B     low  
## 3     3     5 C     high 
## 4     4     4 D     low  
## 5     5     2 <NA>  low  
## 6     6     1 F     low  
## 7     7    NA G     ""

条件を指定してNA

条件に合致した値をNAにします:

mutate(df, x2 = na_if(x1, 4))
## # A tibble: 7 x 4
##      no    x1 s1       x2
##   <int> <int> <fct> <int>
## 1     1     6 A         6
## 2     2     3 B         3
## 3     3     5 C         5
## 4     4     4 D        NA
## 5     5     2 <NA>      2
## 6     6     1 F         1
## 7     7    NA G        NA

NAにしたい値は単一の値、もしくはデータセットと同一の長さのベクトルを受け付けるので注意してください。

該当する値の書き換え

該当する値を書き換えます:

mutate(df,
       s2 = recode(s1, A = "kosaki"),
       s3 = recode(s1, A = "kosaki", .default = "xxx"),
       x2 = recode(x1, `5` = 55L, `6` = 66L, .default = x1),
       x3 = recode(x1, `5` = 55L, `6` = 66L, .default = x1, .missing = -1L))
## # A tibble: 7 x 7
##      no    x1 s1    s2     s3        x2    x3
##   <int> <int> <fct> <fct>  <fct>  <int> <int>
## 1     1     6 A     kosaki kosaki    66    66
## 2     2     3 B     B      xxx        3     3
## 3     3     5 C     C      xxx       55    55
## 4     4     4 D     D      xxx        4     4
## 5     5     2 <NA>  <NA>   <NA>       2     2
## 6     6     1 F     F      xxx        1     1
## 7     7    NA G     G      xxx       NA   - 1

出力結果を確認してもらえればわかるかと思います。なお、数値の場合5のようにバッククォートで挟む必要があり、書き換える値で55Lとしているのは、Rで数値の末尾にLをつけるとそれを数値として扱うようするためです。このようにrecodeは型に対して厳し目になっています。

複数の条件で場合分け

if_else()の複数ケースで、まとめて処理できます:

mutate(df,
       x2 = case_when(
         no %% 2 == 0 ~ "Even",
         no %% 2 == 1 ~ "Odd",
         TRUE ~ as.character(x1)
       ),
       x3 = case_when(
         no <= 3 ~ x1 * -2,
         between(no, 3, 5) ~ as.numeric(x1),
         TRUE ~ no ^ 2
       ),
       s2 = case_when(
         s1 == "A" ~ "kosaki"
       ),
       s3 = case_when(
         s1 == "A" ~ "kosaki",
         TRUE ~ as.character(s1)
       ),
       s4 = case_when(
         TRUE ~ as.character(s1),
         s1 == "A" ~ "chitoge"
       ))
## # A tibble: 7 x 8
##      no    x1 s1    x2        x3 s2     s3     s4   
##   <int> <int> <fct> <chr>  <dbl> <chr>  <chr>  <chr>
## 1     1     6 A     Odd   -12.0  kosaki kosaki A    
## 2     2     3 B     Even  - 6.00 <NA>   B      B    
## 3     3     5 C     Odd   -10.0  <NA>   C      C    
## 4     4     4 D     Even    4.00 <NA>   D      D    
## 5     5     2 <NA>  Odd     2.00 <NA>   <NA>   <NA> 
## 6     6     1 F     Even   36.0  <NA>   F      F    
## 7     7    NA G     Odd    49.0  <NA>   G      G

case_when()では複数の条件を指定していくことができ,引数に論理式と置き換える値を記述していきます。この際,~を利用したformulaを使用します。

また,論理式で拾えなかった要素については全てNAが返ってきます。なお,「それ以外」を指し示すのはTRUEで,上記のように~の左辺に記述すればOKです。

注意事項ですが,順番が重要で最初に書いてあるものが優先されます。s3とs4のコードと出力を比較してください。

データのグループ化と要約統計量

「ある変数の値ごとに処理をして平均や標準偏差を算出したい」という時には、グループ化の処理を行います。ここではdplyrパッケージのgroup_by関数を利用します。

また、集計をするにはdplyrパッケージのsummarize関数もしくはsummarise関数を利用します。

実例

library(dplyr)
df <- group_by(iris, Species)
df
## # A tibble: 150 x 5
## # Groups:   Species [3]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
##           <dbl>       <dbl>        <dbl>       <dbl> <fct>  
##  1         5.10        3.50         1.40       0.200 setosa 
##  2         4.90        3.00         1.40       0.200 setosa 
##  3         4.70        3.20         1.30       0.200 setosa 
##  4         4.60        3.10         1.50       0.200 setosa 
##  5         5.00        3.60         1.40       0.200 setosa 
##  6         5.40        3.90         1.70       0.400 setosa 
##  7         4.60        3.40         1.40       0.300 setosa 
##  8         5.00        3.40         1.50       0.200 setosa 
##  9         4.40        2.90         1.40       0.200 setosa 
## 10         4.90        3.10         1.50       0.100 setosa 
## # ... with 140 more rows

見た目では変化していないように見えますが、これでグループ化されて処理されるようになります:

mutate(df, x1 = cumsum(Sepal.Length))
## # A tibble: 150 x 6
## # Groups:   Species [3]
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species    x1
##           <dbl>       <dbl>        <dbl>       <dbl> <fct>   <dbl>
##  1         5.10        3.50         1.40       0.200 setosa   5.10
##  2         4.90        3.00         1.40       0.200 setosa  10.0 
##  3         4.70        3.20         1.30       0.200 setosa  14.7 
##  4         4.60        3.10         1.50       0.200 setosa  19.3 
##  5         5.00        3.60         1.40       0.200 setosa  24.3 
##  6         5.40        3.90         1.70       0.400 setosa  29.7 
##  7         4.60        3.40         1.40       0.300 setosa  34.3 
##  8         5.00        3.40         1.50       0.200 setosa  39.3 
##  9         4.40        2.90         1.40       0.200 setosa  43.7 
## 10         4.90        3.10         1.50       0.100 setosa  48.6 
## # ... with 140 more rows

また、多くの場合集計に利用します:

# irisでそのまま集計した場合:
summarize(iris, n = n(), mean = mean(Sepal.Length), sd = sd(Sepal.Length))
## # A tibble: 1 x 3
##       n  mean    sd
##   <int> <dbl> <dbl>
## 1   150  5.84 0.828
# group_byしたあと:
summarize(df, n = n(), mean = mean(Sepal.Length), sd = sd(Sepal.Length))
## # A tibble: 3 x 4
##   Species        n  mean    sd
##   <fct>      <int> <dbl> <dbl>
## 1 setosa        50  5.01 0.352
## 2 versicolor    50  5.94 0.516
## 3 virginica     50  6.59 0.636

なお、グループ化を解除するにはungroup関数を使います

df2 <- ungroup(df)
summarize(df2, n = n(), mean = mean(Sepal.Length), sd = sd(Sepal.Length))
## # A tibble: 1 x 3
##       n  mean    sd
##   <int> <dbl> <dbl>
## 1   150  5.84 0.828

データセットを縦型(long型)あるいは横型(wide型)へと変換します。tidy dataという考え方がポイントになりますので、このあたりについては一度別資料を探してみてください。

実例

縦型へ変換

tidyrパッケージのgather関数で行えます:

library(tidyr)
gather(iris, key = hoge, value = fuga, -Species)
## # A tibble: 600 x 3
##    Species hoge          fuga
##    <fct>   <chr>        <dbl>
##  1 setosa  Sepal.Length  5.10
##  2 setosa  Sepal.Length  4.90
##  3 setosa  Sepal.Length  4.70
##  4 setosa  Sepal.Length  4.60
##  5 setosa  Sepal.Length  5.00
##  6 setosa  Sepal.Length  5.40
##  7 setosa  Sepal.Length  4.60
##  8 setosa  Sepal.Length  5.00
##  9 setosa  Sepal.Length  4.40
## 10 setosa  Sepal.Length  4.90
## # ... with 590 more rows

keyには、まとめた時に「この行の値はどの変数に入ってたものか」を示す変数名を指定します。またvalueには、まとめた変数の値が格納されます。その後ろに、まとめ上げる変数群を指定します。ここではdplyr::select()のテクニックがそのまま使えます。

横型へ変換

tidyrパッケージのspread関数で行えます:

# irisにidの列を追加して、一旦gatherでまとめます
library(dplyr)
df <- mutate(iris, id = rownames(iris))
df <- gather(df, key = hoge, value = fuga, contains("l."))
head(df)
## # A tibble: 6 x 4
##   Species id    hoge          fuga
## * <fct>   <chr> <chr>        <dbl>
## 1 setosa  1     Sepal.Length  5.10
## 2 setosa  2     Sepal.Length  4.90
## 3 setosa  3     Sepal.Length  4.70
## 4 setosa  4     Sepal.Length  4.60
## 5 setosa  5     Sepal.Length  5.00
## 6 setosa  6     Sepal.Length  5.40
# これを再度バラします
spread(df, key = hoge, value = fuga)
## # A tibble: 150 x 6
##    Species id    Petal.Length Petal.Width Sepal.Length Sepal.Width
##    <fct>   <chr>        <dbl>       <dbl>        <dbl>       <dbl>
##  1 setosa  1             1.40       0.200         5.10        3.50
##  2 setosa  10            1.50       0.100         4.90        3.10
##  3 setosa  11            1.50       0.200         5.40        3.70
##  4 setosa  12            1.60       0.200         4.80        3.40
##  5 setosa  13            1.40       0.100         4.80        3.00
##  6 setosa  14            1.10       0.100         4.30        3.00
##  7 setosa  15            1.20       0.200         5.80        4.00
##  8 setosa  16            1.50       0.400         5.70        4.40
##  9 setosa  17            1.30       0.400         5.40        3.90
## 10 setosa  18            1.40       0.300         5.10        3.50
## # ... with 140 more rows

このようにgatherとspreadは対応しています。なお、spreadを実行する際、処理後に各レコードが一意に特定できる列(主キーにあたるようなもの)が必要となります。関数の引数で指定する必要はないのですが、これがないと展開してくれずエラーを返すので注意してください。

まとめと参考資料

参考文献

  • Jared P. Lander (著)みんなのR -データ分析と統計解析の新しい教科書- マイナビ
  • Kun Ren (著) Rプログラミング本格入門: 達人データサイエンティストへの道 共立出版
  • 石田 基広 (著) 改訂3版 R言語逆引きハンドブック シーアンドアール研究所

session info

このページを実行した環境は以下のとおりです:

session-info:

sessionInfo()
## R version 3.4.4 (2018-03-15)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 16.04.4 LTS
## 
## Matrix products: default
## BLAS: /usr/lib/libblas/libblas.so.3.6.0
## LAPACK: /usr/lib/lapack/liblapack.so.3.6.0
## 
## locale:
##  [1] LC_CTYPE=ja_JP.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=ja_JP.UTF-8        LC_COLLATE=ja_JP.UTF-8    
##  [5] LC_MONETARY=ja_JP.UTF-8    LC_MESSAGES=ja_JP.UTF-8   
##  [7] LC_PAPER=ja_JP.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=ja_JP.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] tidyr_0.8.0   stringr_1.3.0 bindrcpp_0.2  dplyr_0.7.4  
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_0.12.15     knitr_1.19       bindr_0.1        magrittr_1.5    
##  [5] tidyselect_0.2.3 R6_2.2.2         rlang_0.1.6      tools_3.4.4     
##  [9] utf8_1.1.3       cli_1.0.0        htmltools_0.3.6  yaml_2.1.16     
## [13] rprojroot_1.3-2  digest_0.6.15    assertthat_0.2.0 tibble_1.4.2    
## [17] crayon_1.3.4     purrr_0.2.4      glue_1.2.0       evaluate_0.10.1 
## [21] rmarkdown_1.8    stringi_1.1.6    compiler_3.4.4   pillar_1.1.0    
## [25] backports_1.1.2  pkgconfig_2.0.1

このWebサイトは2018年3月21日に開催された、日本社会心理学会 第5回春の方法論セミナーで行われた「RとRStudio入門」の公開資料です。

本資料はCC-BYにて公開しています。