はじめに
Rでデータフレームへいろいろと操作するとき、apply
とsapply
の動作の違いでちょっとハマってしまったので、備忘録として記事を残しておく。
saplly
,apply
関数とis.character
での文字列の判定
文字列と数字の混ざったデータから、数字データのみを取り出したかった。ので、以下のようなデータフレームを準備する。
a <- c(1, 3, 7)
b <- c("test1", "test2", "test3")
c <- c(0.8, 0.6, 0.4)
x <- data.frame(a, b, c)
x
a b c
1 1 test1 0.8
2 3 test2 0.6
3 7 test3 0.4
ここから、b列を文字判定してTRUE, FALSEのベクトルをつくり、再度データフレームを再構築すれば、実装できそう。なのでapply
関数を使いis.character
を列に適応する。
apply(x, 2, is.character)
a b c
TRUE TRUE TRUE
なぜか、数字のみが入っているa, c も文字列判定されてしまった。ここで、ならばsapply
ではどうなるか疑問が生じたので試してみる。
sapply(x, is.character)
a b c
FALSE TRUE FALSE
こんどは上手くいった!ので、文字列以外の要素でデータフレームを再構成すると、
y <- x[, !sapply(x, is.character)]
y
a c
1 1 0.8
2 3 0.6
3 7 0.4
となり、これで文字列を除外した数値のみのデータが作れた。
apply
関数が目的の通り動作しなかった理由
ここでapply
関数のヘルプを見てみると(?apply
を実行すると)、
If X is not an array but an object of a class with a non-null dim value (such as a data frame), apply attempts to coerce it to an array via as.matrix if it is two-dimensional (e.g., a data frame) or via as.array.
とある。つまりデータフレームなどはas.matrix
を通して操作することになる、と。では、as.matrix
はと言うと、
The method for data.tables will return a character matrix if there are only atomic columns and any non-(numeric/logical/complex) column, applying as.vector to factors and format to other non-character columns. Otherwise, the usual coercion hierarchy (logical < integer < double < complex) will be used, e.g., all-logical data frames will be coerced to a logical matrix, mixed logical-integer will give an integer matrix, etc.
とある。英語が苦手なのできちんと読み解けているか不安だが、非(数値、論理、複素数)型が含まれていると、「文字列」で値を返す仕様らしい。as.matrix
の出力を見てみると、
as.matrix(x)
a b c
[1,] "1" "test1" "0.8"
[2,] "3" "test2" "0.6"
[3,] "7" "test3" "0.4"
となり、確かに全部文字列になってしまっている。もし、b列が無いデータ(整数と少数のみのデータ)の場合は、
tmp <- data.frame(a,c)
tmp
a c
1 1 0.8
2 3 0.6
3 7 0.4
as.matrix(tmp)
a c
[1,] 1 0.8
[2,] 3 0.6
[3,] 7 0.4
apply(tmp, 2, is.character)
a c
FALSE FALSE
となり、全て想定通りの挙動を示した。
おわりに
sapply
は値をリストとして扱ってくれるのでこの問題が起きない、と思われる。
sapply
の詳しい説明はヘルプを見てもよく分からなかったので、もし補足等あればコメント頂けますと幸いです。