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

這篇文章講R Lang的基本物件類型——向量(vector)。

向量的基本概念

向量(vector)是指具有相同模式(mode)的元素組成的集合。其實就是其他程式語言中“數組”的概念。作為一門專攻統計學的程式設計語言,R Lang的基本運算單位並不是其他語言的單一變數,而是變數的群體。這個群體的最小體現就是向量。在R Lang中,單一數值的變數可以被看作長度為1的向量。

向量並不具有維度。例如c(1, 2, 3)可以看作的矩陣,也可以看作的矩陣。

向量的基本資料類型(模式)

可以透過class()函式來查看一個變數的模式,透過is.mode()函式來判斷變數是否是某種模式。

1
2
3
4
5
class(x) # 查看變數x的模式
## [1] "numeric"

is.numeric(x) # 判斷變數x是否是numeric類型
## [1] TRUE

基本模式有下面幾種:

numeric

數值型,也即其他語言的“浮點型”,包含單精度浮點型(single)和雙精度浮點型(double)。

1
pi <- 3.14

integer

整數型,需要在值後面加上“L”,否則解釋器講自動將其歸為numeric類型。整數性可以算作數值型的一個特例,因此使用is.numeric()函式仍然返回為TRUE。

1
2
3
4
5
6
7
x <- 2L

is.integer(x)
## [1] TRUE

is.numeric(x)
## [1] TRUE

logical

布林型,取值為“TRUE(T)”或“FALSE(F)”,或者用1和0分別代表T和F。

1
2
3
4
x <- TRUE

class(x)
## [1] "logical"

complex

複數型

1
2
3
4
x <- 3 + 5i

class(x)
## [1] "complex"

character

文字或字串。在R Lang中並未區分字元和字串這兩個概念。事實上一個字元就可以看作只包含一個字元的字串。在R Lang中統一使用雙引號。

1
2
3
4
x <- "Hello, world."

class(x)
## [1] "character"

向量的產生和運算

使用c()函式即可產生一個向量。

1
2
3
4
x <- c(1, 2, 3)

class(x)
## [1] "numeric"

向量的元素命名

向量中的每一個元素都可以進行命名。可以在定義的時候直接命名或者在後期使用names()函式來進行命名。

1
2
3
4
5
6
7
8
9
x <- c(
age = 50,
chol = 220,
dbp = 84,
sbp = 132
) # 直接命名

y <- c(50, 220, 84, 132)
names(y) <- c("age", "chol", "dbp", "sbp") # names()函式命名

向量的下標與索引

類似於其他程式語言中數組可以透過下標Index訪問,向量也一樣可以。但是在R Lang中向量的下標系統相比其他語言更加強大。R Lang的下標支援正整數、負整數、文字、字串、邏輯向量。

正整數

第幾個元素。於其他程式語言相同。但是特別注意,R Lang中下標從1開始算起,而不是我們熟悉的從0開始。

1
2
3
x <- c(0:5) # 用:來定義從0~5
x[2]
## [1] 1

負整數

返回原向量中排除負整數後的向量結果

1
2
3
x <- c(0:5)
x[-c(2, 4)] # 從向量x中排除第2個和第4個元素
## [1] 0 2 4 5

文字

如果給向量元素命名過,我們就可以使用字串作為Index來查找對應的元素。

1
2
3
4
5
x <- c(0:5)
names(x) <- c("num0", "num1", "num2", "num3", "num4", "num5")
x[c("num2", "num4")]
## num2 num4
## 2 4

邏輯向量

直接將邏輯向量作為Index,以從目標向量中篩選出符合邏輯的子向量。

1
2
3
x <- c(0:5)
x[x > 3]
## [1] 4 5

向量基本算數運算

向量的算術運算包括以下符號:

符號 定義
+ 加法運算
- 減法運算
* 乘法運算
/ 除法運算
! 否定運算
^ 指數冪運算
%% 模運算
%/% 商運算
%*% 矩陣內積乘法
%o% 矩陣外積乘法
%x% 矩陣Kronecker乘法
%in% 配對運算

下面是例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1 + 1 # 加法
## [1] 2

2 - 1 # 減法
## [1] 1

2 * 2 # 乘法
## [1] 4

6 / 3 # 除法
## [1] 2

!TRUE # 否定
## [1] FALSE

2 ^ 3 # 指數冪
## [1] 8

6 %% 4 # 取模
## [1] 2

6 %/% 4 # 取商
## [1] 1

我這個數學廢物搞不明白什麼是矩陣的內積、外積和Kronecker乘法,所以高級的這些運算留到以後再補充吧。

向量關係比較操作

向量關係比較包括下列符號:

符號 定義
> 大於
< 小於
== 恆等於
!= 不等於
>= 大於等於
<= 小於等於

這些同其他的程式設計語言基本相同。看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2 > 1
## [1] TRUE

2 < 1
## [1] FALSE

3 == 3
## [1] TRUE

3 != 3
## [1] FALSE

4 >= 3
## [1] TRUE

4 <= 3
## [1] FALSE

向量的邏輯操作

向量的邏輯操作包括下列符號:

符號 定義
&, && 邏輯與
\ , \ \ 邏輯或
! 邏輯非
xor 邏輯互斥

這裡和其他程式語言依然是基本相同。直接上例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1 > 0 && 3 > 1
## [1] TRUE

1 > 0 || 3 < 0
## [1] TRUE

!TRUE
## [1] FALSE

x <- c(12, 24, 34)
y <- c(11, 36, 32)

xor((x - 12 > 0), (y - 10 > 0))
## [1] TRUE FALSE FALSE

