13 多樣的資料結構:矩陣、資料框與更多

13.1 矩陣

矩陣是能夠儲存列(Row)與欄(Column)的資料結構,如果讀者跟我一樣對於分辨行、列與欄這些中文字有障礙的話,我非常推薦用英文來記憶:Row 指的就是水平方向資料,Column 指的就是垂直方向資料。

我們通用的習慣是先 Row 後 Column,這句話是什麼意思?假如我們現在和您說要建立一個 2x3 的矩陣,意思就是兩個水平方向搭配三個垂直方向的矩陣,外觀長得像這樣:

my_mat <- matrix(1:6, nrow = 2)
my_mat
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6
class(my_mat)
## [1] "matrix"

我們使用 matrix() 函數並指定參數 nrow = 2 將一維的數字向量(1 到 6)轉換成一個 2x3 的矩陣;往後如果聽到一個矩陣是 mxn 或者 (m, n) 的外觀,我們的心中就會知道,這個矩陣具有 m 個水平方向資料,n 個垂直方向資料。matrix() 函數還有一個 byrow 的參數可以指定要用什麼順序擺放原先在向量中的元素:

my_mat <- matrix(1:6, nrow = 2, byrow = TRUE)
my_mat
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6

眼尖的您很快就發現雖然這個矩陣的外觀仍然是 2x3,但六個數字擺放的方向變為水平的。byrow 參數的預設值為 FALSE,意思是如果我們沒有特別指定,就是以垂直的方向來擺放矩陣。

矩陣同樣能夠以 [] 搭配索引值選出裡面的變數,只不過跟向量不同的是,現在有兩個維度的索引值必須指定,所以要用 [m, n] 兩個索引值來搭配選擇:

my_mat <- matrix(1:6, nrow = 2)
my_mat
##      [,1] [,2] [,3]
## [1,]    1    3    5
## [2,]    2    4    6
my_mat[2, 3] # 選出位於(2, 3)這個位置的 6
## [1] 6
my_mat[2, ] # 選出所有第二列(2nd row)的元素
## [1] 2 4 6
my_mat[, 3] # 選出所有第三欄(2nd column)的元素
## [1] 5 6

我們也可以透過判斷運算子來對矩陣進行篩選,選出介於 1 與 6 之間的數字:

my_mat <- matrix(1:6, nrow = 2)
filter <- my_mat < 6 & my_mat > 1
my_mat[filter]
## [1] 2 3 4 5

矩陣與向量有一點很相似的特性,那就是包含一種變數類型,假如我們將數值與邏輯值一同放入矩陣,則邏輯值亦會被自動轉換成數值:

my_mat <- matrix(c(1, 2, TRUE, FALSE, 3, 4), nrow = 2)
my_mat
##      [,1] [,2] [,3]
## [1,]    1    1    3
## [2,]    2    0    4
class(my_mat[, 2]) # 原本第二欄(2nd column)的輸入是兩個邏輯值
## [1] "numeric"

13.2 資料框

資料框絕對是我們最需要關注的一種資料結構!它能夠容許不同的欄位有不同的變數類型,讀取外部資料之後,也通常預設以資料框的格式儲存。我們可以運用 data.frame() 函數手動創造資料框,讓我們建立一個很簡單的資料框叫做 great_nba_teams,這個資料框有隊名、勝場數、敗場數、是否獲得總冠軍與球季:

team_name <- c("Chicago Bulls", "Golden State Warriors")
wins <- c(72, 73)
losses <- c(10, 9)
is_champion <- c(TRUE, FALSE)
season <- c("1995-96", "2015-16")

great_nba_teams <- data.frame(team_name, wins, losses, is_champion, season)

除了將資料框直接輸出在命令列(Console),我們可以使用 View() 函數瀏覽資料框的外觀與內容。我們習慣使用觀測值(Observations,Obs)來稱呼資料框中水平方向的資料,使用變數(Variables)來稱呼資料框中垂直方向的資料,我們用一個簡單的表格和矩陣比對一下:

資料方向 矩陣 資料框
水平 Rows Observations
垂直 Columns Variables

資料框同樣能夠以 [m, n] 兩個索引值來搭配選擇出變數:

