2018年1月31日 星期三

R 語言學習筆記 (四) : 因子

進入 2018 年後工作項目與同事交換, 得重新學習與整理 SOP (我是 SOP 控啊),  R 語言的學習速度就慢下來了, 只能撿夾縫中的五分鐘學習, 以下是最近對因子物件的測試紀錄. 此前的筆記如下 :

R 語言安裝
在樹莓派上安裝 R 語言
R 語言學習筆記 (一) : 基本語法與向量
R 語言學習筆記 (二) : 矩陣
R 語言學習筆記 (三) : 陣列

因子是 R 語言中用來儲存類型變數 (Categorical) 的資料結構. 統計學將變數區分為如下四種 :

 類型 說明 範例
 名義變數 只區別名稱, 無順序之分, 無法排序的變數 男女, 優劣, 晴陰雨
 順序變數 有排序意義的變數 名次, 歌曲排行榜
 離散變數 以整數為計數單位的變數, 其差距有意義 每周外食次數
 連續變數 可連續取值之變數 身高, 體重

其中名義型變數與順序型變數具有可分類或可分級概念, 又稱為類型資料 (Categorical Data), 在 R 語言是以 factor (因子, 等級) 資料結構來儲存, 其中可排序的稱為有序因子 (ordered factor). 因子在 R 語言中是一種特殊的向量類型, 其內容之資料類型是一組表示名義或順序屬性的數值與字串, 例如 "男", "女" 或 1, 2, 3 等.


一. 建立因子物件的方法 : 

1. 呼叫 factor() 函數 : 

factor() 的 API 如下 :

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

其中 :

x=向量 (通常是表示名義變數的字串或數值向量)
levels=設定之因子等級  (通常是字串或數值向量)
labels=因子數值加上標籤 (預設為 levels)
exclude=要排除的因子等級 (預設 NA 不排除)
ordered=因子是有序或無序 (預設 FALSE 無序)
nmax=因子等級最大值 (預設 NA 無最大值)

其中最主要的是前兩個參數, 即向量 x 與因子 levels (等級).

傳入向量 x, factor() 可將其轉換成因子. 例如 :

> grade1 <- c("A","B","A","C","B")
> fac1 <- factor(grade1)
> fac1
[1] A B A C B
Levels: A B C         #因子等級為不重複之元素 (沒有雙引號)

可見顯示因子變數時, 不僅會顯示內容, 還會顯示這些元素的 "等級 (Level)", 即不重複的部分. 這些等級 (level) 預設是以字母順序排列的, 因子與向量不同之處是因子會將元素分群 (grouping), 相同的元素在一群就是一個因子, 因子用來儲存元素的類別 (Category).

用 class() 檢查因子變數可知其資料結構為 factor, 用 mode() 檢查其資料型態卻是 numeric, 這表示 Levels 的資料型態是數值, 雖然 Levels 看起來像是字串, 但實際上 R 語言內部是用整數索引來代表元素之值與等級, 可用 as.numeric() 或 as.integer() 函數得知.

> class(grade1)
[1] "character"
> mode(grade1)    #字串向量元素資料型態為 character
[1] "character"
> class(fac1)         #因子之資料結構為 factor
[1] "factor"
> mode(fac1)
[1] "numeric"            #因子等級其實是用整數索引儲存的

呼叫 str() 會顯示因子變數之結構, 可印證因子元素都是以整數索引儲存 :

> fac1 <- factor(c("A","B","A","C","B"))
> str(fac1)
 Factor w/ 3 levels "A","B","C": 1 2 1 3 2 

可見等級 A, B, C 分別以 1, 2, 3 儲存, 因此因子變數之元素在內部實際上是儲存索引值 1 2 1 3 2.

由於因子變數之值通常是數值, 字串, 或布林值, 因此可呼叫 as.charactor(), as.numeric(), 與 as.logical() 將因子變數分別轉成字串向量, 數值向量, 或布林向量, 例如 :

> fac1 <- factor(c("A","B","A","C","B"))
> fac1
[1] A B A C B
Levels: A B C
> as.character(fac1)
[1] "A" "B" "A" "C" "B"
> as.integer(fac1)
[1] 1 2 1 3 2
> as.logical(fac1)
[1] NA NA NA NA NA     #因 "A", "B", "C" 不是布林值
> as.logical(factor(c("TRUE","FALSE")))
[1]  TRUE FALSE