這裡特別講一下互斥這個東西。這個東西叫“一身反骨”。當兩邊的算式都為TRUE或都為FALSE時,運算結果為FALSE,而當兩邊的運算結果一個是TRUE一個是FALSE時,運算結果是TRUE。即判斷兩個邏輯表達式結果是否不相同。

遺失值

遺失值通常用NA來表示。NaN表示非數值結果。NULL則表示空結果(物件長度為0)。

遺失值的判斷使用is.na()函式,is.nan()可以判斷元素是否是非數值。

移除遺失值則使用na.omit()na.fail()na.exclude()na.action()等函式。最常用的是complete.cases()函式。使用這個函式可以批量移除向量中的遺失值。需要注意的是,complete.cases()本質上返回一個logic類型的向量,用以表示目標向量中每一個值是否是遺失值。需要配合Index系統以進行移除。

1
2
3
4
5
6
7
8
9
x <- c(1, NA, 2, NA, 3)
is.na(x)
## [1] FALSE TRUE FALSE TRUE FALSE

complete.cases(x)
## [1] TRUE FALSE TRUE FALSE TRUE

x <- x[complete.cases(x)]
## 等效於 x <- x[!is.na(x)]

不難看出,complete.cases()函式的返回值事實上等於!is.na()的返回值。因此在移除遺失值方面,這二者是等效的。

類別變數 Factor

名目變數和有序變數

所謂的名目變數,或稱為名目尺度或無序變數,是指向量中的元素並沒有一個序列的尺度,同一向量中的每一個元素之間並沒有典型的量化大小的差別。例如人的五根指頭,就是五根指頭而已,並沒有說誰比誰高貴。

而有序變數則是指一個向量的元素之間是有序列尺度的。類別之間有“輕重”、“大小”、“強弱”之分。比如疼痛的等級可以分為“無”、“輕”、“中”、“重”。

類別資料及類別資料的因子變數

先來了解一下什麼叫做因子變數。因子變數Factor按照我粗淺的理解,暫時可以將其理解為一套分類及別名系統。

比如,我們現在有一個向量,儲存了我們一個月之內每天早上吃什麼的資料。比如我們在幾號吃了麵包和牛奶,幾號吃了油條和豆漿,幾號吃了小米粥。假設我們一個月的早餐就在這三種之間做迴圈。

那麼我們的向量當然可以寫作這樣子:

1
breakfast <- c("麵包和牛奶", "麵包和牛奶", "油條和豆漿", "小米粥", "油條和豆漿", ...)

不難看出,如果一個月的早餐都在這三種食物中迴圈的話,那麼我們的早餐就只有這三類。

我們不妨將其分類:

1
2
3
4
breakfast.factor <- factor(breakfast)
## 輸出breakfast.factor的值:
## [1] 麵包和牛奶 麵包和牛奶 油條和豆漿 小米粥 油條和豆漿
## Levels: 小米粥 油條和豆漿 麵包和牛奶

看到了沒?factor的意義實際上就是一個分類的、可以更改名字的向量

使用factor()函式來創建因子變數。該函式的定義如下:

1
factor(x = character(), levels, labels = levels, exclude = NA, ordered = is.ordered(x), nmax = NA)

解釋一下它的各個引數:

  • x:原始向量,通常是文字向量,如果是數值向量,則R先轉換成文字向量。
  • levels:類別水準,代表可能出現的分類
  • labels:類別水準的標記文字
  • exclude = NA:排除遺失值或某一特定值為一類別水準
  • ordered = is.ordered(x):設定因子物件類別水準的順序,仍是無序因子物件
  • nmax = NA:是否類別水準的最大數目

可是這樣子有幾個固有的問題:

  1. 書寫特別麻煩。每一個元素都是一個字串,就代表著我們要把同樣的字串寫很多遍,會超級麻煩。而且一旦哪一天寫成了錯別字,後續進行統計的難度會加大。
  2. 佔用空間比較多。字串在記憶體中所佔的空間比較大,會比較浪費。

鑑於此,我們試想一下有沒有更加合理的方法。當然有。比如我們可以搞一個密碼本,讓數字1代表麵包和牛奶,數字2代表油條和豆漿,數字3代表小米粥。這樣子我們就只需要記錄數字,輸出的時候轉換到字串就好了。

那麼這樣子,我們就可以給三個分類別名

1
2
3
4
5
6
7
breakfast <- c(1, 1, 2, 3, 2, ...) # 定義記錄值

breakfast.factor <- factor(breakfast, levels = c(1, 2, 3), labels = c("麵包和牛奶", "油條和豆漿", "小米粥"))

## 輸出breakfast.factor的值
## [1] 麵包和牛奶 麵包和牛奶 油條和豆漿 小米粥 油條和豆漿
## Levels: 麵包和牛奶 油條和豆漿 小米粥

我們可以看到在這時候,就會自動把我們的記錄值按照密碼本轉換成實際要的字串。其中,levels引數的向量元素和labels是一一對應的。並且factor儲存的仍然是之前的數字,而不是labels裡面的字串

可以使用levels()函式來修改level。

重新設定類無需因子的參照水準

在統計模型中常使用類別變數作為解釋變數,常常必須令無序因子物件或類別變數的某一個類別水準為參照水準 (reference level),以便建構類別型解釋變數內不同類別水準的對照比較 (contrast comparison)。 使用函式relevel(),可以改變無序因子類別水準的參考水準。

1
2
3
4
breakfast <- c(1, 1, 2, 3, 2, ...) # 定義記錄值

breakfast.factor <- factor(breakfast, levels = c(1, 2, 3), labels = c("麵包和牛奶", "油條和豆漿", "小米粥"))
relevel(breakfast.factor, ref = 2) # 將2作為參考水準

總之,當你有一大堆資料需要分類整理的時候,factor可能是一個好東西。