2020年7月30日 星期四

R 語言學習筆記 (五) : 資料框

在中斷兩年半之後, 我最近又恢復了 R 語言的學習, 原因是向母校借了一本陳旭昇老師寫的 "機率與統計推論" 這本書 (用 R 語言寫的), 覺得在讀這本書之前必須先把 R 語言的基礎建立起來才行, 所以暫停其它學研計畫, 集中心力在 R 的測試學習上. 上一次是停在因子這個科目上 (2018-01), 這次要從 R 語言最重要的資料型態 data.frame (資料框) 下手.

此前的 R 語言筆記參考 :

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

資料框 (data.frame) 是 R 語言用來處理表格資料 (tabular data) 的資料型別, 也是其資料物件中最常用的一種, 它相當於 EXCEL 中的試算表 (sheet), 或者是 Pandas 的 DataFrmae 物件. 資料框與矩陣一樣是二維的資料結構, 具有直行 (或稱為欄) 與橫列兩個維度, 直行代表統計學中的變數 (variables), 橫列則代表觀測值 (observations), 在資料庫中直行代表欄位 (fields), 橫列稱為紀錄 (records).

資料框物件名稱 data.frame 中的句點乍看有物件導向意味, 但這其實是因為 R 早期版本不允許底線作為變數名稱 (因為底線當時用作指派), 因此許多物件名稱採用句點作為分隔或串接字元. 目前的 R 版本雖然已允許底線作為識別字名稱, 但使用句點串接仍是比較好的方式 (一致的風格), 參考 :

R Variables and Constants

資料框的基本結構與矩陣一樣, 都是二維的資料結構, 每一行都是由長度相同的行向量組成, 它與矩陣或二維陣列不同之處主要有下列二個 :
  1. 資料框每一行可以是不同的資料型態; 而矩陣與陣列每個元素的型態必須相同. 
  2. 資料框的行與列都有名稱; 而矩陣與陣列只有索引而無名稱. 
簡言之, 資料框可以說是矩陣的性能擴充, 兩者都可以看成是由數個行向量 (欄) 組成, 只不過矩陣的每個行向量資料型態必須相同 (同質), 而資料框則可以不同 (異質), 亦即資料框可以由數個長度相同的數值向量, 文字向量, 或布林向量組成; 其次是資料框不論是行或列都有名稱 (name) 屬性, 矩陣的行列則無名稱, 只有索引位置.

操作資料框首先要建立 data.frame 物件 :


一. 建立 data.frame 物件的方法 :

1. 呼叫 data.frame() 函數 :

函數 data.frame() 的 API 如下 :

data.frame(..., row.names = NULL, check.rows = FALSE,
           check.names = TRUE, fix.empty.names = TRUE,
           stringsAsFactors = default.stringsAsFactors())

詳細說明可在 R Console 中以 ?data.frame 指令查得.

參數列最前面的 ... 表示可以傳入長度相同的向量, 因子, 數值矩陣, 串列等資料物件. 例如可以將長度相同的一組向量當作參數傳入函數 data.frame() 來建立資料框物件 :

df <- dataframe(V1, V2, V3, ...)

例如 :

> v1 <- c("Kelly", "Peter", "Amy")
> v2 <- c("Female", "Male", "Female")
> v3 <- c(18, 16, 14)
> df1 <- data.frame(v1, v2, v3)
> df1
     v1     v2 v3
1 Kelly Female 18
2 Peter   Male 16
3   Amy Female 14
> class(df1)
[1] "data.frame"        #物件類別是 data.frame
> typeof(df1)
[1] "list"                    #型態居然是 list

可見建立資料框時未指定行向量名稱, 預設會以行向量的程式變數名稱當作行向量的欄名 (即統計學中的觀測變數), 而列的名稱預設就是 1, 2, 3, .... 之連續數字. 因此, 資料框可視為一組相同長度行向量的組合, 組合不同資料結構形成資料框時, 必須注意資料長度是否相同, 例如上例若加入元素多一個的 married 向量就會出現錯誤 :

