# R 基礎技能:資料讀取、匯出、清洗與整理
{%hackmd @themes/orangeheart %}
###### tags: `R Language`
資料的來源有很多種,有可能是來自網路上的表格,也有可能是進行問卷調查後產生的`.csv`、`.xls`或`.xlsx`檔。當然,這些不同的檔案都有不同的讀取、處理方式,因此我們以下要針對這些不同類型的檔案之讀取方式進行教學,接著,有關資料處理的議題我們將會以概論的方式呈現。[^1]
## 資料讀取與寫入
在`R`語言中,讀取資料最土砲也最簡單的方式是透過以下的方式進行,首先在RStudio 右側的面板上,點選`Import Dataset`,接著選取要讀取的檔案類型,找到檔案點選開啟之後就可以成功讀取了。
![](https://i.imgur.com/AXMwAGl.png)
而接下來在進行不同檔案類型讀取方式的教學以前,我們要來知道一下讀取檔案的一項重要議題,就是路徑。首先,我們要知道,電腦在讀取檔案的時候,一定要知道檔案的存儲位置在哪裡,想像你是一位圖書館管理員,當有民眾來向你詢問書籍擺放位置時,跟他說放在某書區,或是放在第幾走道的第幾個櫃子,後者顯然是比較明確的敘述方式。同樣地,我們也應該跟電腦說明我們檔案的確切位置,所以我們有以下幾種作法:
1. 使用絕對路徑:比如說檔案放在 D 槽,我們在輸入檔案名稱時,可以用`D:/某資料夾/檔案名稱.副檔名`這樣的方式告知電腦。
2. 先設定目前工作路徑,再輸入檔案名稱即可。設定目前工作路徑的方式如下:首先我們可以使用`getwd()`獲取目前工作路徑,如果是檔案存儲位置就不用改變;若工作路徑非我們檔案存儲位置,則我們使用`setwd("檔案存儲路徑")`將工作路徑設置到我們檔案的存儲位置。
3. 直接在 RStudio 的功能列設定。
![](https://i.imgur.com/J6mHdcx.png)
### `.csv`檔
相信看過之前的文章,應該知道如何讀取`.csv`檔吧!如果忘記了沒關係,現在再複習一次!
```r
data <- read.csv("檔案名稱.csv")
```
而寫入的方式就是使用`wrtie.csv()`將我們處理好的資料製作成新的`.csv`檔。
```r
write.csv(data, file="檔案名稱.csv",row.names=F)
```
### `.xls`、`.xlsx`檔
如果想要讀取`Excel`相關的檔案,包含`.xls`、`.xlsx`檔等等,我們要安裝`readxl`這個套件,接著使用`read_excel()`讀取資料就可以了。
```r
install.packages("readxl")
library(readxl)
read_excel("檔案名稱.xls")
read_excel("檔案名稱.xlsx")
```
### `.dta`檔
假設我們分析的資料是從`Stata`整理過後的`.dta`檔,我們則需要安裝另一個套件,`haven`。
```r
install.packages("haven")
library(haven)
read_dta("檔案名稱.dta")
```
了解完不同檔案類型的讀取方式後,還有一個小技巧能夠幫助我們在進行資料處理前快速了解資料的狀況,即利用`head()`瀏覽資料的前六筆。
## 資料處理與資料清洗
當我們拿到一筆資料之後,這筆資料有可能存在一些對於我們分析是無用的元素,例如遺漏值,又或者是資料格式不統一,也有可能是資料型別無法直接分析,必須透過轉換型別才得以分析,我們會稱目前資料的狀態是「髒資料」。為了避免上述這些「雜質」影響我們的分析,我們必須透過資料清洗來解決上述的問題。
### 辨別資料型別與轉換型別
我們可以用`is.`或`class`函數辨別我們目前的資料屬於何種型別。比如在[R 基礎技能:運算、流程控制與迴圈](https://hackmd.io/@yueswater/R_Basic)中提到薪資的例子:
```r
wage <- data.frame(Name=c("大明","小華","阿天","阿國"),
Wage=c(23135,41465,35762,90424),
Gender=c(1,0,1,1))
wage
## wage
## Name Wage Gender
## 1 大明 23135 1
## 2 小華 41465 0
## 3 阿天 35762 1
## 4 阿國 90424 1
```
我們可以用上面兩個函數辨別這幾個欄位是否為數值資料:
```r
is.numeric(wage$Name)
## [1] FALSE
is.numeric(wage$Wage)
## [1] TRUE
is.numeric(wage$Gender)
## [1] TRUE
```
或是可以直接回傳這些欄位的型別:
```r
class(wage$Name)
## [1] "character"
class(wage$Wage)
## [1] "numeric"
class(wage$Gender)
## [1] "numeric"
```
而轉換型別的方式就是利用`as.`函數進行轉換。假設我們想要將`wage$Wage`的資料轉換成字串,我們只需輸入以下的指令就可以進行轉換:
```r
wage$Wage <- as.character(wage$Wage)
class(wage$Wage)
## [1] "character"
```
注意到如果沒有指派的話,原始資料是不會有任何變動的。如果我們將一個型別無法互相轉換的資料進行轉換,則會產生下方的訊息:
```r
as.numeric("你好啊!")
## [1] NA
## Warning message:
## NAs introduced by coercion
```
### 字串處理
為什麼字串處理很重要呢?假設我們今天有一筆客戶訂單的資料,裡頭包含客戶的姓名、電話、地址、電子郵件等資訊,但是這份資料是由不同的專員輸入進去的,而有些專員在訪問或輸入時十分不專心,導致姓名本來應該是首字母大寫,卻全部都是小寫。遇到這類的問題,我們就需要對字串進行處理。`R`語言中大致有以下幾項處理字串的函數:
- 切割:`strsplit()`
- 子集:`substr()`
- 大小寫轉換:`toupper()`、`tolower()`
- 兩文字連接:`paste()`與`paste0()`
- 文字取代:`gsub()`
- 前後空白去除:`str_trim()`
```r
strsplit("Hello 你好", " ")
## [[1]]
## [1] "Hello" "你好"
toupper("hello R language")
## [1] "HELLO R LANGUAGE"
tolower("HELLO R LANGUAGE")
## [1] "hello r language"
paste("Hello", "R language", sep=' ')
## [1] "Hello R language"
substr("Hello R language", start=3, stop=7)
## [1] "llo R"
gsub("e","3","Hello R language")
## [1] "H3llo R languag3"
install.packages("stringr")
library(stringr)
str_trim(" Hello World ")
## [1] "Hello World"
```
#### 字串搜尋
如果我們想要查詢字串是否包含某個文字,我們可以用`grep()`與`grepl()`兩個函數得到結果,前者回傳符合條件之索引值,後者則回傳符合條件之布林值,包含的話就是`TRUE`,不包含則為`FALSE`。今天老師在上課時想要點人起來回答問題,但不知道要點誰,於是老師想出了一個點名的方式:名字裡面含有「君」的就要起來回答。我們就可以利用下面的程式碼幫助老師點名:
```r
Stu_names <- c("張儒邦", "林士淑", "張佩君", "陳彥君", "楊怡君", "鄭筱文", "鄭健銘", "林子士", "昌婉佩", "吳淑志")
grep("君",Stu_names)
## [1] 3 4 5
grepl("君",Stu_names)
## [1] FALSE FALSE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
```
#### 字串之正則表達式
正則表達式使用單個字符串來描述、匹配一系列符合某個句法規則的字符串。在很多文本編輯器裡,正則表達式通常被用來檢索、替換那些符合某個模式的文本。[^2]
$$
\begin{array}{lll}
\hline
語法 & 正則表達式 & 範例 \\
\hline
整數 & \texttt{[0-9]+} & 5815 \\
浮點數 & \texttt{[0-9]+.[0-9]+} & 58.15 \\
純英文字串 & \texttt{[A-Za-z]+} & APpLe \\
\text{Email} & \texttt{[a-zA-Z0-9_]+@[a-zA-Z0-9._]+} & \texttt{im@mail.cgu.edu.tw} \\
\text{URL} & \texttt{http://[a-zA-Z0-9./_]+} & \texttt{http://www.is.cgu.edu.tw/}\\
\hline
\end{array}
$$
利用正則表達式,我們可以省略許多時間(省去了我們從 0 輸入到 9 與 A 輸入到 Z 的工作)。此外,搭配一些表達的方式可以篩選、搜尋字串。而關於正則表達式,礙於篇幅限制,請參考[曾意儒教授的文章](https://yijutseng.github.io/DataScienceRBook/manipulation.html)。
##### 表達數量
* `*`:出現$0\sim$無限多次
* `+`: 出現$1\sim$無限多次
* `?`:出現 $0\sim 1$次
* `{n}`:出現 $n$ 次
* `{n,}`:出現 $n\sim$無限多次
* `{n,m}`:出現 $n\sim m$次
##### 表達位置
* `^`: 出現在字串開始的位置
* `$`:出現在字串結束`ˇ`的位置
* `\b`:出現空字串(空白)開始或結束的位置
* `\B`:出現非字串開始或結束的位置
##### 邏輯運算子
* `.`:出現所有的字元一次,包括空字串
* `[...]`:出現字元清單($\cdots$)中的字元一次,可用-表示範圍,如`[A-Z]`,`[a-z]`,`[0-9]`
* `[^...]`:不出現字元清單($\cdots$)中的字元
* `\`:要搜尋字串中的特殊字元時,前方須加上`\`
* `|`“:或
### 資料組合與結合
針對目前的資料,假設我們有新的數據想要加入原有的數據,我們可以用`rbind()`與`cbind()`進行組合,從函數的名字即可看出端倪,前者是以行(row)為單位加入,後者則是以欄(column)為單位加入。
```r
wage <- rbind(wage,
c("阿福", 72393, 1))
wage
## Name Wage Gender
## 1 大明 23135 1
## 2 小華 41465 0
## 3 阿天 35762 1
## 4 阿國 90424 1
## 5 阿福 72393 1
```
資料結合則是將兩筆資料進行整併,比如
```r
student <- data.frame(Name=c("大明","小華","阿天","阿國"),
StuID=c("B09302290","B10106090","B07106090","B08901089"))
student
## Name StuID
## 1 大明 B09302290
## 2 小華 B10106090
## 3 阿天 B07106090
## 4 阿國 B08901089
Score <- data.frame(StuID=c("B09302290","B08901089"),
score=c(78,94))
Score
## StuID score
## 1 B09302290 78
## 2 B08901089 94
merge(student, Score, by="StuID")
## StuID Name score
## 1 B08901089 阿國 94
## 2 B09302290 大明 78
```
### 處理遺漏值
[^1]:本文參考自 6 資料處理與清洗 | 資料科學與R語言. https://yijutseng.github.io/DataScienceRBook/manipulation.html.
[^2]:“正則表達式.” Wikipedia, Wikimedia Foundation, 2 Oct. 2022, https://zh.m.wikipedia.org/zh-hant/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.