list

  • vector 是 R 裡面最「簡單」的資料結構。有時候我們需要比較更複雜的資料結構處理我們遇到的資料,例如,我們或許需要儲存不同資料類型或是具有階層結構的資料。面對這兩種需求,vector 無能為力,因此需要用到 R 的 list。

  • R 可以透過 list() 去製造出 list。list() 的使用方式很類似用來製造 vector 的 c(),但與 c() 不同的是,list()
    1. 能使用不同的資料類型
    2. 具有階層結構,亦即,list() 裡面可以放入另一個 list()
    #> [[1]]
    #> [1] TRUE
    #> 
    #> [[2]]
    #> [1] 1 2 3
    #> 
    #> [[3]]
    #> [1] "Hello"
    #> $kai
    #> [1] TRUE
    #> 
    #> $joy
    #> [1] 1 2 3
    #> 
    #> $jess
    #> [1] "Hello"
    #> [[1]]
    #> [1] 1.1
    #> 
    #> [[2]]
    #> [[2]][[1]]
    #> [1] 2.1
    #> 
    #> [[2]][[2]]
    #> [1] "Hello"

Subsetting

  • 通常我們會習慣為 list 加上名字 (names),幫助我們更容易處理這種比較複雜的資料結構

  • []: 與 vector 一樣,我們可以透過 lst[<char vector of names>]lst[<integer vector>]lst[<logical vector>] 去 subset list

    #> $single
    #> [1] FALSE
    #> $single
    #> [1] FALSE
    #> 
    #> $tags
    #> [1] "ig"     "selfie"
    #> $age
    #> [1] 20
    #> 
    #> $tags
    #> [1] "ig"     "selfie"
  • 就像 vec[<some vector>] 會回傳一部分的 vector (sub-vector)lst[<some vector>] 也會回傳一部分的 list (sub-list)。換言之,使用 [] 時,回傳值的資料結構不會改變

  • 我們可以將 list 想像成一列火車,每節車廂是一個長度為 1 的 sub-list,車廂裡面是這個 sub-list 儲存的值。欲取得 sub-list,使用的是 [];欲取得 sub-list 裡面的值 (i.e. 脫去外層的 list),需使用 [[]]

    #> [1] "list"
    #> $tags
    #> [1] "ig"     "selfie"
    #> 
    #> [1] "character"
    #> [1] "ig"     "selfie"
    • lst[["<name>"]] 有另一種更簡便的寫法:lst$<name>, e.g. ben[["tags"]] 可改寫成 ben$tags
list as a train analogy

list as a train analogy

Nested Structure

#> $age
#> [1] 20
#> 
#> $tags
#> [1] "ig"     "selfie"
#> 
#> [1] "ig"     "selfie"
#> [1] "selfie"
#> $age
#> [1] 20
#> 
#> $tags
#> [1] "ig"     "selfie"
#> 
#> [1] "ig"     "selfie"
#> [1] "selfie"
#> $age
#> [1] 20
#> 
#> $tags
#> [1] "ig"     "selfie"
#> 
#> [1] "ig"     "selfie"
#> [1] "selfie"
#> $age
#> [1] 20
#> 
#> $tags
#> [1] "ig"     "selfie"
#> 
#> [1] "ig"     "selfie"
#> [1] "selfie"

for loop

  • 上週介紹的條件式 (if-else) 讓我們可以依據不同狀況執行不同的程式碼,藉此能幫助我們寫出更有彈性的程式。迴圈讓我們能重複執行某一區塊的程式碼,如此就不需要重複寫出相同的程式碼。

  • R 有 for 與 while 迴圈。一般而言,在資料分析時非常少會用到 while 迴圈,因此實習課不作介紹,有興趣的同學可自行參考線上教材或教科書。

  • for loop 的結構如下

  • for loop 會使 {} 內的程式碼重複執行數次,其次數等於 <vector> 的長度;並且,在第 n 次開始執行 {} 內的程式碼前,會將 <vector> 裡的第 n 個元素指派給 <變數>。所以在第一次迴圈時,可透過 <變數> 取得 <vector> 中的第一個元素;在第二次迴圈時,可取得 <vector> 中的第二個元素;依此類推,最後一次迴圈則可以透過 <變數> 取得 <vector> 中的最後一個元素。

