關卡 1

這門課程要介紹R語言的迴圈,以及常見的應用案例。

關卡 2

迴圈是一種寫程式時很泛用的技巧,可以幫助我們用簡短的程式碼來執行重複的動作。

關卡 3

在做機器學習時,有些演算法是透過不停的重複做一樣的計算,而每次計算都能把學習的結果再改善一些。這類的動作就很適合用迴圈實做。

關卡 4

讓我們用一個數學上的小應用來練習。假設我們要找出一個數x,使得f(x)=x^2的值最小。雖然我們知道這個問題的答案是x=0,但是這裡讓我們用數值方法來逼近答案。

關卡 5

請同學先把數字3存入變數x0

x0 <- 3

關卡 6

先讓我們輸入x0^2,計算f(x0)的結果。

x0^2

關卡 7

接著,我們把x0-0.2*x0存到變數x1

x1 <- x0 - 0.2 * x0

關卡 8

請同學計算f(x1)的值。

x1^2

關卡 9

請問同學,f(x0)和f(x1),何者較小?

f(x1)

關卡 10

事實上,數學可以證明如果我們把x1-0.2*x1存到x2x2-0.2*x2存到x3…以此類推,x1、x2、x3…就會越來越靠近最終的答案,也就是0。讓我們用迴圈來驗證這件事。

關卡 11

首先,假定我們要重複算99次。請同學先建立一個長度為100的數值向量,並且把它存到變數x之中。

x <- numeric(100)

關卡 12

請把3存到x的第一個element

x[1] <- 3

關卡 13

請把x[1]-0.2*x[1]存到x的第二個element

x[2] <- x[1] - 0.2 * x[1]

關卡 14

同學可能已經瞭解,類似x[1]-0.2*x[1]的運算會一直重複。差別只在於x[1]-0.2*x[1]中的x[1]需要做改變。

關卡 15

其實x[1]在這裡,可以看成是x[1]-0.2*x[1]的運算的輸入位置,是x的第一個element。理解了這一點之後,為了讓R可以在改變x[1]的情形下重複x[1]-0.2*x[1]這樣的運算,我們可以利用一個變數i來代表現在要運算的位置。請同學把1存入變數i

i <- 1

關卡 16

接著,我們可以把x[2]<-x[1]-0.2*x[1]這樣的運算用i改寫,請同學試試看。請注意,這裡x[2]的意義,是x[1]的「下一個位置」。

x[i + 1] <- x[i] - 0.2 * x[i]

關卡 17

在完成上一個問題之後,我們只要讓i=1的時候跑一次運算、i=2再跑一次運算、一直重複這個動作到i=99。在R裡面,我們可運用for這個語法來達成這個目的。下一個指令,我們先讓同學複製貼上一個R的迴圈指令,再跟同學解釋。

關卡 18

請同學輸入:for(iin1:99)x[i+1]<-x[i]-0.2*x[i]

for(i in 1:99) x[i + 1] <- x[i] - 0.2 * x[i]

關卡 19

請同學輸入x,就可以看看計算結果了。x越後面的element,就越靠近0。

x

關卡 20

請問同學,這裡1:99的長度是多少?

99

關卡 21

在R中,for(iin1:99)expr的語法代表的,就是讓i是為1:99的第一個element,也就是i=1的狀況下去運算expr。接著,讓i1:99的第二個element,也就是i=2的狀況下去運算expr。重複這個動作,直到i1:99的最後一個element,也就是i=99的狀況。提醒同學,這裡的expr就是x[i+1]<-x[i]-0.2*x[i]

關卡 22

其實大部分機器學習算法的核心,是非常類似我們上述所做的動作:「不停的改善既有的答案」。一開始我們先猜3,然後我們得出一個可以改善答案的方法(x[i+1]<-x[i]-0.2*x[i]),最後得到一個「夠用」的答案。

關卡 23

除了上述的範例之外,for迴圈也常常用於處理各種資料處理的工作。舉例來說,如果我知道所有要讀資料的檔案名稱,依序是:“data/1.csv”、“data/2.csv”和、“data/3.csv”,那我們要如何用for迴圈讀取所有的資料呢?

關卡 24

這類的應用,我們需要懂一點用R產生字串的函數,才能做出來。請同學先輸入:paste0("data/",1,".csv")

paste0("data/", 1, ".csv")

關卡 25

這裡的paste0函數可以把輸入的R物件粘接成一個字串。另一個非常相似的函數是:paste。兩者最主要的差異,在於paste0是直接粘接,而paste在粘接時預設會插入空格。

關卡 26

另一個問題,是跑迴圈時,要如何儲存每一次執行expr後得到的結果。當輸出結果是數字時,我們可以像上述的範例,先建立一個向量來儲存。更一般的狀況中,可以建立一個list來儲存結果。提醒同學,list可以儲存任何的R物件。

關卡 27

假設我們先建立一個list()並取名為retval,接著我們想用for(iin<??>)retval[[i]]<-read.table(paste0("data/",i,".csv"))來一口氣讀取“data/1.csv”、“data/2.csv”、“data/3.csv”。請問同學,<??>應該要填入什麼呢?

c(1,2,3)

關卡 28

上述的兩個例子,大致上呈現R的迴圈功能,以及在使用上所需要搭配的一些小撇步。

關卡 29

接著,再讓我們回到第一個範例。在實務上,我們不可能無窮無盡的讓改善答案的過程一直做下去。當答案「夠用」的時候,我們就想停止這個過程。除了用預先規劃跑99次的方式之外,另外一種常用的方式,是衡量每次改善的幅度,並且當改善幅度太小的時候,中斷執行的程序。

關卡 30

通常,我們會拿這次的答案和上一次的答案做比較,如果差異過小,就代表能改善的幅度已經太小,答案可能已經夠用了。在這裡,我們利用abs(x[2]-x[1])可以計算第一次計算後答案的差異。這裡的abs是取絕對值的運算。abs可以保證回傳的都是正數,在比大小時很常用。同理,請同學計算第二次計算後答案的差異。

abs(x[3] - x[2])

關卡 31

一個實務上常常採用的策略是:我們希望在答案差異小於0.01的時候,終止迴圈運算,否則就會一直計算。同時,我們希望迴圈最多跑99次。

關卡 32

要達到這樣的功能,我們需要先解決:「在改善幅度小於0.01的時候,終止迴圈運算,否則就會一直計算」的程式。在R之中,我們可以利用if(expr1)expr2來寫出這樣的程式。這樣,R只有在expr1TRUE的時候,才會執行expr2。否則就會略過。

關卡 33

在R的break函數則可以中斷迴圈。

關卡 34

回到原本的問題,我們可能會寫出這樣的程式碼。請同學在R打開編輯器之後,閱讀程式碼,再回到swirl中輸入submit()

# 重新初始化x 的內容
x <- numeric(100)
x[1] <- 3
# 尋找x^2的最小值
for(i in 1:99) {
  x[i + 1] <- x[i] - 0.2 * x[i]
  if (abs(x[i + 1] - x[i]) < 0.01) break
}

關卡 35

請同學先印出x得值

x

關卡 36

請問同學,這個計算在執行多少次就停止了?

20

關卡 37

這份課程利用兩個常見的範例來跟同學介紹R中的forif兩個功能。適當的使用這兩個功能,我們可以讓R來完成許多複雜的任務。