R 跟一般的程式語言一樣有許多流程控制與迴圈的語法,讓程式依照設計者的邏輯逐步執行對應的動作。
流程控制
有時候在處理資料時,我們會希望程式依照某些條件來判斷應該要執行什麼動作,而不是很單純的將指令逐行執行,R 提供了許多流程控制的語法,可藉由指定的條件來判斷程式執行的流程。
if 與 else 判斷式
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!
如果要讓 if 為 TRUE 之後,可以執行多行指令,就要使用大括號將要執行的程式區塊包起來:
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!");
}
如果需要判斷多重的條件,可以將 if 與 else 重複配合使用:
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!
下面這個是一個 if 與 else 的簡潔寫法範例:
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 判斷式
if 與 else 的配合之下,雖然可以處理多重條件的判斷,但是這樣的寫法在條件比較多的時候,會顯得比較雜亂:
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 中可以使用的迴圈控制方式主要有 repeat、while 與 for 這三種,雖然大部分的迴圈計算可以使用 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 迴圈中,同樣可以使用 break 與 next:
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 迴圈,改以向量化運算的方式代替。