關卡 1

這堂課程要跟各位同學介紹list和data.frame這兩個很重要的資料結構。

關卡 2

R 的list是非常泛用的物件,同學在前面的課程有簡單操作過R 裡面的迴歸模型,而這些迴歸模型的底層架構,就是用list所建立的。

關卡 3

list的本質,其實就是一個「R 物件的向量」,這裡要特別強調「向量」的概念,因為list是有順序性的。

關卡 4

請同學建立一個list,x <- list(iris = iris, cars = cars, n = 2) 這裡iris = iris的意思是說,第一個元素的名字是"iris",而值會是變數iris的值。 而cars = cars表示第二個元素的名字是"cars",值則是變數cars的值。以此類推。

x <- list(iris = iris, cars = cars, n = 2)

關卡 5

在建立的過程中,因為iris在前面,所以是第一個元素(component),而cars跟在iris後面,所以 是第二個component。我們可以利用兩個中括號:[[]]來取出component。請同學試試看:x[[3]]

x[[3]]

關卡 6

請檢查一下x[[3]]的型態。

mode(x[[3]])

關卡 7

可以看到x[[3]]的型態是numeric,這是因為x <- list(iris = iris, cars = cars, n = 2), 所以mode(x[[3]])的結果等同於mode(2),也就是numeric。 同理,mode(x[[1]])mode(x[[2]])的結果則是x 的第一個值和第二個值的型態。

關卡 8

因為list是個向量,所以可以使用我們過去學過的[]。 如果我們運用如:x[3]的指令,從x 中選出只有包含第三個元素的向量,得到的輸出會和x一樣是一個list向量。 簡言之,mode(x[1])mode(x[2])mode(x[3])的結果都會是list,和mode(x[[3]])是numeric是不同的。請同學試試看mode(x[3])

mode(x[3])

關卡 9

輸出的型態是否仍為list,就是[[]][]主要的不同。

關卡 10

也由於list是一種向量,所以先前學過的函數,如length之類,都是可以在list上使用的。

關卡 11

請各位同學猜猜看,length(x)的結果會是多少呢?

3

關卡 12

list的元素除了用位置做選取之外(如:x[[3]]),也可以用名字做選取。 在我們建立list的時候,使用指令如下:x <- list(iris = iris, cars = cars, n = 2), 這代表第一個元素的名字是"iris",第二個是"cars",第三個是"n"。 而和我們使用x[[3]]的方式類似,我們也可以使用x[["n"]]來選擇第三個,也就是名字是"n"的元素。請同學試試看。

x[["n"]]

關卡 13

除了[[]]之外,也可以使用$來透過名字取出list的值。 舉例來說,x$n就會取出x中名字為"n"的值。 請同學試試看。

x$n

關卡 14

