2016年8月26日 星期五

Advance-R 筆記:Data structures


Data Structures

各種資料型態的維度,與異/均值性

Attributes

所有的 objects 都可以任意加上屬性,也就是添加這筆資料的 metadata 。
我們可以透過使用 attr() 函式,或者 attributes() (用在 list 上)來呼叫該物件擁有的屬性
y = 1:10
# 對 y 指定一個叫做 my_attribute 的屬性,內容是 This is a vector
attr(y, "my_attribute") = "This is a vector"
attr(y, "my_attribute")
## [1] "This is a vector"
str(attributes(y))
## List of 1
##  $ my_attribute: chr "This is a vector"
在預設狀況下,當 vector 修改過之後, attributes 會消失
attributes(y[1])
## NULL
attributes(sum(y))
## NULL
有三種重要的狀況,是就算對該物件做修改後,不會丟失 attributes
  • Names, a character vector giving each element a name
  • Dimensions, used to turn vectors into matrices and arrays
  • Class, used to implement the S3 object system
以上這三種狀況,他們的 attributes可以透過特殊的 accessor function 取得,例如names(x), dim(x), class(x)等。

Name

可以透過以下方法來為 vector 命名
x <- c(a = 1, b = 2, c = 3)
x <- 1:3; names(x) <- c("a", "b", "c")
x <- setNames(1:3, c("a", "b", "c"))
要注意的是,vector 的 names 不一定要各自 unique ,但是在取子集 (subsetting) 、查詢資料時,若 names 都是 unique,會比較容易操作
命名也可以為空
y <- c(a=1, 2, 3)
names(y)
## [1] "a" ""  ""
z <- c(1, 2, 3)
names(z)
## NULL

Factors

屬性的一個重要功能,就是可以定義 factor 。 factor 是一種包含了預定義值的 vector (例如性別只能出現男/女,無論政治正不正確),可以用來儲存分類資料 (categorical data) 。
integer vectors 可以透過兩種 attributes 建立 factors: - class() :會使得該 vector 的行為和一般 integer vectors 截然不同,對 vector 使用 class 時可以看到它是 factor - levels() :定義了 set of allowed values
x <- factor(c("a","b", "b", "a"))
x
## [1] a b b a
## Levels: a b
class(x)
## [1] "factor"
levels(x)
## [1] "a" "b"
一旦指定了 levels() 限定的值以外的值,會出現錯誤
x[2] <- "c"
## Warning in `[<-.factor`(`*tmp*`, 2, value = "c"): invalid factor level, NA
## generated
使用 factors 的好處是,比較能控制分組狀況,而不是用隨便讓資料無限增生各種字串
sex_char <- c("m", "m" ,"m")
sex_factor <- factor(sex_char, levels=c("m", "f"))

table(sex_char)
## sex_char
## m 
## 3
table(sex_factor)
## sex_factor
## m f 
## 3 0
在 R 裡面的 loading functions 常常會自動將 character vectors 轉為 factors ,其實這樣並不是很好,因為 there’s no way for those functions to know the set of all possible levels or their optimal order. (應該是指,如果未來要添加不在被預設的 levels 的值會出問題)。所以建議在讀取資料時,例如 read.csv(..., stringsAsFactor = FALSE) 以關閉這個強制轉換為 factor 的行為,再根據你對資料的專業知識,自己設定該欄位是擁有哪些 levels 的 factor 。
雖然 factors 看起來、操作起來很像 character vectors ,但它的本質仍是 integers ,所以不要亂用操作 strings 的函式去弄他,會噴出 error ,然後崩潰找不到問題在哪裡。
不過有些方法,例如 gsub()grepl() 會將 factors 強制轉型為 strings。

Matrices and arrays

把一個 atomic vector 加上 dim() 屬性,就會讓它的性質類似 a multi-dimensional array
而更特別的 array 則是 matrix ,他擁有兩個軸 (dimension) 。
矩陣 Matrices 通常會用在數學、統計方面,而 array 則更罕用。
他們可以透過 matrix()array() 函式來製造,或者使用 dim()
# 利用兩個純量參數來定義 rows 和 columns
a <- matrix(1:6, ncol=3, nrow=2)

# 用一個 vector 參數來描述所有的 dimensions
b <- array(1:12, c(2,3,2))

