這篇文章講 R Lang 中的資料物件。在 R Lang 中,資料物件可以是向量(vector)、矩陣(matrix)、陣列(array)、列表(lists)或資料框架(data frames)等。
矩陣 Matrix
矩陣是一個包含相同模式資料的二維度資料物件,大致相當於其他程式語言中的二維度數組的概念。
創建矩陣
使用函式 matrix()
來創建矩陣物件。該函式的定義如下:
matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)
下面我們來解釋其中的引數:
- nrow:正整數,設定列數(row numbers)
- ncol:正整數,設定欄數(column numbers)
- byrow:根據自動設定,欄位(column優先填滿)
- dimnames:輸入列表設定列名與欄位名
使用 dim()
函式可以回傳具有維度數屬性的資料物件之維度大小。
下面我們來看一個例子:
## 因為 byrow 預設為 FALSE,因此遵循先欄後列的原則。x <- matrix(c(1, 2, 3, 4, 5, 6), nrow = 2)x## [,1] [,2] [,3]## [1,] 1 3 5## [2,] 2 4 6
dim(x) # 回傳值永遠是:欄數 列數,無關 byrow 的值## [1] 2 3
矩陣命名
矩陣的命名包括欄位名和列名。命名可以使用函式 dimnames()
進行統一命名,也可以使用 rownames()
函式或 colnames()
單獨為列或欄命名。
x.mat <- matrix(1:6, nrow = 2, ncol = 3)
## 使用 dimnames() 函式統一命名,接受 list 引數dimnames(x.mat) <- list(c("A1", "A2"), c("B1", "B2", "B3"))
## 使用 rownames() 函式單獨為列命名rownames(x.mat) <- c("A1", "A2")
矩陣下標索引 Matrix Index
很像 C++ 中的二維數組的下標。只不過在 C++ 中,用到的是 variable[i][j]
,而在 R Lang 中,使用 variable[i, j]
而已。當然,與向量相同,該下標系統具有更強大的特性,一樣可以使用正整數、負整數等等。而如果想要取到一整列,可以使用 variable[i, ]
即可。舉一反三,取到一整欄就是 variable[, j]
。
x <- matrix(c(1:12), nrow = 3, ncol = 4)
## 取到第 3 列第 2 欄的元素x[3, 2]## [1] 6
## 取到第 2 列x[2, ]## [1] 2 5 8 11
## 取到第 3 欄x[, 3]## [1] 7 8 9
## 取到 1,3 列和 2,4 欄的交會點x[c(1, 3), c(2, 4)]## [,1] [,2]## [1,] 4 10## [2,] 6 12
## 如果已經為欄或列命名,則可以使用名字的字串
dimnames(x) <- list(c("Row1", "Row2", "Row3"), c("Col1", "Col2", "Col3", "Col4"))
x["Row2", "Col2"]## [1] 5
我們可以看到,我們取到的只有一欄或一列的時候,會發生維度塌方,生成向量。而如果希望保持 matrix 的形式,則可以加入第三個引數 drop = FALSE
。例如:
x[1, , drop = FALSE]## [,1] [,2] [,3] [,4]## [1,] 1 4 7 10
注意,和 C 家族語言中的無限 for 迴圈一樣,兩個逗號都不能省略,因為靠逗號來識別不同的引數。
向量與矩陣的合併
上一篇文章中我們提到,向量本身是沒有維度的。 和 沒有區別。然而有的時候我們需要把向量和矩陣進行線性代數相關計算。
一個比較簡單的方法是,將其重新定義為 或 的有維度 matrix。
x.vec <- c(1:6)x.mat <- matrix(x.vec, nrow = 1)x.mat## [,1] [,2] [,3] [,4] [,5] [,6]## [1,] 1 2 3 4 5 6
有時候我們需要把向量和矩陣或其他向量進行合併,成為一個新的矩陣,將該向量作為新矩陣的一欄或者一列。這時候,可以使用 cbind()
函式或 rbind()
函式。前者表示將向量作為新矩陣的一欄,後者則表示將向量作為新矩陣的一列。注意,如果向量的元素數量超過了矩陣的長度,則會報出一個 Warning,然後捨棄掉超過的內容。如果不足,則會報出一個 Warning,然後不足的內容開始重複填充,直到補足。
x.vec <- c(1, 2, 3)y.vec <- c(8, 9, 10)x.mat <- matrix(c(11:16), nrow = 2, ncol = 3)x.vec2 <- c(1, 2)
rbind(x.vec, y.vec)## [,1] [,2] [,3]## x.vec 1 2 3## y.vec 8 9 10
cbind(x.vec, y.vec)## x.vec y.vec## [1,] 1 8## [2,] 2 9## [3,] 3 10
rbind(x.mat, x.vec)## [,1] [,2] [,3]## 11 13 15## 12 14 16## x.vec 1 2 3
cbind(x.mat, y.vec)## 在下面,我們看到了 Warning,超出長度的部分 10 被捨棄。## Warning in cbind(x.mat, y.vec): number of rows of result is not a multiple of vector length (arg 2)## y.vec## [1,] 11 13 15 8## [2,] 12 14 16 9
rbind(x.mat, x.vec2)## 在下面,我們看到了 Warning,不足的部分開始重複第一個數字 1,直到補足為止## Warning in cbind(z.mat, y.vec): number of rows of result is not a multiple of vector length (arg 2)## [,1] [,2] [,3]## 11 13 15## 12 14 16## x.vec2 1 2 1
陣列 Array
這個陣列和 Java 以及其他程式設計語言中的陣列完全不同。其他語言中的陣列等於數組,而在 R Lang 中,陣列是指包含相同模式 (mode) 的元素組成的 p 維資料物件。
陣列的操作基本和矩陣相同。使用 array()
函式來創建陣列。函式定義如下:
array(data = NA, dim = length(data), dimnames = NULL)
按照慣例解釋引數:
- dim:陣列的維度,以及每一個維度包含的元素個數。
- dimnames:每一個維度的命名。
一樣可以使用 dimnames()
函式來為陣列的維度命名。rownames()
和 colnames()
也同樣適用第一和第二維度。
關於下標系統,陣列和矩陣基本完全相同,只不過是有幾個維度上幾個引數而已。
由於篇幅原因,陣列部分就不放例子了。讀者可以自行回去試一下。
列表 List
R Lang 中列表的概念也同樣不同於其他我們所熟悉的程式語言。在 R Lang 中,列表事實上就是一個有序的物件的集合體。特別注意的是,在前面介紹的無論是向量還是矩陣,抑或是陣列,都特別表明“包含相同模式 (mode)”。而列表中的元素(稱作“成分”,component)的模式則是所謂的複雜模式,即沒有限制。
列表的產生
使用 list()
函式來產生列表。
通常情況下,我們先產生列表中的成分,再將成分組合成列表。
x.vec <- 1:4y.vec <- c("Male", "Female")z.mat <- matrix(1:9, nrow = 3, ncol = 3)xyz.list <- list(x.vec, y.vec, z.mat)xyz.list## [[1]]## [1] 1 2 3 4#### [[2]]## [1] "Male" "Female"#### [[3]]## [,1] [,2] [,3]## [1,] 1 4 7## [2,] 2 5 8## [3,] 3 6 9
也可以在定義列表的時候,為每一個成分命名:
x.num <- c(1, 3, 6)y.str <- c("chocolate", "vanilla", "strawberry")xy.list <- list(x.num.var = x.num, y.str.var = y.str)xy.list## $x.num.var## [1] 1 3 6#### $y.str.var## [1] "chocolate" "vanilla" "strawberry"
列表的下標與索引 List Index
列表的下標系統和向量、矩陣、陣列有所不同。當我們使用 []
的時候,取到的仍然是list模式的component,而不是我們塞進去的物件。要取到我們的物件,需要使用 [[]]
。
x.vec <- 1:4y.vec <- c("Male", "Female")z.mat <- matrix(1:9, nrow = 3, ncol = 3)xyz.list <- list(x.vec, y.vec, z.mat)
xyz.list[1]## [[1]]## [1] 1 2 3 4
xyz.list[[1]]## [1] 1 2 3 4
若已經為成分命名,則可以使用成分名字來直接訪問到成分或其代表的物件。這裡有兩種方式,可以使用 list.Name[["com.Name"]]
的傳統方式,也可以使用 list.Name$com.Name
的方式。這兩種方式沒有任何區別。
x.num <- c(1, 3, 6)y.str <- c("chocolate", "vanilla", "strawberry")xy.list <- list(x.num.var = x.num, y.str.var = y.str)
xy.list[["x.num.var"]]## [1] 1 3 6
xy.list$y.str.var## [1] "chocolate" "vanilla"
資料框架 Data Frame
事實上,前面我們所講到的矩陣,我們可以把它看作是二維度表格,陣列則是多維度表格。然而,這些表格有一個限制,那就是元素的模式必須是相同的。
資料框架事實上也是一個二維表格,但與矩陣不同的是,它的元素模式可以是不同的。
創建資料框架
使用 data.frame()
函式來創建資料框架。
id.vec <- c(1, 2, 3, 4)age.vec <- c(35, 55, 45, 25)sex.vec <- c("Male", "Male", "Female", "Female")disease.vec <- c("Yes", "No", "No", "Yes")x.df <- data.frame( id = id.vec, age = age.vec, sex = sex.vec, disease = disease.vec)
x.df## id age sex disease## 1 1 35 Male Yes## 2 2 55 Male No## 3 3 45 Female No## 4 4 25 Female Yes
資料框架的下標與索引 Data Frame Index
有兩套體系。一套是類似於矩陣的樣子,使用 [i, j]
來獲取元素,另一套則是類似於列表,使用 [[]]
來獲取成分中的物件。
id.vec <- c(1, 2, 3, 4)age.vec <- c(35, 55, 45, 25)sex.vec <- c("Male", "Male", "Female", "Female")disease.vec <- c("Yes", "No", "No", "Yes")x.df <- data.frame( id = id.vec, age = age.vec, sex = sex.vec, disease = disease.vec)
x.df[1, 2]## [1] 35
x.df[2]## age## 1 35## 2 55## 3 45## 4 25
x.df[[2]]## [1] 35 55 45 25