2018年11月21日 星期三

[R語言] Coursera R Programming Week 3 Assignment超白話解釋


有鑑於這是第一次學習程式語言(高中生科課那種玩玩的不算XD),充滿不熟悉和不確定性,於是希望藉由紀錄學習過程,可以幫助往後自己的學習,然後我自己的邏輯思考跟抽象思考能力不是很好,過去在學習數學和科學理論時常碰壁或卡關,因此希望可以利用一些方法去釐清問題,並想辦法把這些事物轉化成自己可以理解和記憶的形式,希望多少也可以幫助到跟我一樣初學R的夥伴。

這次Coursera的作業是要依樣畫葫蘆寫出一個R function來緩存被計算過的數值,以減少電腦重複計算所需要的時間和空間,尤其是當被計算物件相對複雜時。僅僅是依樣畫葫蘆其實不難完成這個作業,但我看了很久還是不知道這個function是怎麼寫出來的?為什麼這樣寫?

首先,Simon Sinek提出的黃金思維圈(Golden circle)是個好東西,讓我們從Why-How-What的結構先分析這個問題。

Purpose?
Write an R function that is able to cache potentially time-consuming computations

Why?
If an object is complex, and its computation value needs to be reused again, cache the value, so that it can be looped up in the cache rather than recomputed.

cache > recomputation

How?
Take advantage of the scoping rules of R & how they can be manipulated to preserve state inside of an R object

What?
Example:
makeVector <- function(x = numeric()) {        ## makeVector creates a special "matrix"
        m <- NULL                                        ## which is really a list containing a
        set <- function(y) {                             ## function to set the value of the vector
                x <<- y
                m <<- NULL
        }
        get <- function() x                              ## get the value of the vector
        setmean <- function(mean) m <<- mean  ## set the value of the mean
        getmean <- function() m                      ## get the value of the mean
        list(set = set, get = get,
             setmean = setmean,
             getmean = getmean)
}

cachemean <- function(x, ...) {                     ## cachemean calculates the mean of
        m <- x$getmean()                              ## the special "matrix" created above
        if(!is.null(m)) {                                    ## check if the mean has been calculated
                message("getting cached data")   ## if so, get the mean from the cache
                return(m)                                   ## and skips computation
        }
        data <- x$get()                                  ## Otherwise, it calculates the mean of
        m <- mean(data, ...)                           ## the data, and sets the value of the
        x$setmean(m)                                    ## mean in cache via the setmean
        m                                                     ## function
}

Assignment:
makeCacheMatrix <- function(x = matrix()) {
  i <- NULL
  set <- function(y) {
    x <<- y
    i <<- NULL
  }
  get <- function() x
  setinverse <- function(inverse) i <<- inverse
  getinverse <- function() i
  list(set = set, get = get,
       setinverse = setinverse,
       getinverse = getinverse)
}

cacheSolve <- function(x, ...) {
  i <- x$getinverse()
  if(!is.null(i)) {
    message("getting cached data")
    return(i)
  }
  data <- x$get()
  i <- solve(data, ...)
  x$setinverse(i)
  i
}

經過說明後我僅能看得懂片段的function,但針對細節的設定(尤其是為什麼要makeCacheMatrix)還是不清楚,充滿疑問,所以我先把問題記下來,然後再從看得懂的,或最簡單的開始前後推敲,不需要按照順序理解。

黑人問號:
問題1: 透過這兩個function,matrix實際做了哪些事?
問題2: 這兩個function如何緩存計算值?
問題3: 為什麼要定義makeCacheMatrix?其中為什麼要定義這麼多function?目的是?

原始想法:
i <- solve(a, ...)
i                             => 建立function想要把他記憶下來
i <- solve(a, ...)         => 建立function想要省略重複計算
i                             => 建立function想要把他從記憶中提取出來

問題4: 如何把它擴張成可以達成使用目的的function?需要幾個function?一個夠嗎?

先看比較容易懂的cacheSolve function(比較不懂的部分標紅色):
cacheSolve <- function(x, ...) {
  i <- x$getinverse()                         ## 提取被計算與緩存過的matrix inverse值
  if(!is.null(i)) {                                  ## 如果matrix inverse值不是NULL(空值),表示已
    message("getting cached data")    ## 有緩存的值,呈現getting cached data的訊息
    return(i)                                      ## 並回報matrix inverse的值
  }
  data <- x$get()                             ## 若是NULL,則叫出matrix值
  i <- solve(data, ...)                         ## 計算inverse值
  x$setinverse(i)                               ## 將計算好的inverse值賦值給欲緩存的變數i
  i                                                  ## 回報matrix inverse的值
}
小結:這個function主要作用是判斷matrix inverse是否經過計算,若有的話直接提取緩存值,若無的話計算後把結果透過setinverse()緩存,下次就可以用getinverse()提取。