team_name <- c("Chicago Bulls", "Golden State Warriors")
wins <- c(72, 73)
losses <- c(10, 9)
is_champion <- c(TRUE, FALSE)
season <- c("1995-96", "2015-16")
great_nba_teams <- data.frame(team_name, wins, losses, is_champion, season)
great_nba_teams[1, 1] # 選出第一個變數的第一個觀測值 "Chicago Bulls"
## [1] Chicago Bulls
## Levels: Chicago Bulls Golden State Warriors
great_nba_teams[1, ] # 選出第一個觀測值
##       team_name wins losses is_champion  season
## 1 Chicago Bulls   72     10        TRUE 1995-96
great_nba_teams[, 1] # 選出第一個變數
## [1] Chicago Bulls         Golden State Warriors
## Levels: Chicago Bulls Golden State Warriors

眼尖的您不知道有沒有發現,資料框預設會將文字的內容以因素向量儲存,如果您希望修正為文字可以有兩種作法,一種是建立的時候在 data.frame() 函數指定 stringsAsFactors = FALSE

team_name <- c("Chicago Bulls", "Golden State Warriors")
wins <- c(72, 73)
losses <- c(10, 9)
is_champion <- c(TRUE, FALSE)
season <- c("1995-96", "2015-16")
 
great_nba_teams <- data.frame(team_name, wins, losses, is_champion, season, stringsAsFactors = FALSE)
great_nba_teams[, 1] # 選出第一個變數
## [1] "Chicago Bulls"         "Golden State Warriors"
great_nba_teams[, 5] # 選出第五個變數
## [1] "1995-96" "2015-16"

另一種是事後使用 as.character() 函數進行變數類型的轉換:

team_name <- c("Chicago Bulls", "Golden State Warriors")
wins <- c(72, 73)
losses <- c(10, 9)
is_champion <- c(TRUE, FALSE)
season <- c("1995-96", "2015-16")
 
great_nba_teams <- data.frame(team_name, wins, losses, is_champion, season)
great_nba_teams[, 1] <- as.character(great_nba_teams[, 1])
great_nba_teams[, 5] <- as.character(great_nba_teams[, 5])
great_nba_teams[, 1] # 選出第一個變數
## [1] "Chicago Bulls"         "Golden State Warriors"
great_nba_teams[, 5] # 選出第五個變數
## [1] "1995-96" "2015-16"

特別值得一提的是,資料框支援使用變數名稱來選擇,我們可以用 $變數名稱 或者 [, "變數名稱"] 這兩種寫法:

team_name <- c("Chicago Bulls", "Golden State Warriors")
wins <- c(72, 73)
losses <- c(10, 9)
is_champion <- c(TRUE, FALSE)
season <- c("1995-96", "2015-16")
 
great_nba_teams <- data.frame(team_name, wins, losses, is_champion, season, stringsAsFactors = FALSE)
great_nba_teams$team_name
## [1] "Chicago Bulls"         "Golden State Warriors"
great_nba_teams[, "season"]
## [1] "1995-96" "2015-16"

我們也可以透過判斷運算子來對資料框進行篩選,選出最終有獲得總冠軍的隊伍:

team_name <- c("Chicago Bulls", "Golden State Warriors")
wins <- c(72, 73)
losses <- c(10, 9)
is_champion <- c(TRUE, FALSE)
season <- c("1995-96", "2015-16")
 
great_nba_teams <- data.frame(team_name, wins, losses, is_champion, season, stringsAsFactors = FALSE)
filter <- great_nba_teams$is_champion == TRUE
great_nba_teams[filter, ] # 注意這個逗號
##       team_name wins losses is_champion  season
## 1 Chicago Bulls   72     10        TRUE 1995-96

請讀者在這裡一定要注意我們的篩選是針對觀測值(水平方向),所以要將判斷運算子判斷後的結果(filter)放在逗號前面,逗號後面留空(我們沒有要選擇變數。)

資料框不像矩陣僅能包含一種變數類型,您可以發現在我們的 great_nba_teams 資料框中,有文字(team_nameseason)、整數(winslosses)與邏輯值(is_champion),我們可以用 str() 函數觀察:

team_name <- c("Chicago Bulls", "Golden State Warriors")
wins <- c(72, 73)
losses <- c(10, 9)
is_champion <- c(TRUE, FALSE)
season <- c("1995-96", "2015-16")
 
great_nba_teams <- data.frame(team_name, wins, losses, is_champion, season, stringsAsFactors = FALSE)
str(great_nba_teams)
## 'data.frame':    2 obs. of  5 variables:
##  $ team_name  : chr  "Chicago Bulls" "Golden State Warriors"
##  $ wins       : num  72 73
##  $ losses     : num  10 9
##  $ is_champion: logi  TRUE FALSE
##  $ season     : chr  "1995-96" "2015-16"