オブジェクトからリストを作成すると、オブジェクトの名前は失われてしまいます。
a <- 1:3
b <- 11:13
c <- 21:23
list(a, b, c)
[[1]]
[1] 1 2 3
[[2]]
[1] 11 12 13
[[3]]
[1] 21 22 23
名前付きのリストにしたければ、list(a = a, b = b, c = c)
のようにやるわけですが、オブジェクト名を2回書く必要があり、あまりスマートではありません。
ところがリストの特殊な形であるデータフレームを作成する関数data.frame
では、明示的に名前を指定しなければ列名としてオブジェクト名が設定されます。
df <- data.frame(a, b, c)
as.list(df)
$a
[1] 1 2 3
$b
[1] 11 12 13
$c
[1] 21 22 23
ということは、何かやり方があるはずです。
単純にやるなら、オブジェクトの名前を取得して、リストの名前にセットしてやれば良いので、例えば次のような関数を定義します。
namedlist1 <- function(...){
setNames(list(...), eval(substitute(alist(...))))
}
namedlist1(a, b, c)
$a
[1] 1 2 3
$b
[1] 11 12 13
$c
[1] 21 22 23
eval(substitute(alist(...)))
の部分は、rlang
パッケージを使ってもう少し簡潔に書くこともできます。
namedlist2 <- function(...){
setNames(list(...), rlang::exprs(...))
}
しかし、この場合は明示的に名前を指定した場合にもオブジェクトの名前が優先して使われてしまいます。
namedlist1(a, hoge = b, c)
$a
[1] 1 2 3
$b
[1] 11 12 13
$c
[1] 21 22 23
リストの2つめの要素は名前がhoge
になってほしいところでした。
ところでこの「明示的に名前を指定した場合にはその名前を使う」という動作もdata.frame
だと実現できています。
as.list(data.frame(a, hoge = b, c))
$a
[1] 1 2 3
$hoge
[1] 11 12 13
$c
[1] 21 22 23
上記のようにdata.frame
をas.list()
でリストに変換する方法は、いつでも使えるわけではありません。data.frame
には要素数が等しいという制約があるためです。
しかしdata.frame
は実際これをやっているわけですから、中身を覗いてそこだけ拝借すれば同じことができるのではないでしょうか?
やってみました。
namedlist3 <- function(...){
object_name <- as.character(eval(substitute(alist(...))))
x <- list(...)
vnames <- names(x)
novnames <- !nzchar(vnames)
if (length(novnames) > 0L){
vnames[novnames] <- object_name[novnames]
} else {
vnames <- object_name
}
setNames(x, vnames)
}
namedlist3(a, hoge = b, c)
$a
[1] 1 2 3
$hoge
[1] 11 12 13
$c
[1] 21 22 23
できました。
要するに名前が設定されていない部分を探してオブジェクト名をセットしているだけなのですが、data.frame
の中ではこういう地道なことが行われていたのでした。