接著看makeCacheMatrix function(比較不懂的部分標紅色):
makeCacheMatrix <- function(x = matrix()) {  ## 把matrix建立成一個特別的list
  i <- NULL                                                ## 定義變量i,紀錄matrix inverse的值
  set <- function(y) {                                   ## 定義set function,指定一個新的matrix
    x <<- y                                                 ## 在主環境中,當有新的matrix時,
    i <<- NULL                                            ## 重新設定變量i為NULL(空值)
  }
  get <- function() x                                    ## 定義get function,回報matrix本身
  setinverse <- function(inverse) i <<- inverse ## 定義setinverse function,用來指定
                                                                ## i值,賦值計算值為變數i 而不再是NULL
  getinverse <- function() i                          ## 定義getinverse function,回報i值
  list(set = set, get = get,                            ## 回報一個屬於matrix的list
       setinverse = setinverse,                       ## 包含set, get matrix和 set, get matrix
       getinverse = getinverse)                      ## inverse四個物件,提供cacheSolve函式
}                                                             ## 引用

小結:這個function主要作用是把matrix拆成四個物件,以list的方式呈現,供下一個cacheSolve function引用。為什麼要定義這四個物件?少幾個有關係嗎?以實際計算、緩存、提取的需求來說,後三個是必要的,但是set()並沒有出現在cacheSolve function扮演任何角色,經過實際跑程式驗證:沒有定義set()並不會造成運作失敗。那為什麼要定義set()?這個我...就不知道了。

實例分析:
a <- matrix(c(1, 2, 3, 4), nrow = 2)              ## 建立一個matrix叫做a
alist <- makeCacheMatrix(a)                      ## 轉化matrix為list來包含需要使用的物件
alist                                                         ## 看看alist長什麼樣子
$set                                                        ## set function,定義新的matrix的i值
function (y)
{
    x <<- y
    i <<- NULL
}
<bytecode: 0x10f859df0>
<environment: 0x10d9bcd90>

$get                                                        ## get function,回報matrix a本身
function ()
x
<bytecode: 0x10e6ef1c8>
<environment: 0x10d9bcd90>

$setinverse                                              ## setinverse function,用來緩存i值
function (inverse)                                      ## 賦值計算結果給變數i,讓i不再是NULL
i <<- inverse
<bytecode: 0x110106750>
<environment: 0x10d9bcd90>

$getinverse                                              ## getinverse function,用來提取i值
function ()                                                ## 可能是NULL或是被緩存的值
i
<bytecode: 0x10e7636d0>
<environment: 0x10d9bcd90>

cacheSolve(alist)                                       ## 第一次計算matrix inverse
     [,1] [,2]                                                ## i為NULL,需計算並且把計算結果緩存
[1,]   -2  1.5                                             
[2,]    1 -0.5

cacheSolve(alist)                                       ## 第二次計算matrix inverse
getting cached data                                   ## 已有緩存值所以不必計算,直接提取
     [,1] [,2]
[1,]   -2  1.5
[2,]    1 -0.5

結論:這個程式語言是透過function、loop的設計和R的Lexical Scoping rules來達到目的,思考的方向可以從易懂的部份著手,前後推敲,找出每一個編程的用意,想一下為什麼順序要這樣安排,實際操作怎麼運行,有疑問或其他想法可以自己嘗試。學習過程: 參考範例 -> 問問題&記錄問題 -> 尋找為什麼 -> 串連一連串的為什麼來解釋整個故事 -> 練習活用

沒有留言:

張貼留言

統計學 | 什麼是變方分析ANOVA? ANOVA的概念與統計檢定量的推導

什麼是 ANOVA? 在前面的統計學章節,我們學到單變量和兩變量的假設檢定,假設我們想要比較三個以上的樣 本 是否有差異,這時候就要使用 ANOVA了。 ANOVA, analysis of variance, 變方分析,雖然名為「變方」分析,但其實是用來檢定三個以上的樣本...