data.frameから列を選択します。ここではdplyrパッケージにあるselect
関数を使用します。
select(.data, ...)
試しに、iris
データよりSepal.Length
とSpecies
を取り出してみましょう:
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
引数の意味は以下のとおりです;
連番変数の根っこ部分。文字列で指定。
連番の範囲。数値ベクトルで指定。
連番の桁数を指定。
ポイントは引数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(.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
を使うよりも簡単でかつ高速に処理する(はず)なのでこちらをおすすめします。
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を結合する場合、変数は共通です。行を追加してまとめる、あるいは列を追加してまとめます。
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(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(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(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(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
引数を使うと、上述の例のように「どのデータセットから持ってきたか」というのを識別できるような変数を自動で作成します。
新たに変数を生成したり、既存の変数に対して値を再割り当てするには、dplyrの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にします:
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を実行する際、処理後に各レコードが一意に特定できる列(主キーにあたるようなもの)が必要となります。関数の引数で指定する必要はないのですが、これがないと展開してくれずエラーを返すので注意してください。
このページを実行した環境は以下のとおりです:
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