リストに関する Tips 大全
リストに関する操作は、R をマスターする基本です。関連する Tips を脈絡なくできるだけ集めたいと思います。お気づきの正統派・裏技テクニックを お寄せください。一部重複はむしろ好ましいと思います。
関数引数にリストとその成分名を与える際に起こりえるトラブル
> x <- list(a=1:5, b=rnorm(5)) > x$a # x[[1]] でも良い [1] 1 2 3 4 5 > x$"a" # 成分名の指定を「文字列」で行なう [1] 1 2 3 4 5 > plot(x$a, x$b) # 問題なし。plot(x[[1]],x[[2]], plot(x$"a", x$"b") でも当然良い
> test1 <- function(x ,c1, c2) plot(x$c1, x$c2)
> test1(x,a,b) # エラー
Error in xy.coords(x, y, xlabel, ylabel, log) :
x and y lengths differ
> test1(x, "a", "b") # エラー
Error in xy.coords(x, y, xlabel, ylabel, log) :
x and y lengths differ
> test2 <- function(x ,c1, c2) plot(x[[c1]], x[[c2]]) > test2(x,a,b) # エラー Error in plot(x[[c1]], x[[c2]]) : Object "a" not found > test2(x,"a","b") # 問題なし(成分名を文字列で指定している) > test2(x,1,2) # 問題なし
注:ある試行でエラーが起きても残りを続行するために try 関数を使用(help(try)参照)
> doit <- function() {mean(rnorm(30))} # 30個の正規乱数の平均値を求める関数
> res <- lapply(1:3, function(i) try(doit()))
> res
[[1]]
[1] -0.359225966054957
[[2]]
[1] 0.0249291122975083
[[3]]
[1] -0.0972657631255351
リストは属性の異なる任意のオブジェクトを一括保持でき、添字で操作できるため便利な データタイプである。リスト成分は二重かぎ括弧演算子 , でアクセスする。名前付き 成分の場合には [["name"]] もしくは $ 演算子でアクセスする。Lst1 がベクトル・配列 の時は Lst1[1,2] 等でその要素にアクセスできる
一重リストの初期化は
> Lst <- as.list(NA) # リストオブジェクト Lst の初期化
> Lst
[[1]]
[1] NA
> Lst[[1]] <- c(1,2,3) # リストの第一成分(ベクトル)
> Lst
[[1]]
[1] 1 2 3
> Lst[[3]] <- matrix(1:4,ncol=2) # リストの第3成分(行列)
> Lst
[[1]]
[1] 1 2 3
[[2]] # 自動的に途中の成分が生成される
NULL
[[3]]
[,1] [,2]
[1,] 1 3
[2,] 2 4
> Lst[[2]] <- "second character element" # 空の第二成分に文字列を代入
> Lst
[[1]]
[1] 1 2 3
[[2]]
[1] "second character element"
[[3]]
[,1] [,2]
[1,] 1 3
[2,] 2 4
最初から要素数を指定して空のリストを作るには
> Lst <- as.list(rep(NA,2)) # 要素数2 > Lst [[1]] [1] NA [[2]] [1] NA
リストを具体的要素を与えて構成するには list 関数を使う。各要素には名前タグを 付けることが出来、要素番号以外に名前タグで操作(二重かぎ括弧以外に $ 演算子を 使える)できる
> Lst <- list(sex=c(1,2),name=c("Tarou","Hanako"))
> Lst
$sex
[1] 1 2
$name
[1] "Tarou" "Hanako"
> Lst[[1]]
[1] 1 2
> Lst[["sex"]]
[1] 1 2
> Lst$sex
[1] 1 2
二重リスト(リストの各成分がまたリスト)を初期化するには
> Lst <- as.list(c(NA,NA)) > Lst[[1]] <- as.list(c(NA,NA)) > Lst[[2]] <- as.list(c(NA,NA)) > Lst [[1]] [[1]][[1]] [1] NA [[1]][[2]] [1] NA [[2]] [[2]][[1]] [1] NA [[2]][[2]] [1] NA > Lst[[1]][[2]] <- c(1,3) > Lst[[1]] [[1]] [1] NA [[2]] [1] 1 3 > Lst[[1]][[2]] # Lst[[1,2]] は不可 [1] 1 3
for ループのループ範囲にはベクトル以外にリストを与えることが出来る 例えば2重ループ
> for (i in 1:n) { for (j in 1:n) f(i,j)}
は
> rangelist <- multiplerange(1:n) > for (i in rangelist) f(i[1],i[2])
と出来る(同じ多重範囲のループが瀕出する際は、コードが簡潔になり、実行時間も 多少早くなる)。ここで multiplerange(1:n) は全ての組合せ c(i,j), 1<=i,j<=n, からなるリストを作る次の関数:
multiplerange <- function(range) {
mplerange <- as.list(rep(NA,length(range)^2))
ct <- 0
for (i1 in range) { for (i2 in range) {
mplerange[[ct<-ct+1]] <- c(i1,i2)
}}
return(mplerange)
}
一つの関数から複数の値を返すにはそれらをリストとして返り値 とすれば良い。リストの各成分には元の変数名が名前タグとして自動的に付加される
# この暗黙のリスト返り値機能は現在はエラーとされる!!!
> test <- function(x){y=log(x);z=sin(x);return(x,y,z)} # 暗黙のリスト返り値
> test(1:3)
$x
[1] 1 2 3
$y
[1] 0.0000000 0.6931472 1.0986123
$z
[1] 0.8414710 0.9092974 0.1411200
名前タグを自前で指定するには明示的に名前付きリストを返り値にする
> test <- function(x){y=log(x);z=sin(x);return(list(value=x,log=y,sin=z))}
> test(1:3)
$value
[1] 1 2 3
$log
[1] 0.0000000 0.6931472 1.0986123
$sin
[1] 0.8414710 0.9092974 0.1411200
x[i] <- list(NULL)
を用いると, リスト x の要素 i に NULL をセットすることができます. 名前付きの要素についても同様。x[i] あるいは x[ [i] ] に NULL をセットすると, リストから該当する要素を削除してしまう.
リスト x には成分名を表す名前タグ文字ベクトル names(x) が付随する。names(x) の該当成分が空の文字列 "" であることが、すなわち名前タグが無いということ。名前タグの追加、変更、除去はしたがって文字ベクトル names(x) を処理すれば良い。
> x <- list(1:3,matrix(1:4,c(2,2))) # 名前タグの無いリスト
> x
[[1]]
[1] 1 2 3
[[2]]
[,1] [,2]
[1,] 1 3
[2,] 2 4
> names(x) <- c("vector","matrix") # 名前タグを後から付ける
> x
$vector
[1] 1 2 3
$matrix
[,1] [,2]
[1,] 1 3
[2,] 2 4
> x$vector
[1] 1 2 3
> x$matrix
[,1] [,2]
[1,] 1 3
[2,] 2 4
> names(x) <- c("vector","") # 第二成分の名前タグを取り除く
> x
$vector
[1] 1 2 3
[[2]]
[,1] [,2]
[1,] 1 3
[2,] 2 4
> names(x)<-c("","") # タグ無しのリストに戻す
> x
[[1]]
[1] 1 2 3
[[2]]
[,1] [,2]
[1,] 1 3
[2,] 2 4
> names(x)[[1]] <- "vector" # 一部だけ指定する
これはリスト以外にも使えますから、知っていると重宝します。
> x=list(vec=1:3, mat=matrix(1:4,c(2,2)),char=c("a","b"))
> str(x)
List of 3
$ vec : int [1:3] 1 2 3
$ mat : int [1:2, 1:2] 1 2 3 4
$ char: chr [1:2] "a" "b"
lapply() 関数を用いて複数回試行の結果をリストに記録したものを、ベクトルに変換する際便利。逆の操作は as.list()
> x <- list(1,2,3) > x [[1]] [1] 1 [[2]] [1] 2 [[3]] [1] 3 > as.numeric(x) [1] 1 2 3
unlist(options()) unlist(options(), use.names=FALSE)
l.ex <- list(a = list(1:5, LETTERS[1:5]), b = "Z", c = NA) unlist(l.ex, recursive = FALSE) unlist(l.ex, recursive = TRUE)
l1 <- list(a="a", b=2, c=pi+2i)
unlist(l1) # a character vector
l2 <- list(a="a", b=as.name("b"), c=pi+2i)
unlist(l2) # remains a list
> data(iris) # データフレーム iris > hiris = head(iris) # 長過ぎるので簡略化 > L <- list(hiris, hiris) # 2つのデータフレームからなるリスト > lapply(L, "[", 1, 1) # 成分データフレームの第一行第一列をリストとして取り出し [[1]] [1] 5.1 [[2]] [1] 5.1
> lapply(L, "[[", 1) # 成分データフレームの第一列をリストとして取り出し [[1]] [1] 5.1 4.9 4.7 4.6 5.0 5.4 [[2]] [1] 5.1 4.9 4.7 4.6 5.0 5.4 > lapply(L, "[", 1) # 成分データフレームの第一行をデータフレームのリストとして取り出し [[1]] Sepal.Length 1 5.1 2 4.9 3 4.7 4 4.6 5 5.0 6 5.4 [[2]] Sepal.Length 1 5.1 2 4.9 3 4.7 4 4.6 5 5.0 6 5.4
> lapply(L, function(x) x[1,]) # 成分データフレームの第一行をリストとして取り出し [[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 # 成分データフレームの第一行と第一列をからなるリストを、リストとして取り出し > lapply(L, function(x) list(x[1,],x[,1])) # [[1]] [[1]][[1]] Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 5.1 3.5 1.4 0.2 setosa [[1]][[2]] [1] 5.1 4.9 4.7 4.6 5.0 5.4 [[2]] [[2]][[1]] Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 5.1 3.5 1.4 0.2 setosa [[2]][[2]] [1] 5.1 4.9 4.7 4.6 5.0 5.4
リストの何番目かを意識しないで新規成分を加えたり、取り出したりするのに便利。
> lst <- list() > element <- "a" > lst[[element]] <- 1:5 > element <- "b" > lst[[element]] <- letters[1:5] > lst $a [1] 1 2 3 4 5 $b [1] "a" "b" "c" "d" "e"
> lst <- list()
> nm <- c("dude", "chick")
> lst[[nm[1]]] <- 123
> lst[[nm[2]]] <- 456
> lst
$dude
[1] 123
$chick
[1] 456
リストも次元属性を与えれば行列として操作できる
> x <- list(1,2,3,4)
> x
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
> dim(x) <- c(2,2) # 2x2 行列にする
> x
[,1] [,2]
[1,] 1 3
[2,] 2 4
> x[1,2] # 成分は添字操作で取り出せる
[[1]]
[1] 3
> is.list(x[1,2]) # 結果はリスト
[1] TRUE
> x[[1,2]] # 二重鈎括弧演算子でもOK
[1] 3
> is.list(x[[1,2]]) # ただし結果は成分そのものでリストではない
[1] FALSE
または do.call を使う
> L <- list(a=rnorm(5), b=runif(5))
> do.call("rbind", L) # 行列化
[,1] [,2] [,3] [,4] [,5]
a -0.2610772 0.5175920 -0.7930714 -0.5240486 -1.527911
b 0.1986414 0.7121043 0.8124055 0.3421709 0.738326
> do.call("cbind", L) # 行列化
a b
[1,] -0.2610772 0.1986414
[2,] 0.5175920 0.7121043
[3,] -0.7930714 0.8124055
[4,] -0.5240486 0.3421709
[5,] -1.5279110 0.7383260
もっと,変な使い方
> L <- list(a=rnorm(5), b=runif(5))
> t(sapply(L, "+"))
[,1] [,2] [,3] [,4] [,5]
a -0.8160980 -0.04252811 -0.06028301 1.0007900 -0.712706536
b 0.7652187 0.31091773 0.39100277 0.3420776 0.002987919
> sapply(L, "+")
a b
[1,] -0.81609801 0.765218671
[2,] -0.04252811 0.310917726
[3,] -0.06028301 0.391002772
[4,] 1.00078997 0.342077593
[5,] -0.71270654 0.002987919