リストに名前を付けるのは最後にした方がいいっぽい(へんなのR)

へんなのRというハッシュタグがある。
そこに投げつけるための話。

適当な関数の用意

3の時だけNULLであとはirisデータの頭を返す関数を用意する。
これはNULLを返す場合があるってのが本質で、あとは適当でよい。

#3の時だけNULLほかは適当
f <- function(i){
  if(i==3){
    NULL
  } else{
    head(iris,i)
  }
}

以下、この関数を適当にループさせ、その結果をリストに格納する形で使ってみる。

うまくいくケース

まずは何も考えずに使ってみたケース。
想像どおりの動き方&結果だ。

> x <- vector("list", 4)
> for(i in 1:4){
+   x[[i]] <- f(i)
+ }
> x
[[1]]
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa

[[2]]
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa

[[3]]
NULL

[[4]]
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa

うまくいかないケース

気を良くして、上のコードに一行、names(x)という処理を追加すると・・・名前がずれる。

> x <- vector("list", 4)
> names(x) <- paste0("name", 1:4)
> for(i in 1:4){
+   x[[i]] <- f(i)
+ }
> x
$name1
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa

$name2
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa

$name4
NULL

[[4]]
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa

原因と対策

にも書いてあるように、NULLを代入することは要素の削除と同義になっちゃうんでNGという話。
回避するには、この例だと最後に名前を割り当てるようにするか、あるいは汎用的な解決法としてはlapply使えと。
変なのー。