R 跟一般的程式語言一樣有許多流程控制與迴圈的語法,讓程式依照設計者的邏輯逐步執行對應的動作。

流程控制

有時候在處理資料時,我們會希望程式依照某些條件來判斷應該要執行什麼動作,而不是很單純的將指令逐行執行,R 提供了許多流程控制的語法,可藉由指定的條件來判斷程式執行的流程。

ifelse 判斷式

if 判斷式是最簡單、也是最常被使用一種條件判斷語法,他可以判斷一個輸入的邏輯值,如果輸入的值為 TRUE,則執行對應的程式碼,反之該值若為 FALSE,則不執行:

if(TRUE) message("It was true!")
It was true!
if(FALSE) message("It wasn't true!")

通常我們會使用 if 來判斷指定的變數或是運算式,依據該變數或運算結果來決定是否執行後續的動作:

bool.val <- runif(1) > 0.5
if (bool.val) message("Bingo!");
Bingo!

這裡我們使用 runif 函數產生一個 unifrom 分配的隨機變數,若其值大於 0.5 則輸出一行訊息。也可以把兩行合併,這樣可讓程式碼更簡潔:

if (runif(1) > 0.5) message("Bingo!");
Bingo!

如果要讓 ifTRUE 之後,可以執行多行指令,就要使用大括號將要執行的程式區塊包起來:

if (runif(1) > 0.5) {
  message("Bingo!");
  message("Ha!");
}

當判斷式之後的程式碼只有一行時,可將大括號省略,但在撰寫 R 的指令稿時,為了讓程式碼更容易被閱讀,建議可以將大括號都保留下來。

if 判斷式只會在條件判別為 TRUE 時執行對應的程式,若要在條件判斷為 FALSE 時也可以執行另一個動作,可以加上 else 判斷式:

if (runif(1) > 0.5) {
  message("Bingo!");
} else
{
  message("Fail!");
}
Fail!

在使用 else 時要注意一點,他必須跟 if 的結尾大括號放在同一行,如果寫成這樣就會造成程式解析上的錯誤:

if (runif(1) > 0.5) {
  message("Bingo!");
}
else
{
  message("Fail!");
}

如果需要判斷多重的條件,可以將 ifelse 重複配合使用:

x <- runif(1)
if (x > 0.75) {
  message("Perfect!");
} else if (x > 0.5)
{
  message("Good!");
} else if (x > 0.25)
{
  message("Bad!");
} else
{
  message("Worst!");
}
Perfect!

下面這個是一個 ifelse 的簡潔寫法範例:

sample <- if(runif(1) > 0.5) "Good" else "Bad"
sample
[1] "Bad"

這裡的 sample 有一半的機率會是 "Good",另一半的機率會是 "Bad"

缺失值的判斷

if 如果遇到缺失值時,會產生錯誤:

if(NA) message("Who knows if it was true?")
Error in if (NA) message("Who knows if it was true?") :
  需要 TRUE/FALSE 值的地方有缺值

如果要處理缺失值,可以配合 is.na 來使用:

if(is.na(NA)) message("The value is missing!")
The value is missing!

向量化 if 判斷式

一般的 if 判斷式沒辦法直接處理布林向量的判斷,如果將布林向量放進 if 判斷式中,會以第一個元素為準,其餘的元素則會捨棄不用:

if(c(TRUE, FALSE)) message("OK.")
OK.
Warning message:
In if (c(TRUE, FALSE)) message("OK.") :
  條件的長度 > 1,因此只能用其第一元素

若需要一次對一整個布林向量作判斷,可以改用 ifelse 這個函數,其第一個參數為布林向量,第二個參數是條件判斷為 TRUE 時要執行的運算,第二個參數則是條件判斷為 FALSE 時要執行的運算:

ifelse(c(TRUE, FALSE, TRUE), "Good", "Bad")
[1] "Good" "Bad"  "Good"

這是另外一個例子:

x <- c(3:-2)
sqrt(ifelse(x >= , x, NA))
[1] 1.732051 1.414214 1.000000 0.000000
[5]       NA       NA

若第二個與第三個參數為向量時,ifelse 就會依照第一個參數的布林向量來取出對應的值:

idx <- rep(c(TRUE, FALSE), 6)
ifelse(idx, 1:3, -1:-6)
[1]  1 -2  3 -4  2 -6  1 -2  3 -4  2 -6

switch 判斷式

ifelse 的配合之下,雖然可以處理多重條件的判斷,但是這樣的寫法在條件比較多的時候,會顯得比較雜亂:

