Code Fix

中級

sapply は結果を勝手に行列へ簡略化することがある

sapplyは各要素の計算結果が同じ長さのベクトルであれば、リストではなく行列に自動的にまとめてしまいます。1個の値だけを期待していたコードが、複数要素を返す関数を渡した途端に行列を受け取ることになります。

なぜエラーが出ないのか

出力: 6
(エラーなし)
Rは何も報告しません。文法としても実行としても正しいコードだからです
6
実際の出力 — 期待した結果と食い違っている箇所

このエラーが出る典型パターン

パターン1

 1  nums <- list(c(1, 5), c(2, 8), c(3, 9))
 2  minmax <- function(x) c(min(x), max(x))
 3  result <- sapply(nums, minmax)
                 ^
 4  cat(length(result), "\n")
出力: 6

minmaxが長さ2のベクトルを返すため、sapplyは3要素のリストを2x3の行列に簡略化し、lengthが6になってしまいます。

直し方: sapply を lapply にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  groups <- list(c(10, 20), c(5, 15), c(30, 40))
 2  range2 <- function(x) c(min(x), max(x))
 3  result <- sapply(groups, range2)
                 ^
 4  cat(length(result), "\n")
出力: 6

各要素につき2つの値を返す関数をsapplyに渡すと、結果はリストではなく行列にまとめられてしまいます。

直し方: sapply を lapply にします。

この問題を解いてみる →

パターン3

 1  items <- list(c(3, 9), c(1, 7), c(4, 6))
 2  span <- function(x) c(min(x), max(x))
 3  result <- sapply(items, span)
                 ^
 4  cat(length(result), "\n")
出力: 6

要素ごとに長さ3のリストを期待しているなら、行列への簡略化をしないlapplyに置き換える必要があります。

直し方: sapply を lapply にします。

この問題を解いてみる →

パターン4

 1  batches <- list(c(2, 8), c(6, 12), c(1, 5))
 2  edges <- function(x) c(min(x), max(x))
 3  result <- sapply(batches, edges)
                 ^
 4  cat(length(result), "\n")
出力: 6

sapplyは「関数の戻り値がすべて同じ長さのベクトル」という条件を満たすと自動で行列化します。

直し方: sapply を lapply にします。

この問題を解いてみる →

パターン5

 1  samples <- list(c(7, 11), c(2, 9), c(0, 4))
 2  bounds <- function(x) c(min(x), max(x))
 3  result <- sapply(samples, bounds)
                 ^
 4  cat(length(result), "\n")
出力: 6

リストのまま3要素として扱いたい場合は、形を変えずに結果を返すlapplyを使う必要があります。

直し方: sapply を lapply にします。

この問題を解いてみる →

よくある誤解

「sapplyは常にベクトルか単純なリストを返すはず」という思い込みは誤りです。関数が長さ2以上のベクトルを返す場合、sapplyは結果を行列に変換します。形が変わっても壊れないコードにしたい場合はlapplyかvapplyを使う必要があります。

実務での勘所

sapplyのこの自動的な簡略化は、便利さを優先した既定の挙動です。多くの場面では、結果が単純なベクトルや行列にまとまってくれたほうが後続の処理が楽になるため、Rはこの変換を既定で行います。この挙動を保ったまま安全性だけを高めたい場合、sapply(x, f, simplify = FALSE)のようにsimplify引数にFALSEを渡せば、lapplyに乗り換えなくてもsapplyのまま常にリストを返すようにできます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)