rでのtry関数によるエラー処理
TRANSCRIPT
Rでのtry関数によるエラー処理
作成: 2015年3月1日(日)
~ 異常終了の防止方法 ~
こんなことありませんか?昔々あるところに、複数のデータセットに時間
のかかる同じ処理をしなければならないセンサスくんがいました。賢いセンサスくんは、一部のデータを使って処理コードを作り、さらにforループでデータセットを入れ替える処理を加えて自動化し、帰宅間際にPCに投げました。センサスくんが帰宅しても、PCが夜なべで働
き、翌朝出勤時までに仕事が終わっているはずでしたが、その翌朝・・・
あ~っ、途中でabort(異常終了)してる・・・
何が起きるのか確認してみましょう
これは正常終了するコードの例です
rm(list=ls(all=TRUE)) # ワークスペースのクリア
data(iris) # 組み込みのあやめデータ呼出し
aa <- as.matrix(iris[,1:4]) # 量的変数だけ行列属性に変換してaaへ
# 品種を示す第5変数の内容別にデータフレームを作成
dat1 <- aa[which(iris[,5]=="setosa"),]
dat2 <- aa[which(iris[,5]=="versicolor"),]
dat3 <- aa[which(iris[,5]=="virginica"),]
listD <- ls(pattern="d") # ワークスペース内のdで始まる変数名を取得
listD # 中身表示
# listDの数だけループをさせて、dat1~3の共分散行列を表示させる
for (i in 1: length(listD)) {
x <- get(listD[i])
cv <- cov(x) # データの共分散行列を算出
print(solve(cv)) # 共分散行列の逆行列を画面表示
}
逆行列計算がabortするとき先の例では、共分散行列は全て正則なので、
逆行列の計算は正常終了します。続けて、以下のコードにより、dat2の逆行
列が計算できなくなるよう、特定の行の要素だけ有効桁数を超える小さな数に変更してみます。
cv <- cov(dat2)cv[1,] <- cv[1,] / 10**16solve(cv) # ここでエラーが起きる
> cv <- cov(dat2)> cv[1,] <- cv[1,] / 10**16> solve(cv)# エラーが起きる。以下にエラー solve.default(cv) : システムは数値的に特異です:条件数の逆数 = 1.4947e-17
追加コード
コンソール出力
forループ内でこれが起きると、その時点で処理が終了します。複数データ
をforで回していれば、後ろのデータは未処理のまま。
エラーが起きても後のデータを処理して欲しいのですが・・・
そんなあなたにtry関数!
cv <- cov(dat1)cv[1,] <- cv[1,] / 10**16XX <- try(solve(cv)) # ここでtry関数を使うXX # XXにエラーメッセージが入ることに注意!
> cv <- cov(dat1)> cv[1,] <- cv[1,] / 10**16> XX <- try(solve(cv))# ここでtry関数を使うError in solve.default(cv) : システムは数値的に特異です: 条件数の逆数 = 1.4947e-17
> XX# XXにエラーメッセージが入ることに注意![1] "Error in solve.default(cv) : \n システムは数値的に特異です: 条件数の逆数 = 1.4947e-17 \n"attr(,"class")[1] "try-error"attr(,"condition")<simpleError in solve.default(cv): システムは数値的に特異です: 条件数の逆数 = 1.4947e-17 >
追加コード
コンソール出力
エラーが起きてもループの途中で異常終了はせず、後ろの処理が続行されます
実際に試してみましょう
結果の出力は次のスライドへ
# 数値をいじるために一旦共分散行列を変数cv1に格納する
cv1 <- array(NA, c(4,4,3))
for (i in 1: length(listD)) {
x <- get(listD[i])
cv1[,,i] <- cov(x) # 配列cv1に保存
}
cv1[1,,2] <- cv[1,,2] / 10**16 # 二つ目の共分散行列の要素1つを小さく
# try関数なしの場合for (i in 1: length(listD)) {
print(solve(cv1[,,i])) # 共分散行列の逆行列を画面表示
}
# try関数を使う場合for (i in 1: length(listD)) {
print(try(solve(cv1[,,i]))) # 共分散行列の逆行列を画面表示
}
追加コード
結果のコンソール出力> # try関数なしの場合> for (i in 1: length(listD)) {+ print(solve(cv1[,,i]))# 共分散行列の逆行列を画面表示+ }
[,1] [,2] [,3] [,4][1,] 18.943439 -12.404826 -4.500207 -4.776127[2,] -12.404826 15.570540 1.111079 -2.104098[3,] -4.500207 1.111079 38.776204 -17.935035[4,] -4.776127 -2.104098 -17.935035 106.045906以下にエラー solve.default(cv1[, , i]) : システムは数値的に特異です: 条件数の逆数 = 0
>
> # try関数を使う場合> for (i in 1: length(listD)) {+ print(try(solve(cv1[,,i])))# 共分散行列の逆行列を画面表示+ }
[,1] [,2] [,3] [,4][1,] 18.943439 -12.404826 -4.500207 -4.776127[2,] -12.404826 15.570540 1.111079 -2.104098[3,] -4.500207 1.111079 38.776204 -17.935035[4,] -4.776127 -2.104098 -17.935035 106.045906Error in solve.default(cv1[, , i]) : システムは数値的に特異です: 条件数の逆数 = 0
[1] "Error in solve.default(cv1[, , i]) : \n システムは数値的に特異です: 条件数の逆数 = 0 \n"attr(,"class")[1] "try-error"attr(,"condition")<simpleError in solve.default(cv1[, , i]): システムは数値的に特異です: 条件数の逆数 = 0 >
[,1] [,2] [,3] [,4][1,] 10.533867 -3.479726 -9.960146 1.788152[2,] -3.479726 15.875442 1.102689 -8.472851[3,] -9.960146 1.102689 13.405821 -2.890918[4,] 1.788152 -8.472851 -2.890918 19.314050>
try関数で問題の起きる処理をくるむと、i=2でエラーが起きても、i=3での逆行列が計算されます
おわり
8