# 也可以事後透過 dim() 來改變物件
c <- 1:6
# 將 c 指定為 3*2 的矩陣
dim(c) <- c(3,2)
c 
##      [,1] [,2]
## [1,]    1    4
## [2,]    2    5
## [3,]    3    6
# 將 c 指定為 2*3 的矩陣
dim(c) <- c(2,3)
length()names() 在多維的狀況下,要換成: - 想看各軸向的 length ,在 matrices 時要用 nrow()dim() , 在 arrays 時要用 dim() - 想看各維度的 names ,在matrices 時要用 rownamescolnames() , 在 arrays 時要用 dimnames() ,會是 list形式的 character vectors
a <- matrix(1:6, ncol=3, nrow=2)
length(a)
## [1] 6
nrow(a)
## [1] 2
ncol(a)
## [1] 3
rownames(a) <- c("A", "b")
colnames(a) <- c("a", "b", "c")
a
##   a b c
## A 1 3 5
## b 2 4 6
b <- array(1:12, c(2,3,2))
length(b)
## [1] 12
dim(b)
## [1] 2 3 2
dimnames(b) <- list(c("one", "two"), 
                    c("a", "b", "c"), 
                    c("A", "B"))
b
## , , A
## 
##     a b c
## one 1 3 5
## two 2 4 6
## 
## , , B
## 
##     a  b  c
## one 7  9 11
## two 8 10 12
  • c() 在多維的狀況下,在 matrices 時要用 rbind()cbind()
  • 在 array 的狀況下,要用 abind package 提供的 abind()
  • 將 matrix 轉置可以用 t(),在 array 的狀況下是使用 aperm()

Data frames

Data frame 是 R 語言中最常用來儲存資料的方法,系統性的正確使用可以讓資料分析快樂又迅速。
其實 data frame 是由一群相同長度的 vectors 所組成的 list ,因此它擁有 2-dimensional 的結構,也有著和 matrix 與 list 一樣的特性。
也就是說,我們可以將 names()colnames()rownames() 使用在 data frame 上。 使用length() 在 data frame 上的結果相等於 ncol(),而 nrow() 則會回傳 rows 的數量。

Creating Data Frame

df <- data.frame(x=1:3, y=c("a", "b", "c"))
str(df)
## 'data.frame':    3 obs. of  2 variables:
##  $ x: int  1 2 3
##  $ y: Factor w/ 3 levels "a","b","c": 1 2 3
要特別注意的是, data.frame 也會自動將 strings 轉為 factors 形式,可以用前面提過的 stringAsFactors = FALSE 逼迫他停止這個行為
df <- data.frame(
  x = 1:3,
  y = c("a", "b", "c"),
  stringAsFactors = FALSE
)
str(df)
## 'data.frame':    3 obs. of  3 variables:
##  $ x              : int  1 2 3
##  $ y              : Factor w/ 3 levels "a","b","c": 1 2 3
##  $ stringAsFactors: logi  FALSE FALSE FALSE

Testing and Coercion

data.frame 是一種 S3 class ,我們可以使用 class()或者 is.data.frame() 來檢查物件是否是 data frame
typeof(df)
## [1] "list"
class(df)
## [1] "data.frame"
is.data.frame(df)
## [1] TRUE

Combining data frames

利用 cbind()rbind 將 data frames 合併
df <- data.frame(
  x = 1:3,
  y = c("a", "b", "c"),
  stringsAsFactors = FALSE)
cbind(df, data.frame(z = 3:1))
##   x y z
## 1 1 a 3
## 2 2 b 2
## 3 3 c 1
rbind(df, data.frame(x = 10, y = "z"))
##    x y
## 1  1 a
## 2  2 b
## 3  3 c
## 4 10 z
要特別注意的是,當我們要合併兩個 data frames 的 column 時 (column-wise) ,rows 的數量一定要相同才能合併;而合併兩個 data frames 的 rows 時, columns 和 rows 數都要一樣。可以用 plyr::rbind.fill() 來合併兩個不同數量 columns 的 data frames

Special Columns

因為 data frame 是 a list of vectors,所以 data frame 的任何欄位內容可以是 list
df <- data.frame(x = 1:3)
df$y <- list(1:2, 1:3, 1:4)
df
##   x          y
## 1 1       1, 2
## 2 2    1, 2, 3
## 3 3 1, 2, 3, 4
但是當一個 list 被指定到 data.frame() 時,他會將每個 list 裡的項目塞到 list 自己的 column,導致錯誤
data.frame(x = 1:3, y = list(1:2, 1:3, 1:4))
## Error in data.frame(1:2, 1:3, 1:4, check.names = FALSE, stringsAsFactors = TRUE): arguments imply differing number of rows: 2, 3, 4
遇到這種狀況,可以使用 I() ,可以讓 data.frame 把 list 當作一個 unit 來處理:
dfl <- data.frame(x = 1:3, y = I(list(1:2, 1:3, 1:4)))
str(dfl)
## 'data.frame':    3 obs. of  2 variables:
##  $ x: int  1 2 3
##  $ y:List of 3
##   ..$ : int  1 2
##   ..$ : int  1 2 3
##   ..$ : int  1 2 3 4
##   ..- attr(*, "class")= chr "AsIs"
dfl[2, "y"]
## [[1]]
## [1] 1 2 3

0 comments:

張貼留言