#> [1] "謝"
#> [1] "老師"
#> [1] "好"
#> [1] "帥"

for loop 的各種型態

  • R 的 for 只有一種結構:每次疊代將 vector (或 list) 中的一個元素指派給變數 (<var> in <vector>)。但因為 R 向量式程式語言的特性,R 的 for 迴圈很容易改寫成其它更方便的型態。
  1. 有時候我們需要知道迴圈進行到 <vector> 的第幾個元素,這時候通常會使用 seq_along(<vector>) 去製造出與 <vector> 等長的整數序列 (e.g. seq_along(c('a', 'b', 'c')) 會回傳 1 2 3),如此我們便可知道進行到第幾次迴圈,也可透過 <vector>[i] 取得與該次迴圈對映的元素。

    #> [1] "1 謝"
    #> [1] "2 老師"
    #> [1] "3 好"
    #> [1] "4 帥"
    #> [1] "謝"
    #> [1] "老師"
    #> [1] "好"
    #> [1] "帥"
    #> [1] "?"
  2. 我們也可以透過 names() 在 for loop 裡使用 <vector> 的 names 屬性:

    #> [1] "Monday was rainy."
    #> [1] "Tuesday was cloudy."
    #> [1] "Wednesday was sunny."

break & next: 進階迴圈控制 (有興趣者自行參考)

實際應用:修改檔案名稱

下方的程式碼能將上週作業使用到的骰子的圖片重新命名 (並透過 next 忽略某些檔案)。有興趣者請下載此週原始碼,裡面有一個相同的資料夾 dice/,並且多出了一份檔案 00_not_an_img.txt,執行此程式碼前,需將工作目錄設至 dice/ 資料夾

Wrap up:for loop 與 list

data frame

  • data frame 是 R 語言非常重要的資料結構,它造就了 R 強大的表格式資料處理能力

  • data frame 是一種二維的資料結構。這種資料結構基本上與我們熟悉的 Excel (或 google 試算表) 非常類似:

    • A data frame looks like an Excel Spreadsheet

      A data frame looks like an Excel Spreadsheet

    • data frame 的每一橫列 (row) 皆是一筆資料 (e.g. 一位受訪者所填的問卷)

    • data frame 的每一(直)欄 (column) 代表一個變項 (e.g. 問卷上的某個題目)


data frame 例子

data frame 例子

Subsetting: returning a data frame

  • data frame 的篩選 (subsetting) 與 vector 和 list 類似,差別只在於 data frame 屬於二維的資料結構,因此需要提供 2 個 vector 進行資料的篩選:

    df[<vector 1>, <vector 2>]
    • 在這裡,<vector 1> 篩選的是「列 (row)」,亦即,<vector 1> 決定要篩選出哪幾個觀察值 (observations)。<vector 2> 篩選的則是「欄 (column)」,亦即,<vector 2> 決定要篩選出哪些變項 (variables)。

    • 以這種語法進行篩選,回傳的一定是 data frame2,即使只有篩選出一個值 (e.g. df[1, 1])

#> # A tibble: 1 x 1
#>   name 
#>   <chr>
#> 1 jessy
#> # A tibble: 1 x 2
#>   name    age
#>   <chr> <dbl>
#> 1 jessy    20
#> # A tibble: 1 x 3
#>   name    age grad 
#>   <chr> <dbl> <lgl>
#> 1 jessy    20 TRUE

Subsetting: returning a vector


  1. R 的內建函數 data.frame() 是最常被用於建立 data frame 的函數。tibble 套件裡的 tibble() 則與 data.frame() 的功能幾乎一樣,只是 tibble() 更改了 data.frame() 裡面常令使用者感到困惑的一些預設行為。

    tibble 套件為 Tidyverse 套件群的一員,目的是為了解決 base R 眾多函數紛亂不一致的問題 (e.g. 命名不一致、預設行為不一致、類似的函數回傳值不一致等)。

  2. 這只有使用 tibble 套件的 tibble() 所回傳的 data frame (class 為 tbl_df, tbldata.frame) 才有這種特性。使用 R 的內建函數 data.frame() 所建立的 data frame 或是 R 的內建資料集 (class 只有 data.frame) 則會根據所篩選出之資料的形狀的不同而回傳不同的資料結構 (有可能是 data.frame 或是 vector)