sapply()でハマった

ので同じ過ちを避けるためメモ。
他のいくつかのapply系関数でも似たようなことが起こる(みんな内部でlapply呼んでたりするので)。
問題はsapplyに与える関数の返り値にnamesを設定していると起こる。
例示のため引数をnamesに設定した値を返す関数を定義する。

f <- function(x){
  y <- numeric(1)
  names(y) <- x
  y
}
> f(1)
1 
0 
> f(letters[5])
e 
0
> f("hoge")
hoge
   0

それでこの関数にsapply()で引数を与える。このとき、引数が文字列ベクトルか否かで返り値が変わる、という点に気付かなくてハマってた。

> sapply(1:5, f)
1 2 3 4 5 
0 0 0 0 0 
> sapply(as.character(1:5), f)
1.1 2.2 3.3 4.4 5.5 
  0   0   0   0   0
> sapply(letters[1:5], f)
a.a b.b c.c d.d e.e 
  0   0   0   0   0 

これはlapply()のhelpを読めばUSE.NAMESという引数が関係するということが書いてある。

USE.NAMES logical; if TRUE and if X is character, use X as names for the result unless it had names already.

デフォルトでこの値はTRUEなので、sapplyの第一引数Xが文字列ベクトルである場合はそれが名前に設定される。

> f2 <- function(x) x
> sapply(as.character(1:5), f2)
  1   2   3   4   5 
"1" "2" "3" "4" "5" 
> sapply(1:5, f2)
[1] 1 2 3 4 5
> sapply(as.character(1:5), f2)
  1   2   3   4   5 
"1" "2" "3" "4" "5" 
> sapply(as.character(1:5), f2, USE.NAMES=FALSE)
[1] "1" "2" "3" "4" "5"

helpは「既にnamesが設定されている場合は設定されない」と書いてあるように見えるのだけれど、実際は返り値にnamesが設定されていると既存のnamesの前にXとドットが追加される。

> f3 <- function(x){
+     names(x) <- "hoge"
+     x
+ }
> sapply(1:5, f3)
hoge hoge hoge hoge hoge 
   1    2    3    4    5 
> sapply(as.character(1:5), f3)
1.hoge 2.hoge 3.hoge 4.hoge 5.hoge 
   "1"    "2"    "3"    "4"    "5"
> sapply(as.character(1:5), f3, USE.NAMES=FALSE)
hoge hoge hoge hoge hoge 
 "1"  "2"  "3"  "4"  "5" 

バグなのか僕が何か勘違いしてるのかよく分からなくなってきたので知ってる人誰か教えてください…