> v1 <- c("Kelly", "Peter", "Amy")
> v2 <- c("Female", "Male", "Female")
> v3 <- c(18, 16, 14)
> v4 <- c(FALSE, FALSE, FALSE, FALSE)    #長度多 1 個> df1 <- data.frame(v1, v2, v3, v4)
Error in data.frame(v1, v2, v3, v4) : 
  arguments imply differing number of rows: 3, 4   

呼叫內建函數 View() 會開啟一個視窗以表格顯示資料框內容, 與 EXCEL 的試算表類似 :

> View(df1)   




可見這是一個維度是 3*3 (3 列 3 行) 的資料框, 呼叫 dim(), nrow(), 以及 ncol() 函數會分別傳回資料框的維度, 列數, 以及行數, 例如 :

> dim(df1)        #傳回維度
[1] 3 3                    #3 列 3 行
> nrow(df1)      #傳回列數
[1] 3
> ncol(df1)        #傳回行數
[1] 3

在建立資料框時也可以指定欄位名稱, 這樣就不會使用預設之行向量名稱了, 例如 :

> df2 <- data.frame(name=v1, gender=v2, age=v3)
> df2
   name gender age      #指定之變數 (行向量) 名稱
1 Kelly Female  18
2 Peter   Male  16
3   Amy Female  14

可見指定欄位名稱後, 預設的欄名 (變數名稱) v1, v2, v3 就被 name, gender, age 取代了.

呼叫 str() 函數可觀察資料框結構, 每個 $ 符號後面就是欄位名稱, 然後跟著的是欄位元素之資料型別與內容 :

> str(df1)
'data.frame':   3 obs. of  3 variables:
 $ v1: Factor w/ 3 levels "Amy","Kelly",..: 2 3 1
 $ v2: Factor w/ 2 levels "Female","Male": 1 2 1
 $ v3: num  18 16 14
> str(df2)
'data.frame':   3 obs. of  3 variables:
 $ name  : Factor w/ 3 levels "Amy","Kelly",..: 2 3 1
 $ gender: Factor w/ 2 levels "Female","Male": 1 2 1
 $ age   : num  18 16 14

可見 df2 指定欄位名稱後與 df1 的預設欄名不同. 其次要注意的是, 若變數 (欄) 為文字向量, 則資料框預設會自動將其轉換為 Factor 因子變數, 例如上面 v1, v2 與 name, gender 變數後面顯示其為 Factor 向量. 如果要阻止自動轉換, 可在建立資料框時指定 stringAsFactor 參數為 FALSE 即可, 例如 :

> df1 <- data.frame(v1, v2, v3, stringAsFactor=FALSE)
> str(df1)
'data.frame':   3 obs. of  4 variables:
 $ v1            : chr  "Kelly" "Peter" "Amy"
 $ v2            : chr  "Female" "Male" "Female"
 $ v3            : num  18 16 14
 $ stringAsFactor: logi  FALSE FALSE FALSE

可見 v1, v2 這兩個變數之資料型態獲得保留為字元 (chr). 呼叫 View() 函數顯示資料框結構時也會標示這個屬性值為 FALSE, 例如 :




呼叫 names() 或 colnames() 函數會傳回資料框的欄名, 呼叫 rownames() 則傳回列名, 例如 :

> names(df1)
[1] "v1" "v2" "v3"
> names(df2)                      #傳回欄位名稱
[1] "name"   "gender" "age" 
> colnames(df1)                 #傳回欄位名稱
[1] "v1" "v2" "v3"
> colnames(df2)                 #傳回欄位名稱
[1] "name"   "gender" "age" 
> rownames(df1)               #傳回列名稱
[1] "1" "2" "3"
> rownames(df2)                #傳回列名稱
[1] "1" "2" "3"

除了在建立資料框時指定欄位名稱外, 也可以利用內建函數 colnames() 透過向量索引改變欄位名稱, 例如 :

> df3 <- df1    #複製資料框 df1 至 df3
> df3
     v1     v2 v3
