關卡 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的參數,代表要輸入的長度。這裡選n=3是因為通常BOM最多就是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。R會不定時的關閉不使用的connection,並且在console顯示警告訊息。所以之後同學會不定時的看到:closingunusedconnection

關卡 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

接著我們開始來講解read.table這個讀取CSV格式檔案最泛用的指令。

關卡 39

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

lvr_land.path

關卡 40

請同學特別注意read.table的參數fileEncoding的用法,這個參數雖然可以解碼,但只能用在file參數為字串的case。請同學試試看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 = ",")