關卡 1

在做資料分析之前,我們要能載入資料。

關卡 2

這個課程的目的是介紹R最常見的幾種資料載入方式

關卡 3

R內建有許多知名的資料集。這些內建的資料集,目前都在datasets這個套件之中。同學輸入:library(help=datasets)來檢視所有的資料集。請同學試試看。

library(help=datasets)

關卡 4

在載入資料之前,為了更清楚地說明R的函數,請同學先輸入:iris<-1

iris <- 1

關卡 5

我們可以運用data這個指令來載入列出的資料。請同學試試看:data(iris,package="datasets"),這個指令的意思為從套件datasets中載入資料集iris。

data(iris, package = "datasets")

關卡 6

接著,我們請同學輸入:head(iris)

head(iris)

關卡 7

同學會在螢幕上看到R輸出一個data.frame,而不是當初輸入的1。

關卡 8

這就是data函數的功能。它可以讓同學從套件載入特定資料集(DataSets)。舉例來說,我們剛才已由library(help=datasets)觀察到datasets套件中有包含許多資料集,例如:AirPassengers、BJsales、…、iris…等。我們可以用data(iris,package="datasets")指令,並透過第一個參數指定特定資料集,R則會把iris的資料集輸入到iris這個變數之中。

關卡 9

接著,請同學查詢iris的欄位名稱。

colnames(iris)

關卡 10

雖然我們看到了欄位名稱,可是並不清楚這些欄位或是每列資料所代表的意義。這些事情,必須在實際進行資料分析之前就要預先理解的。

關卡 11

如果是使用R提供的資料集,通常都會有相關的說明。甚至會包含該資料集出處的論文。請同學試試看輸入:help(iris)

help(iris)

關卡 12

以上練習,已經讓同學了解如何使用R內建的資料集。

關卡 13

接著,我們來展示如何讓同學讀取外部的資料集。

關卡 14

外部資料集的格式有千百種,今天只跟大家介紹最常見的csv檔。

關卡 15

csv檔的格式是以行(Line)為單位。每一行(Line)就是一筆紀錄(Record)。而每一筆紀錄中,以一個分隔符號來讓使用者或程式分辨欄位。常見的分隔符號有:tab()或逗號(,)。若以逗號做為分隔符號,則學生姓名加上4次小考分數的一筆紀錄,可用CSV表達如下:柯小哲,77,82,87,92

關卡 16

我們拿一個自http://data.gov.tw/node/6213下載的csv檔做範例。這個檔案的目錄已經存入了lvr_land.path變數之中。我們將利用這個檔案來示範如何將中文資料載入到R之中。

關卡 17

載入中文資料時,最重要的就是要偵測檔案所採用的中文編碼。可惜目前我還不清楚如何直接在R中偵測編碼。另外,如果有同學不清楚編碼是什麼,可以參考這個youtube影片:https://www.youtube.com/watch?v=GG2UFrmdtH4

關卡 18

簡單起見,我們將帶著同學分步驟測試這個檔案的編碼。第一步先檢查檔案中是否帶有BOM。BOM是一種利用檔案最前面的2個或3個位元組來標示檔案編碼的方式。在windows系統中建立的中文檔案經常會帶有BOM。請同學執行:readBin(lvr_land.path,what="raw",n=3)

readBin(lvr_land.path, what = "raw", n = 3)

關卡 19

R中的readBin是一個可以直接讀取檔案中的位元組的指令,可以正確的處理BOM。第一個參數為檔案的路徑。第二個為稱作what的參數,目的是告訴R要如何處理讀入的資料。若what="raw"代表讀入的資料用位元處理,而不是文字。第三個為稱作n的參數,代表要輸入的長度。因為BOM通常最多是3個位元組,所以這裡選擇n=3

關卡 20

接著,我們可以到https://zh.wikipedia.org/wiki/位元組順序記號查詢常用的BOM。這裡直接告訴同學,此份檔案中的三個位元不屬於任何已知編碼所使用的BOM,所以我們猜測這個檔案不帶BOM。

關卡 21

如果沒有BOM,一種方法是使用file搭配readLines兩個函數來測試編碼。如果讀出來的中文可以正常顯示,就是猜中編碼了。如果讀出亂碼,或是讀取發生錯誤,那可能就猜錯了,需要換個編碼試試看。

關卡 22

請同學試試看BIG5編碼:readLines(file(lvr_land.path,encoding="BIG5"),n=1)這裡的file指令會打開一個檔案,第一個參數是檔案的路徑,第二個名稱為encoding的參數代表檔案的編碼。如果是BIG5編碼,就輸入名稱:"BIG5",UTF-8的話,就輸入"UTF-8"readLines函數會一行一行的讀取檔案的內容。第一個參數就是用file函數開啟的檔案。第二個為稱作n的參數,則代表讀取的行數。這裡n=1代表讀取一行,所以只會回傳一個元素的character向量。

