Menu

Rのif else:if・else if・ifelse()・switch()

Rの条件分岐:if/else if/else の書き方、スクリプトを壊す波かっこの落とし穴、ベクトル全体に効く ifelse()、そして多分岐のための switch() を解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

if 文

Rの if 文は、条件が TRUE のときだけコードのブロックを実行します。条件はかっこの中、コードは波かっこの中に置き、任意の else がそれ以外の場合を担当します。

条件 score >= 60 は単一の TRUE に評価されるので、最初の分岐が実行されます。こうした条件を組み立てる材料が比較演算子と論理演算子——==>=&&%in% など——です。

結果が3通り以上になる場合は else if で条件を連鎖させます。Rは最初の条件から順にたどり、条件が TRUE になった最初の分岐を実行して、残りを飛ばします。

順番が重要です。85 >= 8085 >= 70 より先に判定されるので、評価は "B" になります。判定を逆順に並べていたら、70より上のすべてのスコアが "C" になってしまいます。

波かっこの落とし穴:else をどこに置くか

これはほぼ全員が最初のRスクリプトで踏むエラーです。スクリプト(あるいは Rscript で実行するもの)では、else は直前の分岐の閉じ波かっこと同じ行になければなりません。次は失敗します。

if (score >= 60) {
  print("You passed")
}
else {              # Error: unexpected 'else'
  print("Try again")
}

理由はこうです。Rは文ごとに解析します。行の先頭にある } に到達した時点で if 文は文法的に完結しており、Rはその場で評価してしまいます。次の行の else は新しい文の始まりとして届きますが、文が else で始まることはできません。修正はこのページ全体で使っているパターンです。

if (score >= 60) {
  print("You passed")
} else {            # brace and else on the same line
  print("Try again")
}

関数本体や外側の波かっこの内側では、この厳密なルールは緩みます(Rは外側の波かっこが閉じるまで読み続けます)。しかしそれに頼ってはいけません。} else { のスタイルはどこでも機能し、Rのスタイルガイドが定めているものでもあります。

条件は1つの TRUE か FALSE でなければならない

if() はちょうど1つの論理値を要求します。これに違反するケースが2つあり、どちらもエラーになります。

if (NA) print("hi")
# Error: missing value where TRUE/FALSE needed

if (c(TRUE, FALSE)) print("hi")
# Error: the condition has length > 1

NA のケースは実務で重要です。欠損データとの比較は NA生むからです。xNA なら x > 10NA になり、その条件を渡された if はスクリプトを止めます。防御的なパターンは短絡評価の && を使います。

!is.na(x)FALSE なので x > 10 の部分は一度も評価されず、NA が条件に到達しません。NA が比較を通じてどう伝播するかは欠損値で扱います。

長さが1より大きいというエラー(R 4.2以降で強制。それ以前のRは最初の要素を静かに使っていました)は、たいてい本当は別のものを意図していたことを意味します。1つでも一致すれば十分なら any(v > 5)、すべての要素が満たすべきなら all(v > 5)、要素ごとの答えが欲しいなら ifelse() です。次のセクションで扱います。

ifelse():ベクトル化された if else

if は一度だけ判定します。ifelse(test, yes, no)ベクトルのすべての要素について判定し、同じ長さのベクトルを返します。

temps の各要素は独立に判定され、"hot" "mild" "mild" "hot" "mild" になります。既存のベクトルやデータフレームの列から新しいものを導くための道具で、ループは不要です。

if と違い、ifelse() は欠損値をうまく扱います。テストの中の NA は、エラーではなく結果の中の NA になります。

3分割したいときは ifelse() を入れ子にできますし、この深さならまだ読めます。

ただしそこまでにしましょう。ifelse() の入れ子が3段以上になると、かっこの対応を追うパズルになります。そうなったら(数値の範囲には)cut()、あるいは各条件を1行ずつ並べられる dplyr::case_when() に手を伸ばしましょう。

多分岐のための switch()

範囲を判定するのではなく、1つの値を既知の名前の一覧と照合するとき、長い else if の連鎖は不格好な道具です。switch() はそれを直接表現します。

知っておくべき挙動が3つあります。

  • 照合switch() は文字列の値を引数名と照合し、一致した結果を返します。
  • フォールスルー:右辺がの名前(sat = ,)は次のケースの結果へ落ちていきます。"sat""sun" が繰り返しなしに "weekend" を共有できるのはこの仕組みです。
  • 既定値:最後の名前なし引数("weekday")は、何にも一致しなかったときに返されます。既定値がないと、一致しない値に対して switch() は不可視の NULL を返します。関数が静かに何も返さないことになり、混乱を招くバグです。必ず既定値を用意しましょう(あるいは明示的に stop() させましょう)。

switch() はモードやメソッドの引数で分岐する関数の中で威力を発揮します。数値のしきい値に基づくものは else ifcut() を使いましょう。

この記事のまとめ

  • if (condition) { } else { } — 条件はかっこの中、実行されるのは1つの分岐。else if で連鎖し、最初に一致したものが勝ちます。
  • スクリプトでは } else { を1行に書きます。行頭の else は構文エラーです。
  • if() はちょうど1つの TRUE/FALSE を必要とします。NA はエラー、長いベクトルもエラーです(R 4.2以降)。!is.na(x) && で守るか、any()/all() で縮めましょう。
  • ifelse(test, yes, no) がベクトル化された版です。新しいベクトルの計算に使い、入れ子は2段までに。
  • switch() は名前による多分岐を扱い、空のケースでフォールスルーできます。必ず既定値を与えましょう。

次は、同じことを何度も行う方法です。for ループと、ループが適切な道具である(あるいはそうでない)場面を扱います。

よくある質問

Rでif else文はどう書きますか?

if (condition) { ... } else { ... } と書きます。条件はかっこの中、各分岐は波かっこの中です。さらに条件を続けるには else if (condition) で連鎖させます。スクリプトでは else を直前の分岐の閉じ波かっこと同じ行(} else {)に置かなければならず、そうしないとRは unexpected 'else' を報告します。

Rのifとifelse()の違いは何ですか?

if は制御構文で、ちょうど1つのTRUE/FALSEを判定し、片方の分岐のコードを実行します。ifelse(test, yes, no) はベクトル化された関数で、ベクトルのすべての要素を一度に判定し、結果のベクトルを返します。プログラムの動作を決めるには if、既存のベクトルから新しい列やベクトルを計算するには ifelse() を使いましょう。

Rが「the condition has length > 1」と言うのはなぜ?

単一のTRUEかFALSEを必要とする if() にベクトル全体を渡したからです。R 4.2以降、これは最初の要素を静かに使うのではなくエラーになります。any(x > 5)all(x > 5) でベクトルを1つの値に縮めるか、要素ごとの結果が欲しいならベクトル化された ifelse() に切り替えて修正しましょう。

Rのswitch()はどう動きますか?

switch(value, name1 = result1, name2 = result2, default) は文字列の値を引数名と照合し、一致した結果を返します。右辺が空の名前は次の結果へ落ちていくので、複数のケースで1つの結果を共有できます。最後の名前なし引数が既定値として働き、それがない場合、一致しない値に対しては不可視のNULLが返ります。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める