R Learning Notes: DataFrame, Loading Dataset

此篇僅做記錄用,本文所有文字內容,皆來自:
R語言翻轉教室

7: 01-RBasic-06-List-DataFrame
8: 01-RBasic-07-Loading-Dataset

— — — — — — — — — — — — — — — — — — — — —

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

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

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

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

這裡`iris =iris`的意思是說,第一個元素的名字是`”iris”`,而值會是變數`iris`的值。 而`cars = cars`表示第二個元素的名字是`”cars”`,值則是變數`cars`的值。

因為list是個向量,所以可以使用我們過去學過的`[]`。 如果我們運用如:`x[3]`的指令,從x中選出只有包含第三個元素的向量,得到的輸出會和`x`一樣是一個list向量。

簡言之,`mode(x[1])`、`mode(x[2])`和`mode(x[3])`的結果都會是list,和`mode(x[[3]])`是numeric是不同的。

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

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

list的元素除了用位置做選取之外(如:`x[[3]]`),也可以用名字做選取。

而和我們使用`x[[3]]`的方式類似,我們也可以使用`x[[“n”]]`來選擇第三個,也就是名字是`”n”`的元素。


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

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

由於傳統的matrix和array有同質性的限制(所有的元素都要同樣的型態),所以在資料分析上並不方便。

因為我們分析的資料,通常並不會全部都用相同的型態。在結構化的資料中,通常資料是以表格的形式,而各欄位會有自己的型態,例如是:數值型態、類別型態等等。

R 的data.frame就是要表現出二維表格特性的物件。

以下我們要介紹data.frame這個物件。

首先,data.frame是一種 list

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

第二,因為表格的資料是結構化的,所以data.frame存放的值會有格式上的限制。

具體來說,data.frame的各個元素必須是以下幾種類型之一:

數值(numeric)、字串(character)、布林(logical)、類別(factor)、數值矩陣(numeric matrix)、 list或data.frame。
最重要的是,因為data.frame代表的是二維表格,所以每一個值的長度須一致

(若為矩陣或data.frame,則是列(row)的數量需一致)。

上述的特性可以在整理資料時提供很大的幫助。


在R 之中,可以使用`data.frame`函數來建立一個data.frame。

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

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

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

colnames(a)
[1] “class” “id” “scores.1” “scores.2”
在data.frame上,通常會先對欄位做選擇,取得欄位中的向量以後,才能使用中括號。

中括號`[]`也可以一次選取多列多欄。舉例來說,`a[1:2,”id”]`可以一次選取`a$id`底下第1個到第2個的值。


在輸入`a[1:2,”id”]`的時候,R會輸出一個向量,但是輸入`a[1:2,1:2]`的時候R 反而會給一個data.frame呢?

其實這樣不一致的行為,在未來若要進行更複雜的資料整理時,會帶來困擾的。

`[]`中括號這個函數,有一個參數叫做:`drop`,而且預設為`TRUE`。

當我們使用`[]`取得結果的時候,如果有一個方向可以縮減維度(例如只有一個欄位的表格),R就會自動把表格的結構破壞,回傳一個向量。

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

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


mean(CO2[CO2$Type==”Quebec”,”uptake”])

選出”uptake”內,Type==”Quebec”的Data,並求出平均。


我們可以利用`model.matrix`來建立一個矩陣。
model.matrix(~ Type + Treatment + conc, CO2)

可以建立一個基於Type、Treatment和conc的矩陣。


Loading Database

R 內建有許多知名的資料集。 這些內建的資料集,目前都在`datasets`這個套件之中。