1 Kelly Female 18
2 Peter   Male 16
3   Amy Female 14
> colnames(df3)[c(1,2,3)] <- c("name", "gender", "age")      #更改欄位 1, 2, 3 的名稱
> df3
   name gender age                         #欄位名稱已被更改
1 Kelly Female  18
2 Peter   Male  16
3   Amy Female  14

同樣地, 也可以利用 colnames() 函數更改列名稱, 例如 :

> rownames(df3)[c(1,2,3)] <- c("R1", "R2", "R3")       #更改列索引 1, 2, 3 的名稱
> df3
    name gender age
R1 Kelly Female  18
R2 Peter   Male  16
R3   Amy Female  14

以矩陣建立資料框時, 需注意每個矩陣之列數必須相同, 且須等於其他向量變數的長度, 例如下面的資料框 df4 由一個數值向量 v, 兩個矩陣 m1, m2, 以及一個布林向量 b 組成, 那麼它們的列長度必須一樣才行 :

> v <- c(9:10)                                     #向量 (2 列)
> m1 <- matrix(1:6, nrow=2)             #矩陣 (2 列)
> m2 <- matrix(7:12, nrow=2)           #矩陣 (2 列)
> b <- c(TRUE, FALSE)                    #向量 (2 列)
> df4 <- data.frame(v, m1, m2, b)
> df4
  v X1 X2 X3 X1.1 X2.1 X3.1    b
1  9  1  3  5    7    9   11  TRUE
2 10  2  4  6    8   10   12 FALSE

矩陣匯入到資料框時會將矩陣的各行 (欄) 轉成資料框的變數, 預設會將矩陣的每一行以 "X" 開頭依序命名, 例如 X1, X2, X3, ... 第二個矩陣為 X1.1, X2.1, X3.1, ... 等等, 呼叫 str() 可觀察其資料結構 :

> class(df4)
[1] "data.frame"
> str(df4)
'data.frame':   2 obs. of  8 variables:
 $ v  : int  9 10
 $ X1  : int  1 2
 $ X2  : int  3 4
 $ X3  : int  5 6
 $ X1.1: int  7 8
 $ X2.1: int  9 10
 $ X3.1: int  11 12
 $ b  : logi  TRUE FALSE
> colnames(df4)             #傳回資料框欄名向量
[1] "v"    "X1"   "X2"   "X3"   "X1.1" "X2.1" "X3.1" "b"  

可見第一個矩陣 m1 的三行依序被命名為 X1, X2, X3; 而矩陣 m2 的三行則依序被命名為 X1.1, X2.1, 與 X3.1 等等.


2. 呼叫 as.dataframe() 建立資料框物件 :

資料框的資料來源可以是儲存於串列中的向量, 這時可呼叫 as.data.frame() 函數將串列轉成資料框, 例如 :

> v1 <- c("Kelly", "Peter", "Amy")
> v2 <- c("Female", "Male", "Female")
> v3 <- c(18, 16, 14)
> lst <- list(name=v1, gender=v2, age=v3)
> df5 <- as.data.frame(lst)      #將串列轉成資料框
> class(df5)
[1] "data.frame"
> df5
   name gender age
1 Kelly Female  18
2 Peter   Male  16
3   Amy Female  14

可見串列中的每一項相當於一個行向量, 而 as.data.frame() 會將串列轉成資料框, 串列中的 key 成為欄名 (即變數).


3. 呼叫 do.call() 建立資料框物件 :

如果資料來源不是以行向量呈現, 而是儲存在串列中觀測值 (列資料), 每一筆的觀測值以單列資料框儲存於列表中, 則可利用 do.call() 函數呼叫 rbind() 來將這些列資料組合成資料框, 例如 :

> ob1 <- data.frame(c("Kelly"), c("Female"), c(18))
> ob2 <- data.frame(name=c("Peter"), gender=c("Male"), age=c(16))
> ob3 <- data.frame(name=c("Amy"), gender=c("Female"), age=c(14))
> df6 <- do.call(rbind, list(ob1, ob2, ob3))
> class(df6)
[1] "data.frame"
> df6
   name gender age