readLines(file(lvr_land.path, encoding = "BIG5"), n = 1)

關卡 23

一試就中,所以同學的Rconsole上應該能夠顯示出正確的中文字串。

關卡 24

這裡打個岔。file函數會建立一個物件代表檔案,在R之中叫做connection。之後同學會不定時的看到:closingunusedconnection,表示R會不定時關閉不使用的connection,並在console顯示警告訊息。

關卡 25

一開始先用readLines看一下資料是個好習慣。除了第一行外,我們應該多看幾行,以方便預測檔案的格式。請同學再輸入一次filereadLines的組合指令,並且讀取5行。

readLines(file(lvr_land.path, encoding = "BIG5"), n = 5)

關卡 26

有時候,由於不明原因,R會發生無法讀取特定編碼的狀況。這時候,我們可以先把資料以位元的方式讀取到R中,再進行轉碼。

關卡 27

在程式的世界中,資料在最終都是以0和1位元的方式儲存。處理資料時,程式需要先對資料有初步的認識才能進行處理,例如把資料當成字串、整數或浮點數。只有文字資料才會被編碼影響,所以如果我們只是用位元來處理檔案的內容,就可以避開編碼的問題。

關卡 28

R利用型態為raw的向量來處理位元的物件。只要能知道檔案的大小,readBin就可以把所有的檔案資料以raw的形式輸入到R中。我們可以使用file.info來查詢檔案的大小。請同學利用file.info(lvr_land.path)來查詢檔案的資訊,並且把結果存到變數lvr_land.info

lvr_land.info <- file.info(lvr_land.path)

關卡 29

請同學先輸入class(lvr_land.info)

class(lvr_land.info)

關卡 30

請問同學,file.info的回傳值是什麼型態?

data.frame

關卡 31

接下來請同學列出lvr_land.info的欄位名稱。

colnames(lvr_land.info)

關卡 32

請問同學,lvr_land.path背後的檔案大小,應該是多少呢?請輸入一個指令,讓R輸出一個數值,代表這個檔案的大小。

lvr_land.info$size

關卡 33

接著請同學利用readBin來把lvr_land.path的檔案以位元的方式讀到R中,並且存到名稱為lvr_land.bin的變數。

lvr_land.bin <- readBin(lvr_land.path, what = "raw", n = lvr_land.info$size)

關卡 34

接著,我們需要使用stringi套件。由於swirl本身已經相依於stringi,所以請同學直接載入stringi。

library(stringi)

關卡 35

透過stringi套件的stri_encode,我們可以把一個位元組的向量(在R中,這類向量的型態是raw)從一個編碼轉換為另一個編碼。

關卡 36

請同學把stri_encode(lvr_land.bin,"BIG-5","UTF-8")的結果存到lvr_land.txt之中。

lvr_land.txt <- stri_encode(lvr_land.bin, "BIG-5", "UTF-8")

關卡 37

透過以上的方法,我們可以取得轉換編碼後的character向量lvr_land.txt

關卡 38

接著,我們來講解讀取CSV格式檔案時最泛用的指令:read.table

關卡 39

在R中,read.table的第一個參數和readLines的第一個參數一樣,代表要開啟的檔案。如果我們不在乎檔案的編碼,可以直接輸入檔案的路徑。以下哪一個是正確的路徑呢?

lvr_land.path

關卡 40

請同學特別注意read.table的參數fileEncoding的用法,這個參數雖然可以解碼,但只能用在file參數為字串的案例中。請同學試試看read.table(lvr_land.path,fileEncoding="BIG-5")

read.table(lvr_land.path, fileEncoding = "BIG-5")

關卡 41

可惜我們需要設定編碼。(註:在中文版windows上會預設使用BIG5編碼,所以可以直接和上面例題一樣輸入lvr_land.path。)第二個重要的參數是headerheader=TRUE代表檔案的第一行是欄位名稱,而不是資料。header=FALSE代表檔案的第一行就是資料。剛剛我們看到檔案的前五行的內容是:[1]"鄉鎮市區,交易標的,..."[2]"文山區,房地(土地+建物)..."。請問同學,header的參數應該要給哪一個呢?

TRUE

關卡 42