library(help=datasets)`檢視所有的資料集

我們可以運用`data`這個指令來載入列出的資料。

data(iris,package = “datasets”)
head(iris)

這就是`data`函數的功能。 它可以讓同學從套件載入特定資料集(Data Sets)。

舉例來說,我們剛才已由library(help=datasets)觀察到datasets套件中有包含許多資料集,例如:AirPassengers、BJsales、…、iris…等。

我們可以用`data(iris, package = “datasets”)`指令,並透過第一個參數指定特定資料集,R則會把iris的資料集輸入到iris這個變數之中。


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

如果是使用R 提供的資料集,通常都會有相關的說明。甚至會包含該資料集出處的論文。

`help(iris)`。

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

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

csv 檔的格式是以行(Line)為單位。每一行(Line)就是一筆紀錄(Record)。

而每一筆紀錄中,以一個分隔符號來讓使用者或程式分辨欄位。常見的分隔符號有: tab(\t)或逗號(,) 。

以逗號做為分隔符號,則學生姓名加上4次小考分數的一筆紀錄,可用CSV表達如下:`柯小哲,77,82,87,92`


簡單起見,我們將帶著同學分步驟測試這個檔案的編碼。

第一步先檢查檔案中是否帶有BOM 。

BOM 是一種利用檔案最前面的2 個或3 個位元組來標示檔案編碼的方式。

在windows系統中建立的中文檔案經常會帶有BOM 。

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

R 中的`readBin` 是一個可以直接讀取檔案中的位元組的指令,可以正確的處理BOM 。

第一個參數為檔案的路徑。
第二個為稱作what的參數。

目的是告訴R要如何處理讀入的資料。若`what = “raw”`代表讀入的資料用位元處理,而不是文字。

第三個為稱作n的參數。

代表要輸入的長度。因為BOM 通常最多是3 個位元組,所以這裡選擇`n = 3`。


如果沒有BOM ,一種方法是使用`file`搭配`readLines` 兩個函數來測試編碼。

如果讀出來的中文可以正常顯示,就是猜中編碼了。

如果讀出亂碼,或是讀取發生錯誤,那可能就猜錯了,需要換個編碼試試看。


試試看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`函數會建立一個物件代表檔案,在R 之中叫做`connection`。

之後同學會不定時的看到:`closing unused connection`,表示R會不定時關閉不使用的connection,並在console 顯示警告訊息。


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

在程式的世界中,資料在最終都是以0 和1 位元的方式儲存。


處理資料時,程式需要先對資料有初步的認識才能進行處理,例如把資料當成字串、整數或浮點數。

只有文字資料才會被編碼影響,所以如果我們只是用位元來處理檔案的內容,就可以避開編碼的問題。


R 利用型態為raw 的向量來處理位元的物件。

只要能知道檔案的大小,readBin就可以把所有的檔案資料以raw 的形式輸入到R 中。
我們可以使用`file.info`來查詢檔案的大小。
lvr_land.info <- file.info(lvr_land.path)
class(lvr_land.info)
[1] “data.frame”

請輸入一個指令,讓R輸出一個數值,代表這個檔案的大小:

lvr_land.info$size
接著請同學利用`readBin`來把`lvr_land.path`的檔案以位元的方式讀到R中,並且存到名稱為`lvr_land.bin`的變數。
lvr_land.bin <- readBin(lvr_land.path, what = “raw”, n = lvr_land.info$size)

接著,我們需要使用stringi套件。

由於swirl本身已經相依於stringi,所以請同學直接載入stringi。

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

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

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


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

R 中,`read.table`的第一個參數和readLines 的第一個參數一樣,代表要開啟的檔案。

如果我們不在乎檔案的編碼,可以直接輸入檔案的路徑。

lvr_land.path

請同學特別注意`read.table`的參數fileEncoding的用法,這個參數雖然可以解碼,但只能用在file參數為字串的案例中。

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

第二個重要的參數是`header`。 `header = TRUE`代表檔案的第一行是欄位名稱,而不是資料。

`header=FALSE`代表檔案的第一行就是資料。

第三個重要的參數是`sep` ,這代表各行欄位間的分隔符號。

依照以上利用`read.table`,用BIG5編碼進行讀取。 `lvr_land.path`,並且把輸出存到變數`lvr_land`

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

如果要直接從`lvr_land.txt`的內容取出表格,一種方法是直接把`lvr_land.txt`寫成一個檔案,再用`read.table`讀取。

另外一種方法則是把`lvr_land.txt`視為一個類似檔案的物件,也就是connection。

事實上,如果同學自行下載原本的檔案來處理,會發現上述的流程還是會出錯。

因為在資料中第21行,資料的最後一欄的內容包含:`,`這個符號,導致欄位判斷出錯。

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


另外一種方式,是透過`readLines` 來讀取資料,再用R的其他函數自行把讀入的字串轉換為data.frame。

這部分很難歸納,會隨著資料的乾淨程度而有所不同。 同學可以閱讀本課程中一些實際分析Open Data 的範例,就可以學到一些處理技巧。 或是繼續學習R 的字串處理函數與套件。

如果要使用R 讀取XML 的資料,可以使用套件XML 。 如果要讀取JSON,則可以使用rjsonlite 。

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

Like what you read? Give Kevin Hsu a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.