不過在 "R 語言 : 邁向 Big Data 之路 (上奇, 洪錦魁)" 一書中提到, 由於因子變數之元素值在 R 語言內部以 1, 2, 3, ... 等整數儲存, 對於元素為數字之因子變數而言, 使用 as.integer() 轉換時可能會有計算上的問題, 例如下列成績資料 :

> scores <- factor(c(78,99,67,56,100,23))
> scores
[1] 78  99  67  56  100 23
Levels: 23 56 67 78 99 100
> str(scores)
 Factor w/ 6 levels "23","56","67",..: 4 5 3 2 6 1
> as.numeric(scores) 
[1] 4 5 3 2 6 1 

可見將因子用 as.numeric() 轉回數值向量時, 傳回的是因子變數內部代表元素的 1, 2, 3, 4, 5 索引值, 而非原始的成績. 這時可先呼叫 as.character() 再呼叫 as.numeric 來避免運算上的錯誤, 因為 as.charactor() 會傳回因子變數元素值之字串向量, 而非元素索引之字串向量 :

> as.character(scores)                        #as.character() 會傳回元素之字串向量
[1] "78"  "99"  "67"  "56"  "100" "23"
> as.numeric(as.character(scores))    #先呼叫 as.character()  轉成字串向量
[1]  78  99  67  56 100  23

可見傳回的是原始成績之數值向量而不是 1, 2, 3 ... 的索引了.

factor() 函數的第二參數 levels 用來列舉因子變數有哪些的等級. 雖然 factor() 會從元素的分群中自動找出因子的等級, 但當收集的數據不完整時, 等級 (Levels) 就會有缺漏, 例如 :

> seasons <-c("Spring", "Winter", "Spring", "Summer")
> fac2 <- factor(seasons)
> fac2
[1] Spring  Winter Spring  Summer
Levels: Spring Summer Winter              #等級按照字母順序排列

可見由於原始資料缺了 "Fall", 因此也會使等級缺了 "Fall" 這一項, 這時可利用 levels 參數指定完整的等級有哪些, 且等級會按照 levels 參數指定之順序排列 :

> fac3 <- factor(seasons, levels=c("Winter","Fall","Summer","Spring"))
> fac3
[1] Spring  Winter Spring  Summer
Levels: Winter Fall Summer Spring        #等級按照 levels 參數指定之順序排列

注意, 以 levels 指定等級時, 顯示變數時會按照所指定之順序排列而非預設之按字母順序. 注意, 若指定的 levels 參數不是完整的因子序列, 則顯示因子變數時, 在 levels 中找不到等級之元素會顯示 NA, 例如 :

> ordered(c("A","B","A","C","B"),levels=c("C","B"))
[1] <NA> B    <NA> C    B 
Levels: C < B

呼叫 levels() 函數會傳回一個字串向量, 其元素為因子變數的全部等級. 注意, 即使等級是數值, levels() 也是傳回字串向量, 例如 :

> fac4 <- factor(c("A","B","B","C"))    #元素為字串
> fac4
[1] A B B C
Levels: A B C                                #沒有雙引號
> fac5 <- factor(c(1,2,2,3))   #元素為數值
> fac5
[1] 1 2 2 3
Levels: 1 2 3                                  #沒有雙引號
> levels(fac4)
[1] "A" "B" "C"
> levels(fac5)
[1] "1" "2" "3"                               #有雙引號 : levels() 一律傳回字串向量

函數 nlevels() 則會傳回因子有幾個等級, 例如 :

> grade1 <- c("A","B","A","C","B")
> fac1 <- factor(grade1)
> fac1
[1] A B A C B
Levels: A B C
nlevels(fac1)      #傳回因子等級數目
[1] 3                            #含有 A, B, C 三個等級

factor() 函數預設使用元素本身作為等級之值, 參數 labels 可搭配 levels 參數來變更因子的顯示標籤, 例如以 "E", "W", "S", "N" 縮寫來代替 "EAST", "WEST", "SOUTH", "NORTH" :