x <- "mean"
y <- 1:10
if ( x == "mean" ) {
  mean(y)
} else if ( x == "sd" ) {
  sd(y)
} else if ( x == "median" ) {
  median(y)
} else if ( x == "sum" ) {
  sum(y)
}
[1] 5.5

這時候可以改用 switch 的寫法:

switch(x,
  mean = mean(y),
  sd = sd(y),
  median = median(y),
  sum = sum(y))
[1] 5.5

若要處理所有條件都不符合的預設狀況,可以加入一個沒有名稱的參數,這樣在所有條件都不符合時,預設就會傳回該參數值:

z <- "nothing"
switch(z,
  mean = mean(y),
  sd = sd(y),
  median = median(y),
  sum = sum(y),
  99)
[1] 99

如果要執行多行運算式,就要使用大括號包起來:

z <- "custom"
switch(z,
  mean = mean(y),
  sd = sd(y),
  median = median(y),
  sum = sum(y),
  custom = {
    y2 <- y * 1.2 + pi / 2
    prod(sin(y2))
  })
[1] -0.00102691

如果 switch 第一個參數放置的是數值,後續的選項可以不需要指定名稱,這種狀況 switch 會直接取出該數值所對應的參數:

switch(
  3,
  "first",
  "second",
  "third",
  "fourth"
)
[1] "third"

上面這樣的用法就沒有預設的選項可以使用,如果想要使用數值來當作判斷的依據,但是又需要預設選項時,可以採用這樣的做法:

switch(
  as.character(328),
  "328" = "the anser 328",
  "default anser"
)
[1] "the anser 328"

迴圈控制

在 R 中可以使用的迴圈控制方式主要有 repeatwhilefor 這三種,雖然大部分的迴圈計算可以使用 R 的向量化運算來處理,不過在重複執行某區段的程式碼時,還是必須使用到這裡的迴圈控制結構。

repeat 迴圈

repeat 迴圈是最簡單的迴圈控制結構,它會讓 R 不斷得執行指定的程式碼,直到遇到 break 中斷迴圈的執行為止:

x <- 0
repeat {
  message("x = ", x)
  x <- x + 1
  if (x == 5) break
}
x = 0
x = 1
x = 2
x = 3
x = 4

當 R 在執行程式時若遇到 break 會直接跳出整個迴圈的執行,若只想要讓 R 跳過當次的迭代,執行下一次的重複動作,可以使用 next

x <- 0
repeat {
  x <- x + 1
  if (x %% 2 == 0) next
  message("x = ", x)
  if (x > 7) break
}
x = 1
x = 3
x = 5
x = 7
x = 9

上面這樣的程式碼會跳過所有 x 是偶數的迭代,只輸出奇數的 x 值。

while 迴圈

while 迴圈類似 repeat,不過它會先檢查指定的條件,在條件為 TRUE 的情況下才會執行迴圈的內容:

x <- 0
while (x != 5) {
  message("x = ", x)
  x <- x + 1
}
x = 0
x = 1
x = 2
x = 3
x = 4

while 迴圈中,同樣可以使用 breaknext

x <- 0
while ( x < 100 ) {
  x <- x + 1
  if (x %% 2 == 0) next
  message("x = ", x)
  if (x > 7) break
}
x = 1
x = 3
x = 5
x = 7
x = 9

任何的 repeat 迴圈都可以很簡單的改寫成 while 迴圈,反之亦然,通常如果迴圈至少要執行一次的狀況,就可以使用 repeat,如果迴圈可能會因為條件判斷不符合而完全不執行,則可使用 while,這樣可讓程式碼更容易被閱讀。

for 迴圈

for 迴圈主要是用在程式執行前就已經知道迭代次數的情況,它在執行時會將輸入向量中的每個元素值逐一指定給迭代變數,然後重複執行迴圈的內容:

for ( x in 0:4 ) {
  message("x = ", x)
}
x = 0
x = 1
x = 2
x = 3
x = 4

如果迴圈的內容只有一行運算式,也可以省略大括號:

for ( x in 0:4 ) message("x = ", x)
x = 0
x = 1
x = 2
x = 3
x = 4

for 迴圈不只可以處理整數,R 任何的向量都可以使用 for 迴圈來處理:

colors <- c("red", "blue", "yellow")
for (c in colors) {
  message("The color is ", c)
}
The color is red
The color is blue
The color is yellow

雖然 for 迴圈的寫法跟 R 的向量化運算類似,但實際上 R 向量化運算的效能跟 C 語言層級的迴圈相當,而 R 的 for 迴圈則不同,其執行效率上會差很多,所以在執行大量運算時,應盡可能避免使用 for 迴圈,改以向量化運算的方式代替。