---
disqus: yueswater
---
# R 語言與 tidyverse 套件
{%hackmd @themes/orangeheart %}
<style>
.likecoin-button {
position: relative;
width: 100%;
max-width: 485px;
max-height: 240px;
margin: 0 auto;
}
.likecoin-button > div {
padding-top: 49.48454%;
}
.likecoin-button > iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
###### tags: `R Language`
<!-- 參考文件:https://statacumen.com/teach/ShortCourses/R_Programming/IntroTidyverse_ACASA_201802/Erhardt_RTidyverse_20180216.pdf -->
## 安裝 tidyverse 套件
首先我們需要安裝`tidyverse`套件,其中包含`ggplot2`、`tibble`、`dplyr`等重要的相依套件。
```r
install.packages("tidyverse")
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.3.6 ✔ purrr 0.3.4
## ✔ tibble 3.1.8 ✔ dplyr 1.0.10
## ✔ tidyr 1.2.1 ✔ stringr 1.4.1
## ✔ readr 2.1.2 ✔ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────────tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
```
接著引用套件:
```r
library(tidyverse)
```
`tidyverse`套件很可愛地提供了`tidyverse_logo()`這個函式,使用後就可以看到`tidyverse`的 logo。
```r
tidyverse_logo()
⬢ __ _ __ . ⬡ ⬢ .
/ /_(_)__/ /_ ___ _____ _______ ___
/ __/ / _ / // / |/ / -_) __(_-</ -_)
\__/_/\_,_/\_, /|___/\__/_/ /___/\__/
⬢ . /___/ ⬡ . ⬢
```
## 開始使用 tidyverse:暖身操
在正式使用`tidyverse`之前,首先要來介紹一些好用的指令。相信如果有看過其他人使用`tidyverse`套件的程式碼的人,一定對`%>%`指令不陌生,但它代表什麼意思呢?這個指令叫做 **pipe**,中文可以翻譯成**通道**(但我不喜歡用這個翻譯,所以下面一律都會使用 pipe),這個指令提供我們能夠將兩個東西進行連接,有點類似合成函數的概念(但不完全是)。舉例來說
```
x %>% f(y)
```
會等於`f(x, y)`,而
```
x %>% f(y) %>% g(z)
```
會等於`g(f(x, y), z)`。我們使用`tidyverse`套件中的汽車資料`mpg`來做實例:
:::info
**小試身手:`%>%`的操作**
使用`%>%`找出`mpg`資料庫變數與觀察值數量
:::spoiler 查看參考答案
```r
## 變數數量
mpg %>%
colnames() %>%
length()
## 觀察值數量
mpg %>%
rownames() %>%
length()
```
:::
### tibble 的操作
`tibble`可以視為`data.frame`資料型別的進階版,基本上使用方式都相同,只是加上了一些額外的功能。如果我們要將原本的`data.frame`型別轉換成`tibble`,可以使用`as_tibble()`函式(`tibble 2.0.0.`版本已經將原本的`as.tibble()`換掉了,因此要使用文中的函式)。
```r
mpg <- as_tibble(mpg)
head(mpg)
# A tibble: 6 × 11
## manufacturer model displ year cyl trans drv cty hwy fl class
## <chr> <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 audi a4 1.8 1999 4 auto(l5) f 18 29 p compact
## 2 audi a4 1.8 1999 4 manual(m5) f 21 29 p compact
## 3 audi a4 2 2008 4 manual(m6) f 20 31 p compact
## 4 audi a4 2 2008 4 auto(av) f 21 30 p compact
## 5 audi a4 2.8 1999 6 auto(l5) f 16 26 p compact
## 6 audi a4 2.8 1999 6 manual(m5) f 18 26 p compact
class(mpg)
## [1] "tbl_df" "tbl" "data.frame"
```
我們也可以用`glimpse()`或`str()`函式查看資料。
#### 手動創建 tibble
如同`data.frame`型別一般,我們也可以手動創建`tibble`:
```r
tibble(
x = 1 : 6,
y = 2,
z = log(x) + y
)
# A tibble: 6 × 3
## x y z
## <int> <dbl> <dbl>
## 1 1 2 2
## 2 2 2 2.69
## 3 3 2 3.10
## 4 4 2 3.39
## 5 5 2 3.61
## 6 6 2 3.79
```
或是以轉置(transposed)的方式創建,我們使用`tribble`函式。
```r
tribble(
~x, ~y, ~z,
"你好", 1, TRUE,
"早安", 3.2, NA
)
# A tibble: 2 × 3
## x y z
## <chr> <dbl> <lgl>
## 1 你好 1 TRUE
## 2 早安 3.2 NA
```
#### 印出指定行、列數
如果我們想要指定`tibble`印出指定的行(column)、列(row)數,我們可以在`print()`函數中指定。
```r
mpg %>%
print(n = 3, width = Inf)
# A tibble: 234 × 11
## manufacturer model displ year cyl trans drv cty hwy fl class
## <chr> <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 audi a4 1.8 1999 4 auto(l5) f 18 29 p compact
## 2 audi a4 1.8 1999 4 manual(m5) f 21 29 p compact
## 3 audi a4 2 2008 4 manual(m6) f 20 31 p compact
```
上面的程式碼代表我們印出 3 行與所有欄位的`mpg`資料。
#### 取出特定行、欄
我們可以使用`$`或`[[]]`來查看資料。
```r
tbl <- tibble(
x = runif(5),
y = rnorm(5)
)
## check the x column
tbl$x
## [1] 0.7007443 0.3922571 0.6044753 0.6750059 0.9717587
tbl[[1]]
## [1] 0.7007443 0.3922571 0.6044753 0.6750059 0.9717587
## using pipe
tbl %>% .$x
## [1] 0.7007443 0.3922571 0.6044753 0.6750059 0.9717587
tbl %>% .[[1]]
## [1] 0.7007443 0.3922571 0.6044753 0.6750059 0.9717587
```
## 讀取資料
`tidyverse`提供了使用者匯入資料的相依套件。以下我們就簡單介紹:
- `readr`:可匯入`.csv`、`.tsv`、`.delim`、`.log`檔
- `readxl`:匯入`.xlsx`、`.xls`檔
- `haven`:匯入`.dta`、`.sas`、`.spss`、`.xpt`檔
- `jsonlite`:匯入`.json`檔
- `rvest`:提供使用者進行爬蟲
### 安裝`readr`套件與匯入資料
如果在安裝`tidyverse`後仍沒有辦法使用`readr`,則可以使用以下方式安裝並使用套件:
```r
install.packages("readr")
library(readr)
```
在[資料視覺化:使用 ggplot2](https://hackmd.io/@yueswater/ggplot2)這篇文章中,我們使用的是`read.csv()`函式,這是`R`原本就有的。既然已經有了讀取`.csv`檔的函式,為何還要使用新的呢?以下我們就沿用該篇文章中提到的`pizza.csv`進行舉例。首先,`read_csv()`與`read.csv()`使用方式相同,接下來我們就來看為何我們要使用新的函式。我們藉由`system.time()`這個函式計算系統的運行速度:
```r
system.time(pizza_base <- read.csv("https://chris.userweb.mwn.de/book/pizza_delivery.csv"))
## user system elapsed
## 0.063 0.038 2.732
system.time(pizza_readr <- read_csv("https://chris.userweb.mwn.de/book/pizza_delivery.csv"))
## user system elapsed
## 0.144 0.026 2.599
```
可看到新函式的系統運算速度較原本的快,因此以後多多使用`read_csv()`!此外,我們可以指定當讀取文件時出現未預期的值時,若想要給予其`NA`的編碼,則在`read_csv()`後加上`na = c()`,其中`c()`為要取代的值。
### 安裝`readxl`套件與匯入資料
同樣地,我們可以透過下面的方式安裝`readxl`套件。
```r
install.packages("readxl")
library(readxl)
```
如果我們想要讀取某個`.xlsx`檔,則使用下列方式:
```r
dat_xlsx <- read_xlsx("xlsx_data.xlsx", sheet = "Sheet 1")
```
可以看到我們利用`sheet = "Sheet 1"`指定應讀取哪些試算表。
<!-- parse_*()函數 -->
### 安裝`haven`套件與匯入資料
我們可以透過下面的方式安裝`haven`套件。
```r
install.packages("haven")
library(haven)
```
利用下面的方式讀取檔案:
```r
library(haven)
read_sas("SAS_data.sas7bdat")
read_spss("SPSS_data.sav")
read_stata("Stata_data.dta")
```
### 使用`rvest`讀取網頁表格
我們可以透過下面的方式安裝`rvest`套件。
```r
install.packages("rvest")
library(rvest)
```
假設我們今天想要爬取[List of countries by GDP (nominal)](https://en.wikipedia.org/wiki/List_of_countries_by_GDP_(nominal))這個網站中的表格:

首先我們要知道,`rvest`這個套件之作用類似於爬蟲,因此我們如果想要爬取網頁的元素,就需要透過`html`程式碼或`xpath`、`css`selector 等方式獲取資料。以下我們使用`xpath`
```r
gdp <- read_html("https://en.wikipedia.org/wiki/List_of_countries_by_GDP_(nominal)")
gdp_table <- gdp %>%
html_node(xpath = '//*[@id="mw-content-text"]/div[1]/table[2]') %>%
html_table() %>%
as_tibble(.name_repair = "universal")
```
而獲取`xpath`的方式參考如下:首先在網頁任意處點擊右鍵

點選檢查(或 inspect),接著就可以看到以下畫面

接著使用網頁原始碼瀏覽器中的選擇器點選表格


接著在原始碼瀏覽器中表格的地方點擊右鍵,依序按下`Copy`、`Copy XPath`即可獲得`xpath`。

不過我們可以注意到,原始的表格是有部分欄位是合併的,因此跑出來的結果欄位名稱會有重複的情況,因而得到下面的報錯訊息:
```r
Error:
! Column names `IMF[1]`, `World Bank[13]`, and `United Nations[14]` must not be duplicated.
Use .name_repair to specify repair.
Caused by error in `repaired_names()`:
! Names must be unique.
✖ These names are duplicated:
* "IMF[1]" at locations 3 and 4.
* "World Bank[13]" at locations 5 and 6.
* "United Nations[14]" at locations 7 and 8.
```
因此我們需要在`as_tibble()`中加上`.name_repair = "universal"`,讓系統自動幫我們設定新的欄位名稱。
<div class="likecoin-embed likecoin-button">
<div></div>
<iframe scrolling="no" frameborder="0" src="https://button.like.co/in/embed/xiaolong70701/button?referrer=hackmd.io"></iframe>
</div>