> directions <- c("EAST", "WEST", "SOUTH", "NORTH", "WEST", "NORTH")
> fac6 <- factor(directions,levels=c("EAST","WEST","SOUTH","NORTH"),labels=c("E", "W", "S", "N"))
> fac6
[1] E W S N W N   
Levels: E W S N    

可見不論元素或等級之值都被對應之 label 所取代了. 如果 labels 不是對應 levels 的字串向量, 而是單一字串, 則會以該字串冠上元素的等級數值作為新的元素與等級, 例如 :

>directions <- c("EAST", "WEST", "SOUTH", "NORTH", "WEST", "NORTH")
> fac7 <- factor(directions)
> str(fac7)
 Factor w/ 4 levels "EAST","NORTH",..: 1 4 3 2 4 2
> fac8 <- factor(directions, labels="d")          #會冠在等級數值前面
> str(fac8)
 Factor w/ 4 levels "d1","d2","d3",..: 1 4 3 2 4 2 
> fac8
[1] d1 d4 d3 d2 d4 d2      #元素被改掉了
Levels: d1 d2 d3 d4         #等級也被改掉了

呼叫 tables() 函數會傳回一個 table 物件, 顯示各個等級的次數統計, 例如 :

> fac1 <- factor(c("A","B","A","C","B"))
> table(fac1)
fac1
A B C
2 2 1                                #levels 的等級統計
> class(table(fac1))
[1] "table"                       #傳回 table 物件
> str(table(fac1))
 'table' int [1:3(1d)] 2 2 1
 - attr(*, "dimnames")=List of 1
  ..$ fac1: chr [1:3] "A" "B" "C"

傳回的 table 物件可以用索引存取, 例如 :

> tab1 <- table(fac1)
> tab1[1]       #以索引存取 table 物件
A
2
> tab1[1] == 2
   A
TRUE

factor() 的 ordered 參數預設為 FALSE, 表示預設建立無序因子, 若設為 TRUE 表示所建立的因子是有序的, 例如 : 

> fac1 <- factor(c("A","B","B","C"))
> fac1
[1] A B B C
Levels: A B C
> fac7 <- factor(c("A","B","B","C"), ordered=TRUE)    #有序因子
> fac7
[1] A B B C
Levels: A < B < C      #等級是有順序的

可見有序因子的等級 (levels) 是照字母順序排序的. 


2. 呼叫 as.factor() : 

除了用 factor() 建立因子外, 也可以用 as.factor() 函數將向量轉成因子, 例如 :

> grade1 <- c("A","B","A","C","B")
> fac1 <- factor(grade1)
> fac1
[1] A B A C B
Levels: A B C
> fac2 <- as.factor(grade1)
> fac2
[1] A B A C B
Levels: A B C
> class(fac2)
[1] "factor"
> identical(fac1, fac2)    #比較是否為相同物件
[1] TRUE

用 identical() 函數比較可知, 用 factor() 與 as.factor() 所建立之因子物件是雷同的 (結構與內容都一樣). 由於因子的元素通常為字串, 數值, 或布林, 因此可以用 as.character() 將因子物件轉型為字串向量, 用 as.integer() 與 as.numeric() 函數將因子物件轉為數值向量, 或用 as.logical() 轉成布林向量, 例如 :

> grade2 <- as.character(fac1)
> grade2
[1] "A" "B" "A" "C" "B"
> identical(grade1, grade2)
[1] TRUE
> grade3 <- as.numeric(fac1)
> grade3
[1] 1 2 1 3 2
> grade4 <- as.integer(fac1)
> grade4
[1] 1 2 1 3 2

可見因子的等級 A, B, C 分別對應到 1, 2, 3 這三個整數, 由 mode() 可知等級實際上在 R 語言內部是以整數儲存.


2. 呼叫 ordered() 建立有序因子 (ordered factor) : 

有序的類別型資料 (即統計學中的順序變數) 可用有序因子處理, 此函數 API 如下 :

ordered(x, ...)

其中參數 x 為原生向量 (數值, 字串, 布林等), 其餘參數 ... 與 factor() 參數一樣. 例如 :