1 Kelly Female  18
2 Peter   Male  16
3   Amy Female  14


二. 存取資料框元素 :

存取資料框的內容有兩種方式 :
  • 使用中括號 [列索引, 行索引] 存取整列, 整行, 或某行某列元素
  • 使用 $行名 存取整欄 (行)
  • 使用雙重中括號 [[行索引]]
注意, 中括號內第一個索引為列索引, 第二個是行索引, 行索引空白表示存取整列; 列索引空白表示存取整行.

1. 使用中括號 [列, 行] 存取 : 

此種方式可以存取整行, 整列, 或指定儲存格元素, 例如 :

> df2
   name gender age
1 Kelly Female  18
2 Peter   Male  16
3   Amy Female  14
> df2[1,]                                     #取得第 1 列
   name gender age
1 Kelly Female  18
> df2[2,]                                     #取得第 2 列
   name gender age
2 Peter   Male  16
> df2[3,]                                     #取得第 1 列
  name gender age
3  Amy Female  14
> df2[, 1]                                    #取得第 1 行
[1] "Kelly" "Peter" "Amy"
> df2[, 2]                                    #取得第 2 行
[1] "Female" "Male"   "Female"
> df2[, 3]                                    #取得第 3 行
[1] 18 16 14
> df2[1, 1]                                  #取得第 1 列第 1 行
[1] "Kelly"
> df2[2, 2]                                  #取得第 2 列第 2 行
[1] "Male"
> df2[3, 3]                                  #取得第 3 列第 3 行
[1] 14


2. 使用 $行名 存取 : 

此種方式可存取整行之變數資料 :

> df2
   name gender age
1 Kelly Female  18
2 Peter   Male  16
3   Amy Female  14
> df1$v1                                    #取得名稱為 v1 之變數 (整行)
[1] "Kelly" "Peter" "Amy"           
> df1$v2                                    #取得名稱為 v2 之變數 (整行)
[1] "Female" "Male"   "Female"
> df1$v3                                    #取得名稱為 v3 之變數 (整行)
[1] 18 16 14


3. 使用雙重中括號 [[行索引]] : 

此種方式也是可存取整行之變數資料, 但使用是行索引來定位 :

> df2
   name gender age
1 Kelly Female  18
2 Peter   Male  16
3   Amy Female  14
> df1[[1]]                                      #取得第 1 行整行變數
[1] "Kelly" "Peter" "Amy"   
> df1[[2]]                                      #取得第 2 行整行變數
[1] "Female" "Male"   "Female"
> df1[[3]]                                      #取得第 3 行整行變數
[1] 18 16 14

下面是利用賦值運算符改變資料框內容的範例 :

> df2
   name gender age
1 Kelly Female  18
2 Peter   Male  16
3   Amy Female  14
> df2[1, 3] <- 19
> df2
   name gender age
1 Kelly Female  19
2 Peter   Male  16
3   Amy Female  14
> df2[, 3] <- c(20, 18, 16)
> df2
   name gender age
1 Kelly Female  20
2 Peter   Male  18
3   Amy Female  16
> df2[3, ] <- c("Tony", "Male", 55)
> df2
   name gender age
1 Kelly Female  20
2 Peter   Male  18
3  Tony   Male  55
> df2$age <- c(21, 19, 56)
> df2
   name gender age
1 Kelly Female  21
2 Peter   Male  19
3  Tony   Male  56


三. 資料框常用函數 : 

上面所使用之資料框函數如下表 :


 data.frame 常用函數 說明
 data.frame(v1, v2, ...) 將行向量 v1, v2, ... 組合資料框物件
 names(df) 傳回資料框 df 的行名 (欄, 變數名)
 rownames(df) 傳回資料框 df 的列名 
 str(x) 顯示物件結構與內容
 length(x) 顯示陣列元素個數
 dim(x) 顯示物件 (矩陣, 陣列, 資料框) 維度 : 列, 欄
 nrow(df) 傳回資料框 df 之列數
 ncol(df) 傳回資料框 df 之行 (欄) 數



參考 :

R 語言基礎

沒有留言 :