在windows系統中,如果名字是中文時,使用$的語法可能會出錯。一個比較保險的寫法是: x$中文名稱 。 這裡的 `符號的按鍵,在美式鍵盤中位於1的左方,這個符號唸作:Grave accent,是R 在console中的跳脫字元。 以特殊符號為名稱的變數可以透過兩邊包覆Grave accent來在console中進行存取。

關卡 15

接著,我們要介紹一個在R 中很有名的物件。這個物件的設計,完全滿足了近代對結構化資料分析的需求。

關卡 16

包含最近火紅的Apache Spark,以及Python的pandas都有受到這個物件的影響。這個物件就是R 的data.frame。

關卡 17

由於傳統的matrix和array 有同質性的限制(所有的元素都要同樣的型態),所以在資料分析上並不方便。 因為我們分析的資料,通常並不會全部都用相同的型態。 在結構化的資料中,通常資料是以表格的形式,而各欄位會有自己的型態,例如是:數值型態、類別型態等等。

關卡 18

R 的data.frame就是要表現出二維表格特性的物件。以下我們要介紹data.frame這個物件。

關卡 19

首先,data.frame是一種list。因為表格的各欄是型態不一的向量,所以我們需要用list來裝不同型態的向量。

關卡 20

第二,因為表格的資料是結構化的,所以data.frame存放的值會有格式上的限制。 具體來說,data.frame的各個元素必須是以下幾種類型之一:數值(numeric)、字串(character)、布林(logical)、類別(factor)、數值矩陣(numeric matrix)、 list或data.frame。

關卡 21

最重要的是,因為data.frame代表的是二維表格,所以每一個值的長度須一致(若為矩陣或data.frame,則是列(row)的數量需一致)。 上述的特性可以在整理資料時提供很大的幫助。

關卡 22

請問下列哪一個list物件,符合data.frame的要素?如果不確定的話,可以看Hint了解原因。

list(a = 1:3, b = matrix(1:6, nrow = 3, ncol = 2))

關卡 23

data.frame的操作也是非常重要的。

關卡 24

在R 之中,可以使用data.frame函數來建立一個data.frame。 請同學現在試試看利用data.frame(class = "NTU", id = 1:10,scores = matrix(c(80:99),nrow=10,ncol=2 ) ) 建立一個data.frame,並且把這個物件存到變數a

a <- data.frame(class = "NTU", id = 1:10,scores = matrix(c(80:99),nrow=10,ncol=2 ) )

關卡 25

所有list的操作,都可用在data.frame上。 請問同學,下面哪一個語法「不能」選取a 的第一欄?

a[[“id”]]

關卡 26

除了list的語法之外,在data.frame上也可以使用和matrix一樣的語法。

關卡 27

舉例來說,nrow(a)可以列出a 所包含的列數、ncol(a)可以列出a 所包含的欄數、dim(a)則可以一次列出兩者。 上述特性都和matrix一樣。

關卡 28

資料的選取的方式也是一樣的。我們可以用a[,1]選取第一欄。請同學試試看。

a[,1]

關卡 29

根據螢幕上的輸出,請問同學a[,1]的類別是什麼呢?

factor

關卡 30

同理,我們也可以用a[1,2]來取出第1 列、第2 欄的資料。 請同學試試看。

a[1,2]

關卡 31

colnames(a)則可以取出a 的欄位名稱。請同學試試看。名稱也可以用於接下來的資料選取中。

colnames(a)

關卡 32

資料選取時,欄位的方向也可以用名字代替。 舉例來說,a[2,"id"]可以選到第二列第二欄,因為a[2,]代表第二列,而a[,"id"] 會選到名稱為"id"的第二欄。 兩者的交集,就是第2 列第2 欄。請同學試試看。

a[2,"id"]

關卡 33

中括號[]也可以一次選取多列多欄。 舉例來說,a[1:2,"id"]可以一次選取a$id底下第1個到第2個的值。 請問同學,下面哪一種語法「不能」得到一樣的結果?

a[1:2][“id”]

關卡 34

另一個用法是:a[1:2,1:2],可以選許出前兩列的前兩欄。請同學試試看。

a[1:2,1:2]

關卡 35

請問同學,能不能用class查看a[1:2,1:2]的型態呢?

class(a[1:2,1:2])

關卡 36

同學是否注意到,在輸入a[1:2,"id"]的時候,R 會輸出一個向量,但是輸入a[1:2,1:2]的時候R 反而會給一個data.frame呢? 其實這樣不一致的行為,在未來若要進行更複雜的資料整理時,會帶來困擾的。

關卡 37

其實[]中括號這個函數,有一個參數叫做:drop,而且預設為TRUE。 當我們使用[]取得結果的時候,如果有一個方向可以縮減維度(例如只有一個欄位的表格),R 就會自動把表格的結構破壞,回傳一個向量。 請同學試試看:a[1:2, "id", drop = FALSE]

a[1:2, "id", drop = FALSE]

關卡 38

從R 顯示在console的輸出結果,同學會發現這是一個data.frame。 在R 中,console會根據不同的class而用不同的方式呈現輸出結果。

關卡 39

上述的內容就是我們對於list和data.frame的介紹。 data.frame是處理資料和進行進階演算法時非常實用結構。 所以請同學務必要熟悉不同data.frame的操作,才能有系統地整理資料,並且在資料上運行其他的演算法。

關卡 40

接著,請同學依照這個課程所學到的技巧,做一個簡單的練習。 請同學在完成之後存檔,並輸入submit()來檢查結果是否符合預期。 如果同學在檔案中看到亂碼,請使用Rstudio 左上角的File -> Reopen With Encoding… -> 選取:UTF-8

#' 這裡我們使用CO2這個資料集請同學做練習
data(CO2)

#' 請問CO2 有多少列?
answer1 <- nrow(CO2)

#' 請問CO2 有多少行(column)?
answer2 <- ncol(CO2)

#' 請問CO2 的各行的名稱為何?
answer3 <- colnames(CO2)

#' 請問uptake這欄的平均值為多少?
answer4 <- mean(CO2$uptake)

#' CO2 共有很多很多列(answer1)
#' 請從CO2 中挑出一些列,滿足以下的條件:
#' 這些列的uptake直,超過全部CO2的uptake值
#' (`mean(CO2$uptake)`)
#' 
#'   你可以先取出uptake的向量、接著拿該向量和平均值做比較、把結果的logical vector丟到`[]`的第一個參數
answer5 <- CO2[CO2$uptake > answer4,]

#' 請問Type有多少種類別?
answer6 <- length(levels(CO2$Type))

#' 請問當Type的類別為Quebec時,uptake的平均值為多少?
answer7 <- mean(CO2[CO2$Type == "Quebec","uptake"])

#' 請問當Type的類別為Mississippi時,uptake的平均值為多少?
answer8 <- mean(CO2[CO2$Type == "Mississippi","uptake"])

#' 我們可以利用`model.matrix`來建立一個矩陣。舉例來說:
#' `model.matrix(~ Type + Treatment + conc, CO2)`可以
#' 建立一個基於Type、Treatment和conc的矩陣。

X <- model.matrix(~ Type + Treatment + conc, CO2)

#' 請取出uptake的值放入y 之中
y <- CO2$uptake

#' 請利用<https://en.wikipedia.org/wiki/Ordinary_least_squares#Estimation>的公式,利用迴歸的演算法,
#' 找出beta.hat讓 X %*% beta.hat 很靠近 y
#' ps. class(beta.hat) 應該為matrix
#'     dim(beta.hat) 應該為 c(4, 1)
#'     rownames(beta.hat) 應該為 c("(Intercept)","TypeMississippi","Treatmentchilled","conc")
beta.hat <- solve(t(X) %*% X, t(X) %*% y)

#' 請計算X %*% beta.hat 和 y 的correlation(提示:用函數`cor`)
answer11 <- cor(X %*% beta.hat, y)

#' answer11 的平方,就是迴歸分析時常提到的:R-squared。
#' 很多分析師會用這個數據來判斷這個模型好不好。
#' 在R 裡面,跑迴歸分析,可以簡單用`lm`這個函數:
g <- lm(uptake ~ Type + Treatment + conc, CO2)

#' g 這個物件就會包含我們剛剛算過得答案
#' g$coef就會是beta.hat
#' g$fitted.value就會是X %*% beta.hat
#' summary(g)則會顯示各個參數的t 檢定,以及整個模型的R-squared
g.s <- summary(g)
#' mode(g.s)顯示它是一個list。
#' 請找出一個名字,answer12,讓g[[answer12]]就是R-squared
#' 你可以參考help(summary.lm)裡面的說明。
answer12 <- "r.squared"