[R Lang] R Lang與高級醫學統計學(3)
Adrian Chen

這篇文章講R Lang中的資料物件。在R Lang中,資料物件可以是向量(vector)、矩陣(matrix)、陣列(array)、列表(lists)或資料框架(data frames)等。

矩陣 Matrix

矩陣是一個包含相同模式資料的二維度資料物件,大致相當於其他程式語言中的二維度數組的概念。

創建矩陣

使用函式matrix()來創建矩陣物件。該函式的定義如下:

1
matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)

下面我們來解釋其中的引數:

  • nrow:正整數,設定列數(row numbers)
  • ncol:正整數,設定欄數(column numbers)
  • byrow:根據自動設定,欄位(column優先填滿)
  • dimnames:輸入列表設定列名與欄位名

請中國大陸的讀者注意:在中國大陸和台灣,對於“行”和“列”的定義恰好是相反的。台灣地區的“行”或“欄”是指一個表格中的縱向(column),而“列”則是指一個表格中的橫向(row)。本文採用台灣地區的定義方式,敬請知曉。

使用dim()函式可以回傳具有維度數屬性的資料物件之維度大小。

下面我們來看一個例子:

1
2
3
4
5
6
7
8
9
## 因為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()單獨為列或欄命名。

1
2
3
4
5
6
7
8
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]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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。例如:

1
2
3
x[1, , drop = FALSE]
## [,1] [,2] [,3] [,4]
## [1,] 1 4 7 10

注意,和C家族語言中的無限for迴圈一樣,兩個逗號都不能省略,因為靠逗號來識別不同的引數。

向量與矩陣的合併

上一篇文章中我們提到,向量本身是沒有維度的。沒有區別。然而有的時候我們需要把向量和矩陣進行線性代數相關計算。

一個比較簡單的方法是,將其重新定義為的有維度matrix。

1
2
3
4
5
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,然後不足的內容開始重複填充,直到補足。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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()函式來創建陣列。函式定義如下:

1
array(data = NA, dim = length(data), dimnames = NULL)

按照慣例解釋引數:

  • dim:陣列的維度,以及每一個維度包含的元素個數。
  • dimnames:每一個維度的命名。

一樣可以使用dimnames()函式來為陣列的維度命名。rownames()colnames()也同樣適用第一和第二維度。

關於下標系統,陣列和矩陣基本完全相同,只不過是有幾個維度上幾個引數而已。

由於篇幅原因,陣列部分就不放例子了。讀者可以自行回去試一下。

列表 List

R Lang中列表的概念也同樣不同於其他我們所熟悉的程式語言。在R Lang中,列表事實上就是一個有序的物件的集合體。特別注意的是,在前面介紹的無論是向量還是矩陣,抑或是陣列,都特別表明“包含相同模式 (mode)”。而列表中的元素(稱作“成分”,component)的模式則是所謂的複雜模式,即沒有限制。

列表的產生

使用list()函式來產生列表。

通常情況下,我們先產生列表中的成分,再將成分組合成列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
x.vec <- 1:4
y.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

也可以在定義列表的時候,為每一個成分命名:

1
2
3
4
5
6
7
8
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,而不是我們塞進去的物件。要取到我們的物件,需要使用[[]]

1
2
3
4
5
6
7
8
9
10
11
x.vec <- 1:4
y.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的方式。這兩種方式沒有任何區別。

1
2
3
4
5
6
7
8
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

xy.list$y.str.var
## [1] "chocolate" "vanilla"

資料框架 Data Frame

事實上,前面我們所講到的矩陣,我們可以把它看作是二維度表格,陣列則是多維度表格。然而,這些表格有一個限制,那就是元素的模式必須是相同的。

資料框架事實上也是一個二維表格,但與矩陣不同的是,它的元素模式可以是不同的。

創建資料框架

使用data.frame()函式來創建資料框架。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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]來獲取元素,另一套則是類似於列表,使用[[]]來獲取成分中的物件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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