關卡 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的左方,這個符號唸作:Graveaccent,是R在console中的跳脫字元。以特殊符號為名稱的變數,可以透過兩邊包覆Graveaccent來在console中存取。

關卡 15

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

關卡 16

包含最近火紅的ApacheSpark,以及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)、數值矩陣(numericmatrix)、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的語法之外,我們也可以使用類似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底下的第一到第二的值。請問同學,下面哪一種語法「不能」得到一樣的結果?

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->ReopenWithEncoding…->選取:UTF-8

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

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

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

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

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

#' CO2 共有很多很多列(answer3)
#' 請從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"