第三個重要的參數是sep,這代表各行欄位間的分隔符號。根據之前我們看到的前五行的資料:[1]"鄉鎮市區,交易標的,土地區段位置...,土地移轉總面積..."[2]"文山區,房地(土地+建物),臺北市文山區...,43.44..."[3]"中正區,房地(土地+建物),臺北市中正區...,40.19..."。請問同學,這份檔案個欄位的分隔符號是什麼呢?

,

關卡 43

依照以上問答題的答案,請同學利用read.table,用BIG5編碼進行讀取。lvr_land.path,並且把輸出存到變數lvr_land

lvr_land <- read.table(file(lvr_land.path, encoding = "BIG5"), header = TRUE, sep = ",")

關卡 44

如果要直接從lvr_land.txt的內容取出表格,一種方法是直接把lvr_land.txt寫成一個檔案,再用read.table讀取。另外一種方法則是把lvr_land.txt視為一個類似檔案的物件,也就是connection。

關卡 45

在windows系統上,有一個很不方便的地方,接下來的作法和Windows的語系有關。如果同學的Windows的語系有支援多位元組字串(例如中文、日文等語系),則語法需要進行調整。

關卡 46

我們可以用l10n_info()來查詢作業系統對於不同Encoding的支援狀況。l10n_info()會回報系統是否支援UTF-8(UTF-8欄位),或是多位元組字串(MBCS欄位)。請同學試試看輸入:l10n_info()

l10n_info()

關卡 47

根據經驗,如果l10n_info()的輸出中,MBCS為TRUE且UTF-8為FALSE,這時須使用textConnection(lvr_land.txt)來從lvr_land.txt建立一個connection。除此之外,則使用textConnection(lvr_land.txt,encoding="UTF-8")即可。請同學依據上一題的結果,在MBCS為TRUE且UTF-8為FALSE時執行:read.table(textConnection(lvr_land.txt),header=TRUE,sep=",")否則,請執行:read.table(textConnection(lvr_land.txt,encoding="UTF-8"),header=TRUE,sep=",")

read.table(get_text_connection_by_l10n_info(lvr_land.txt), header = TRUE, sep = ",")

關卡 48

以上提供數種在R中處理中文資料的方法給同學參考。

關卡 49

事實上,如果同學自行下載原本的檔案來處理,會發現上述的流程還是會出錯。因為在資料中第21行,資料的最後一欄的內容包含:,這個符號,導致欄位判斷出錯。

關卡 50

遇到這種狀況,最好的方式是自己用其他的編輯器(如notepad++),自行編輯原始的檔案資料(依照錯誤訊息,移除多餘的符號),再用R讀取。由於這樣的動作超過這個課程系統的內容,所以請同學自行練習操作了。

關卡 51

另外一種方式,是透過readLines來讀取資料,再用R的其他函數自行把讀入的字串轉換為data.frame。這部分很難歸納,會隨著資料的乾淨程度而有所不同。同學可以閱讀本課程中一些實際分析OpenData的範例,就可以學到一些處理技巧。或是繼續學習R的字串處理函數與套件。

關卡 52

在這門課程中我們展示了如何從套件中載入資料,以及如何從檔案中讀取CSV的資料。

關卡 53

如果要使用R讀取XML的資料,可以使用套件XML。如果要讀取JSON,則可以使用rjsonlite。只要資料格式是公開格式,我們可以容易找到R的套件來讀取該資料格式。這就是R為OpenSource的威力!

關卡 54

受限於課程內容,所以我們這裡只講讀取CSV的方式,這是最泛用的內容了。未來有時間,會再找社群的其他大大合作開寫其他和讀資料相關的課程。同學如果很期待看到處理某些形態資料的課程,歡迎到https://github.com/wush978/DataScienceAndR/issues許願。

關卡 55

最後,還是要請同學利用這次所學的內容,做一個小練習。請同學在完成之後存檔,並輸入submit()來檢查結果是否符合預期。如果同學在檔案中看到亂碼,請使用Rstudio左上角的File->ReopenWithEncoding…->選取:UTF-8

#' 請同學用這章節所學的技巧,讀取`orglist.path`的檔案
#' 資料來源:<http://data.gov.tw/node/7307>

# <你可以在這裡做各種嘗試>
answer.raw <- readBin(orglist.path, what = "raw", n = file.info(orglist.path)$size)
answer.txt <- stringi::stri_encode(answer.raw, from = "UTF-16", to = "UTF-8")
get_text_connection_by_l10n_info <- function(x) {
  info <- l10n_info()
  if (info$MBCS & !info$`UTF-8`) {
    textConnection(x)
  } else {
    textConnection(x, encoding = "UTF-8")
  }
}
answer <- read.table(get_text_connection_by_l10n_info(answer.txt), header = TRUE, sep = ",")