> ordered(c("A","B","A","C","B"))
[1] A B A C B
Levels: A < B < C         #等級按字母順序升序排列
> ordered(c("A","B","A","C","B"),levels=c("C","B","A"))    #指定等級序列
[1] A B A C B
Levels: C < B < A         #按 levels 參數指定之等級排序       
> ordered(c(1,2,1,3,2))
[1] 1 2 1 3 2
Levels: 1 < 2 < 3                 
> ordered(c(TRUE,FALSE,TRUE,TRUE,FALSE))
[1] TRUE  FALSE TRUE  TRUE  FALSE
Levels: FALSE < TRUE         

可見有序因子的等級與一般因子不同之處在於多了方向標誌 "<", 若沒有用 levels 參數指定等級順序, 預設為按字母排列. 呼叫 class() 可以看出有序因子多了 "ordered" :

> class(ordered(c(1,2,1,3,2)))     
[1] "ordered" "factor"                     #物件類型為有序因子
> class(factor(c(1,2,1,3,2)))
[1] "factor"                                      #物件類型為一般因子

事實上, 這與呼叫 factor() 時使用 ordered=TRUE 參數建立之因子變數是完全一樣的, 例如 :

> fac3 <- ordered(c("A","B","A","C","B"))
> fac3
[1] A B A C B
Levels: A < B < C
> fac4 <- factor(c("A","B","A","C","B"), ordered=TRUE)
> fac4
[1] A B A C B
Levels: A < B < C
> identical(fac3, fac4)
[1] TRUE


二. 存取因子變數 :

因子變數其實就是添加了等級屬性的向量物件, 存取元素之方法與向量一樣都是使用索引 [], 例如 :

> fac1 <- factor(c("A","B","A","C","B"))
> fac1
[1] A B A C B
Levels: A B C
> fac1[1]
[1] A
Levels: A B C
> fac1[5]
[1] B
Levels: A B C
> fac1[2:4]
[1] B A C
Levels: A B C

更改因子變數之元素值, 所賦予之值必須在等級 (levels) 範圍內, 否則會出現錯誤, 例如 :

> fac1[1] <- c("K")       #以 "K" 替換第一元素失敗 : 不在等級範圍內
Warning message:
In `[<-.factor`(`*tmp*`, 1, value = "K") :
  invalid factor level, NA generated
> fac1[1] <- c("C")        #第一元素改為 "C"
> fac1
[1] C B A C B
Levels: A B C

函數 levels() 會將因子之等級以向量傳回, 因此也可用 [] 索引來存取因子等級 :

> fac1 <- factor(c("A","B","A","C","B"))
levels(fac1)                        #傳回因子變數之全部等級
[1] "A" "B" "C"
> class(levels(fac1))             #levels() 傳回字串向量
[1] "character"
> levels(fac1)[1]                   #顯示指定之等級分量
[1] "A"
> levels(fac1)[2]
[1] "B"
> levels(fac1)[3]
[1] "C"
> levels(fac1)[1] <- c("D")    #改變因子等級
> fac1
[1] D B D C B                #等級 A 被改成 D 了
Levels: D B C


三. 因子常用函數 :

因子變數常用函數整理如下表 :

 函數 說明
 factor(x, levels, ...) 將向量 x 轉型為因子物件
 gl(n, k) 建立一個含有 k 個等級
 str(x) 顯示物件結構與內容
 length(x) 顯示陣列元素個數
 dim(x) 顯示物件 (矩陣, 陣列, 資料框) 維度
 is.factor(x) 檢查是否為因子類型
 as.factor(x) 將向量轉型為因子類型
 as.numeric(x) 將因子轉型為數值類型
 as.integer(x) 將因子轉型為整數類型
 as.charactor(x) 將因子轉型為字串類型
 identical(x, y) 檢查兩個物件 x, y 是否為相同物件
 levels(x) 傳回因子 x 之水平 (等級) 向量
 nlevels(x) 傳回因子 x 之水平 (等級) 數量


3 則留言 :

柳丁 提到...

謝謝 最後兩個常用函數的解釋"水平" 看不懂? 是等級嗎?

小狐狸事務所 提到...

嗨, 柳丁, LEVEL 應該是譯為等級較妥, 水平一詞來自手邊幾本中文書, 譯者大概認為不同之等級一如不同之水平, 且與原文字面意思較近, 故也就沿用了.

柳丁 提到...

感恩!