關卡 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函數的功能。 它可以讓同學從套件載入特定資料集(Data Sets)。 舉例來說,我們剛才已由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 = &quot;raw&quot;, n = 3)

readBin(lvr_land.path, what = &quot;raw&quot;, n = 3)

關卡 19

R 中的readBin 是一個可以直接讀取檔案中的位元組的指令,可以正確的處理BOM 。 第一個參數為檔案的路徑。第二個為稱作what的參數,目的是告訴R 要如何處理讀入的資料。 若what = &quot;raw&quot;代表讀入的資料用位元處理,而不是文字。 第三個為稱作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 = &quot;BIG5&quot;), n = 1) 這裡的file指令會打開一個檔案,第一個參數是檔案的路徑,第二個名稱為encoding的參數代表檔案的編碼。 如果是BIG5編碼,就輸入名稱:&quot;BIG5&quot;,UTF-8 的話,就輸入&quot;UTF-8&quot;readLines 函數會一行一行的讀取檔案的內容。 第一個參數就是用file函數開啟的檔案。 第二個為稱作n 的參數,則代表讀取的行數。這裡n=1 代表讀取一行,所以只會回傳一個元素的character 向量。

readLines(file(lvr_land.path, encoding = &quot;BIG5&quot;), n = 1)

關卡 23

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

關卡 24

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

關卡 25

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

readLines(file(lvr_land.path, encoding = &quot;BIG5&quot;), 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 &lt;- 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 &lt;- readBin(lvr_land.path, what = &quot;raw&quot;, n = lvr_land.info$size)

關卡 34

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

library(stringi)

關卡 35

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

關卡 36

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

lvr_land.txt &lt;- stri_encode(lvr_land.bin, &quot;BIG-5&quot;, &quot;UTF-8&quot;)

關卡 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 = &quot;BIG-5&quot;)

read.table(lvr_land.path, fileEncoding = &quot;BIG-5&quot;)

關卡 41

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

TRUE

關卡 42

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

,

關卡 43

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

lvr_land &lt;- read.table(file(lvr_land.path, encoding = &quot;BIG5&quot;), header = TRUE, sep = &quot;,&quot;)

關卡 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 = &quot;UTF-8&quot;)即可。 請同學依據上一題的結果,在MBCS為TRUE且UTF-8 為FALSE時執行: read.table(textConnection(lvr_land.txt), header = TRUE, sep = &quot;,&quot;) 否則,請執行:read.table(textConnection(lvr_land.txt, encoding = &quot;UTF-8&quot;), header = TRUE, sep = &quot;,&quot;)

read.table(get_text_connection_by_l10n_info(lvr_land.txt), header = TRUE, sep = &quot;,&quot;)

關卡 48

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

關卡 49

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

關卡 50

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

關卡 51

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

關卡 52

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

關卡 53

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

關卡 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 else是因為需要讓正確答案跨平台
  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 = ",")