if 文
Rの if 文は、条件が TRUE のときだけコードのブロックを実行します。条件はかっこの中、コードは波かっこの中に置き、任意の else がそれ以外の場合を担当します。
条件 score >= 60 は単一の TRUE に評価されるので、最初の分岐が実行されます。こうした条件を組み立てる材料が比較演算子と論理演算子——==、>=、&&、%in% など——です。
結果が3通り以上になる場合は else if で条件を連鎖させます。Rは最初の条件から順にたどり、条件が TRUE になった最初の分岐を実行して、残りを飛ばします。
順番が重要です。85 >= 80 が 85 >= 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 を生むからです。x が NA なら x > 10 は NA になり、その条件を渡された 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 if か cut() を使いましょう。
この記事のまとめ
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が返ります。