---
# System prepended metadata

title: 【R 語言與統計資料分析】Ch5：R 繪圖與 ggplot2、plotly 實戰
tags: [統計學與R語言]

---

---
title: 【R 語言與統計資料分析】Ch5：R 繪圖與 ggplot2、plotly 實戰
image: https://ppt.cc/fnT4xx@.png
---

# 【R 語言與統計資料分析】Ch5：R 繪圖與 ggplot2、plotly 實戰

# 5.1 什麼是圖表?

**圖表** (Charts/Graphs) 是透過視覺化手段來呈現數據、統計數字或複雜概念的工具，旨在**簡化資訊**並**凸顯資料間的結構、趨勢與差異**。

## 5.1.1 數據圖表 

**數據圖表** (Chart) 為帶有量化數據的圖形或圖案，即具圖像化的資料表，用以使讀者方便理解大量數據，以及數據之間的關係。如：圓餅圖、長條圖、直方圖、折線圖等各式統計圖表。

<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/r1VqrYNwWg.png" width="200">
  <br>
  <span style="color: gray; font-size: 0.8em;">圓餅圖就是一種數據圖表</span>
</div>




## 5.1.2 資訊圖表

**資訊圖表** (Infographics) 其英文顧名思義為「資訊」 (information) 以及「圖表」(graphic) 所結合而成的複合單字，指資料、資訊或知識的視覺化表現形式。目的為清楚準確的解釋或表達甚為複雜且大量的資訊，被廣泛應用在各式各樣的文件檔案上。

<div style="text-align: center;">
  <img src="https://www.ydn.com.tw/api/ugc_imagehandler.ashx?NFID=906464" width="300">
  <br>
  <span style="color: gray; font-size: 0.8em;">由中央氣象署發布的資訊圖表</span>
</div>

## 5.1.3 圖表的 TOP 原則

#### Target（對象）：圖表是給誰看的？

我們必須優先確認受眾是誰，再進一步決定圖表的呈現的深度與複雜度。在設計時，或許可以透過顏色、標記或直接在標題寫出結論，引導受眾在最短時間內抓到重點。

#### Object（目的）：希望傳達出什麼訊息或達成什麼目的？

釐清目的能決定圖表的類型選擇與敘事方式，比如針對不同的資料型態，使用對應的圖表進行敘事；對主管報告，優先提供決策所需的資訊等等。

#### Place（場合）：圖表用什麼形式展現？

圖表呈現的場合是相當重要的，會直接影響圖表的「視覺設計細節」，如: 放在正式文件、內部會議簡報、社群媒體文案，還是 E-mail 附件等等。若是現場簡報，圖表應簡潔並由講者輔助；若是電子文件，圖表必須具備自我解釋能力（Self-explanatory）。


# 5.2 在 R 中用 `Base` 繪製圖表

## 5.2.1 用 `par()` 開始處理畫布

#### 用 `mar` 控制邊界

<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/BJMAhqEwZg.png" width="350">
  <br>
  <span style="color: gray; font-size: 0.8em;">mar = c(下, 左, 上, 右)</span>
</div>

```R
# mar = c(下, 左, 上, 右)
# 邊界超小，圖片超大
par(mar = c(1,1,1,1) )
boxplot(iris$Sepal.Width ~ iris$Species,
        ylab = "Sepal width (cm)",
        main = "Anderson's Iris Data")
        
# 邊界超大，圖片超小
par(mar = c(15,15,15,15) )
boxplot(iris$Sepal.Width ~ iris$Species,
        ylab = "Sepal width (cm)",
        main = "Anderson's Iris Data")
```


#### 用 `mfcol` 與 `mfrow` 來合併繪圖

在進行圖片繪製時，常常會需要將多張圖片放置在一起排版，此事就可以使用 `mfcol` 與 `mfrow` 來進行版面的規劃。`mfcol` 為依照「**直行**」的方向進行排列，而 `mfrow` 為依照「**橫列**」的方向進行排列。

```R
# mfcol 的範例
par(mfcol = c(3,2)) #指形成一個 3x2 的繪圖矩陣

boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot A")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot B")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot C")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot D")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot E")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot F")
```
```R
# mfrow 的範例
par(mfrow = c(3,2)) #指形成一個 3x2 的繪圖矩陣

boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot A")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot B")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot C")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot D")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot E")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot F")
```

在執行 `par(mfcol = c(3,2))` 或 `par(mfrow = c(3,2))` 後，畫圖的布局就已被更改，下次若要繪圖，一樣會從繪圖區的左上角依序繪製。
若要恢復成單一圖表狀態，可使用 `par(mfcol = c(1,1))` 更改布局或是使用 `dev.off()` 來恢復成預設狀態。

#### 用 `layout` 做不對稱排版
除了對稱的排版之外，我們也會使用不對稱的排版。舉例來說，我們可能會想要凸顯某一張圖作為重點，而其他圖片作為此議題的補充，此時就會想把重點圖片作放大，其餘部分就可以做為陪襯。
關於 `layout()` 函數的應用，與矩陣呈現的形式息息相關，我們直接以實際案例作介紹:
```
matrix(c(1,1,2,3),2,2) #先看矩陣長什麼樣子
layout(matrix(c(1,1,2,3),2,2))
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot A")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot B")
boxplot(iris$Sepal.Width ~ iris$Species,
        main = "Plot C")
```

<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/HkjkjjNPZg.png" width="300">
  <br>
  <span style="color: gray; font-size: 0.8em;">用 layout() 做不對稱排版</span>
</div>


## 5.2.3 圖形基本設定

在 `par()` 中，我們也可以控制畫圖的細節部分，如線條樣式、顏色等等，此外也可以在如 `plot()` 等繪圖函數中進行設定。

 - `lwd`：線條粗度
 - `type`：點與點間連線樣式
 - `lty`：線條樣式
 - `cex`：字體大小
 - `pch`：點的樣式
 - `font`：控制粗體斜體
 - `col`：顏色控制，

<div style="text-align: center;">
  <img src="https://r-graph-gallery.com/6-graph-parameters-reminder_files/figure-html/thecode-1.png" width="300">
  <br>
  <span style="color: gray; font-size: 0.8em;">R 繪圖設定代碼</span>
</div>


在色彩方面，可以使用不同的代碼，在 R 中都可以去解讀:

- 文字名稱：如 "red"、"skyblue"、"gold"。R 內建超過 600 種顏色名稱（可用 `colors()` 查看）。
- 十六進位碼：如 "#FF5733"，適合與網頁設計或品牌色對接。
- 數字代號：1=黑, 2=紅, 3=綠, 4=藍 ...。

```R
# 產生測試數據
x <- 1:10
y <- x^2

# 繪圖
plot(x, y, 
     main = "Analysis",         # main
     xlab = "(sec)",       # xlab
     ylab = "(Value)",         # ylab
     col = "darkorange",            # col
     pch = 21,                      # pch (使用帶邊框的圓)
     bg = "yellow",                 # 配合 pch=21 的填充色
     cex = 2,                       # cex (放大點的大小)
     type = "b",                    # 同時畫點和線 (both)
     lty = 2,                       # lty (使用虛線)
     lwd = 2)                       # 加粗線條
```


<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/BJrxraEv-l.png" width="300">
  <br>
</div>

## 5.2.3 基本繪圖補充

在 [Ch2](https://hackmd.io/@wwwh0225/H1ImLs9rZg#24-%E6%95%B8%E6%93%9A%E6%B4%9E%E5%AF%9F%EF%BC%9A%E6%95%98%E8%BF%B0%E7%B5%B1%E8%A8%88%E8%88%87%E7%B9%AA%E5%9C%96%E6%87%89%E7%94%A8) 中，我們已有稍微提到繪圖相關的程式碼，我們在此做一些小補充。
當我們畫好一張圖後，若要進行修改，在 R 中是比較困難的。在 R 中的繪圖是類似「疊圖」的方式進行繪製，因此若一步做錯，整張圖就需要重新再繪製一次。

讓我們以與前面章節同樣的一張圖做延伸:
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/BJrxraEv-l.png" width="200">
  <br>
</div>

- 加上「**點**」：`points(x,y)`
    ```R
    points(6,60,pch = 11)      
    ```
- 加上「**線段**」：`lines(x,y)`
    ```R
    #畫上兩點
    x1 <- c(2,4)
    y1 <- c(80,80)
    points(x1,y1 ,pch = 15)
    #連起來
    lines(x1,y1)
    ```
- 加上「**文字**」：`text(x,y,labels)`
    ```R
    text(8,20,"GOOD")  #在 (8,20) 寫字
    ```
- 加上「**直線**」：`abline(a,b)` 為 $y=a+bx$ 的直線
    ```R
    #加上 45度 直線
    abline(1,1)
    #加上水平線
    abline(h = 50)
    #加上垂直線
    abline(v = 5)
    ```    

  <div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/rk6cfAVDWl.png" width="300">
  <br>
</div>

## 5.2.4 中文字體處理
在進行 R 語言中的繪圖時，中文字體有時會出現顯示異常的情形，我們以下列程式碼為例:
```R
curve(dnorm(x, mean = 0, sd = 1), 
      from = -3, to = 3, 
      col = "blue",          # 線條顏色
      lwd = 2,               # 線條寬度
      main = "標準常態分佈 N(0,1) pdf", 
      xlab = "z 分數", 
      ylab = "機率密度")
```
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/ByUMURVvbl.png" width="300">
  <br>
  <span style="color: gray; font-size: 0.8em;">有時候中文字體會顯示不出來</span>
</div>

這時，我們必須強制指定一個中文字體 (`family`)，讓 R 語言可以正確地進行渲染編碼。
```R
# 以 Mac 電腦為例
curve(dnorm(x, mean = 0, sd = 1), 
      from = -3, to = 3, 
      col = "blue",          # 線條顏色
      lwd = 2,               # 線條寬度
      main = "標準常態分佈 N(0,1) pdf", 
      xlab = "z 分數", 
      ylab = "機率密度",
      family = "STKaiti") # 此處填寫字體名稱
```
    
<div style="text-align: center;">
  <img src="https://ppt.cc/fSJMGx@.png" width="350">
  <br>
  <span style="color: gray; font-size: 0.8em;">指定中文字體後就可以正確顯示</span>
</div>

找尋字體名稱的方法，可以參考[此網址](https://r-lover.com/tutorial/visualization/chinese-in-ggplot2/#%E5%9C%A8Windows%E7%B3%BB%E7%B5%B1%E4%B8%AD%E5%91%88%E7%8F%BE%E4%B8%AD%E6%96%87%E5%AD%97%E9%AB%94)。
    
# 5.3 用 `ggplot2` 套件進行繪圖

`ggplot2` 是由 Hadley Wickham 開發，基於 Leland Wilkinson 的 「圖形語法」(Grammar of Graphics) 理論，而它的核心思想是：任何圖表都可以拆解成相同的基本組件。
<div style="text-align: center;">
  <img src="https://ggplot2.tidyverse.org/articles/ggplot2_files/figure-html/overview_graphic-1.png" width="350">
  <br>
  <span style="color: gray; font-size: 0.8em;">ggplot2 的圖層概念</span>
</div>
繪製一張 ggplot2 圖表就像疊加投影片的圖層，基本概念如下:

$$\text{ggplot}(\text{data}) + \text{aes}(\text{mappings}) + \text{geom_xxx}()$$

若要使用 `ggplot2` 套件，需要在 R 中先下載此套件:
```R
# 下載套件
install.packages("ggplot2") 
```
```R
# 呼叫套件
library(ggplot2)
```
在 `ggplot2` 中，許多的設定都已被模組化寫好，讓我們以實際案例來介紹 `ggplot2` 的強大之處。

## 5.3.1 直方圖 

我們使用 `geom_histogram` 來繪製直方圖。

我們在此使用美國高中生的身高資料，請先載入套件後，使用 `heights` 資料集。由於 `heights` 資料集內的身高為英吋單位，我們在此先轉換為公分單位。
```
library(dslabs) #載入套件中的資料集
# 如果沒有此套件，請執行 install.packages("dslabs")
# 觀看查詢前 6 筆資料
heights$height_cm <- heights$height * 2.54  #英吋換成公分
head(heights)
```
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/Bye2fFHDbx.png" width="200">
  <br>
  <span style="color: gray; font-size: 0.8em;">身高資料 (cm)</span>
</div>


接著，我們將男生與女生的身高分佈統一畫在同一張圖內。這跟先前所使用的 `par()` 設定不同，`par()` 的並圖是分別畫完一張圖後放入設定好的空間；而 `ggplot2` 是在內部會依據類別的數量自動規劃圖形的合併與呈現規劃。此處的 `facet_wrap` 與 `fill = sex` 就是扮演這個角色。
由於中文字型需要被另外設定，我們需要在 `theme` 中加入指定字體的程式碼，如：`element_text(family = "PingFang TC")`，各台電腦所要對應的字體就如同前述，大家可以測試看看。

```R
ggplot(heights, aes(x = height_cm, fill = sex)) +
  geom_histogram(binwidth = 5, color = "black", alpha = 0.7) + # 設定 binwidth 和透明度
  facet_wrap(~ sex, scales = "free_y") + # 依據 sex 拆圖，scales="free_y" 讓y軸範圍各自調整
  labs(
    title = "男生與女生身高分佈直方圖",
    subtitle = "分別顯示",
    x = "身高 (cm)",
    y = "人數 (Count)",
    fill = "性別"
  ) +
  theme_minimal() +
  scale_fill_manual(values = c("Female" = "salmon", "Male" = "skyblue")) + # 手動設定顏色
  theme(text = element_text(family = "PingFang TC")) # 設定文字字體
```

<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/SkWbmKBwbl.png" width="340">
  <br>
  <span style="color: gray; font-size: 0.8em;">依照性別繪製直方圖</span>
</div>

## 5.3.2 長條圖 

在 `ggplot2`，我們使用 `geom_bar` 來繪製長條圖，以下使用 `mpg` 資料集，裡面搜集了許多關於不同品牌汽車油耗的資料。

```R
# 觀察前 6 筆資料
head(mpg)
```
依照汽車品牌繪製長條圖，由於某些品牌名稱較長，我們設定品牌名稱會旋轉 45 度後呈現：
```R
# 長條圖

ggplot(mpg, aes(x = manufacturer)) +
  geom_bar(fill = "steelblue") +
  theme_minimal() +
  labs(title = "各品牌汽車數量長條圖", x = "品牌", y = "數量") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        text = element_text(family = "PingFang TC")) # 設定文字字體
```
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/HkpcUtBvZe.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">汽車品牌長條圖</span>
</div>
 

若我們想要依照不同車型上色，一樣是在 `aes()` 中加入 `fill = class`。
 
 
```R
# 以車型分類

ggplot(mpg, aes(x = manufacturer, fill = class)) +
  geom_bar() +
  scale_fill_brewer(palette = "Set3")+ # 顏色調色盤
  theme_minimal() +
  labs(title = "各品牌之車型組成長條圖",
       x = "品牌",
       y = "數量",
       fill = "車型類別") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        text = element_text(family = "PingFang TC")) # 設定文字字體
  ```
 <div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/r1aK8KSv-e.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">汽車品牌長條圖 - 以類別分組</span>
</div>

## 5.3.3 散佈圖
在 ggplot 繪製散佈圖也是十分簡單，只要將 x 軸 與 y 軸對應的座標點匯入即可：

```
# 散佈圖
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(size = 3) +  # 設定點的大小
  theme_minimal() +       # 使用簡潔主題
  labs(title = "Iris 鳶尾花花萼長寬散佈圖",
       x = "花萼長度 (Sepal Length)",
       y = "花萼寬度 (Sepal Width)") +
  theme(text = element_text(family = "PingFang TC")) # 設定文字字體
```

 <div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/BJ9Q_KBD-g.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">鳶尾花散佈圖</span>
</div>

如果我們想要把「圖例」移到下方怎麼辦呢? 
善用 ChatGPT、Gemini 等工具!!!
 
```R
# AI 修訂
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(size = 3) +  # 設定點的大小
  theme_minimal() +       # 使用簡潔主題
  labs(title = "Iris 鳶尾花花萼長寬散佈圖",
       x = "花萼長度 (Sepal Length)",
       y = "花萼寬度 (Sepal Width)") +
  theme(legend.position = "bottom") #圖例放在最下面
```

<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/r1fYWVLvWx.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">鳶尾花散佈圖</span>
</div>

此外，`ggplot2` 也可以很簡單地插入變數間的迴歸線，用以表示資料之間的關聯趨勢。


```R
# 加入預測線
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(size = 3) +  # 設定點的大小
  theme_minimal() +       # 使用簡潔主題
  labs(title = "Iris 鳶尾花花萼長寬散佈圖",
       x = "花萼長度 (Sepal Length)",
       y = "花萼寬度 (Sepal Width)") +
  theme(text = element_text(family = "PingFang TC"))+ # 設定文字字體
  geom_smooth(method = "lm", color = "black")  # 畫出一條整體的黑色預測線
```
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/S1DvOKBvbg.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">鳶尾花散佈圖與迴歸直線</span>
</div>


先前，我們曾介紹 `pair()` 函數，用來直接繪製所有資料兩兩間的關係，在 `ggplot2` 的世界裡，我們可以套用 `GGally` 套件，可以給予我們更豐富的分析視角。

`GGally` 除了提供散佈圖之外，還會回報變數間的相關係數、核密度估計 (kernel density estimation, KDE) 以及長條圖及盒狀圖等圖形。

```R
# 安裝並載入套件
install.packages("GGally")
```

```R
library(GGally) # 記得執行
library(ggplot2) # 記得執行
ggpairs(iris, 
        aes(color = Species, alpha = 0.5),
        title = "GGally 矩陣圖：包含相關係數與分佈") +
  theme_bw() +
  theme(text = element_text(family = "PingFang TC")) # 設定文字字體
```
![image](https://hackmd.io/_uploads/HkY3LYHv-x.png)



## 5.3.4 熱力圖

熱力圖 (Heatmap) 是一種用顏色表示數值大小的二維圖表，適合用在矩陣型資料 (如：時間 × 地點 → 銷售額)、數值變數之間的相關係數等情境。
我們以 `mtcars` 資料集為例，顯示 32 款不同汽車的性能數據: 

```R
# 先閱讀資料
head(mtcars)
```
我們希望觀察資料間相關係數的強弱關係，透過熱力圖將相關係數矩陣視覺化:

```R
library(ggplot2)
library(reshape2) # 若無此套件，請記得下載install.packages("reshape2")

# 取數值欄位
num_mtcars <- Filter(is.numeric, mtcars)


# 計算相關係數
cor_mat <- cor(num_mtcars)

# 用 melt 轉長表
long_df <- melt(cor_mat)   
print(head(long_df))

# 畫 heatmap
ggplot(long_df, aes(x = Var1, y = Var2, fill = value)) +
  geom_tile() +
  scale_fill_gradient2(low="blue", mid="white", high="red", limits=c(-1,1)) +
  labs(title="mtcars Correlation Heatmap", fill="Correlation") +
  theme_minimal()
```

<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/SJj2jPvPWx.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">相關係數熱力圖</span>
</div>




## 5.3.4 折線圖

折線圖是分析時間資料常會用到的圖形，我們以航空業資料 `AirPassengers` 來做介紹，我們先將其轉為 `dataframe` 格式後再行繪圖。

```R
df_air <- data.frame(
  Year      = as.numeric(floor(time(AirPassengers))),
  Month     = as.numeric(cycle(AirPassengers)),
  Passengers = as.numeric(AirPassengers)
)

# 將月份數字轉為英文縮寫 (如 1 轉為 Jan)
df_air$Month_Name <- factor(month.abb[df_air$Month], levels = month.abb)

head(df_air)
```
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/BkUhSVIvWe.png" width="200">
  <br>
  <span style="color: gray; font-size: 0.8em;">依照年份、月份分類的航空業旅客資料</span>
</div>

接下來，讓我們依照年分畫出對應的折線圖:
```R
# 折線圖
ggplot(df_air, aes(x = Month_Name, y = Passengers, group = Year, color = factor(Year))) +
  geom_line(size = 1) +           # 畫出折線
  geom_point(size = 1.5) +        # 加入數據點，方便觀察每個月的數值
  scale_color_viridis_d() +       # 使用 viridis 調色盤，顏色過渡較自然且專業
  theme_minimal() +               # 使用簡潔主題
  labs(
    title = "AirPassengers 每月乘客數年度變化圖",
    subtitle = "觀察各月份在不同年份的增長趨勢",
    x = "月份",
    y = "乘客數",
    color = "年份"                # 圖例名稱
  ) +
  theme(
    legend.position = "right",    # 圖例放在右側
    plot.title = element_text(face = "bold", size = 14),
    text = element_text(family = "PingFang TC")
  )
```

<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/HyhOOFBP-e.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">依照年份、月份分類的航空業旅客數折線圖</span>
</div>








## 5.3.5 標題位置調整

從我們畫的這幾張圖中，可以看到 `ggplot2` 對於圖表標題的預設是於左上方開始進行呈現。但我們常常會希望進行標題置中，那此時就需要於 `theme()` 中設定 `plot.title = element_text(hjust = 0.5)`，`hjust = 0.5` 是置中的意思，預設的位置是在 0。




```R
# 散佈圖
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(size = 3) +  # 設定點的大小
  theme_minimal() +       # 使用簡潔主題
  labs(title = "Iris 鳶尾花花萼長寬散佈圖",
       x = "花萼長度 (Sepal Length)",
       y = "花萼寬度 (Sepal Width)") +
  theme(text = element_text(family = "PingFang TC"), # 設定文字字體
        plot.title = element_text(hjust = 0.5)) # 置中標題
```
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/H195_FSPZe.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">標題置中的鳶尾花散佈圖</span>
</div>


# 5.4 `plotly` 套件應用

`plotly` 是一個基於 JavaScript (plotly.js) 的繪圖庫，就像是一個儀表板一樣，我們可以透過滑鼠與資料進行互動，或是對圖表進行縮放等直觀性操作。

```R
# 下載套件
install.packages("plotly")
```

## 5.4.1 `plotly` 內建寫法

我們可以透過 `plot_ly()` 函數去進行圖表的繪製，但這又得讓我們重新學一個繪圖的語法。
```R
plot_ly(data = iris, 
        x = ~Sepal.Length, 
        y = ~Sepal.Width, 
        color = ~Species, 
        type = "scatter", 
        mode = "markers")
```

## 5.4.1 使用 `ggplotly` 進行轉換

在 R 語言中，`plotly` 最強大的地方在於能透過 `ggplotly()` 函數與 `ggplot2` 無縫接軌。我們只需要照常繪製 `ggplot2` 圖形，再用 `ggplotly()` 包起來，靜態圖瞬間變成互動圖。


<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/rJektFBDWx.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">ggplotly</span>
</div>

```R
# 範例1
library(ggplot2)
library(plotly)

# 先畫一個標準的 ggplot
p <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point() +
  theme_minimal()

# 轉化為互動模式
ggplotly(p)
```


```R
# 範例2
library(ggplot2)
library(plotly)
# 折線圖轉為 plotly
lp <- ggplot(df_air, aes(x = Month_Name, y = Passengers, group = Year, color = factor(Year))) +
  geom_line(size = 1) +           # 畫出折線
  geom_point(size = 1.5) +        # 加入數據點，方便觀察每個月的數值
  scale_color_viridis_d() +       # 使用 viridis 調色盤，顏色過渡較自然且專業
  theme_minimal() +               # 使用簡潔主題
  labs(
    title = "AirPassengers 每月乘客數年度變化圖",
    subtitle = "觀察各月份在不同年份的增長趨勢",
    x = "月份",
    y = "乘客數",
    color = "年份"                # 圖例名稱
  ) +
  theme(
    legend.position = "right",    # 圖例放在右側
    plot.title = element_text(face = "bold", size = 14),
    text = element_text(family = "PingFang TC")
  )

ggplotly(lp)

```


# 5.5 盡信書，不如無書

## 5.5.1 Y 軸的陷阱

我們在進行資料時，目的就是希望能夠更直觀的去認識資料，但有時總是會有一些「**狀況**」是我們身為圖表繪製者以及讀者都要去注意的。

#### 折線圖的案例
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/HJKUAwvw-l.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">不同 Y 軸起點對視覺造成的影響</span>
</div>

```
# 安裝套件（如未安裝）
# install.packages("patchwork")

library(ggplot2)
library(patchwork) #如未安裝，請執行 install.packages("patchwork")

# 範例資料
df <- data.frame(
  year = 2015:2022,
  value = c(100, 102, 103, 105, 104, 106, 107, 108)
)

# 圖 1：Y 軸截斷
p1 <- ggplot(df, aes(x = year, y = value)) +
  geom_line(linewidth = 1) +
  geom_point(size = 3) +
  scale_y_continuous(limits = c(98, 106)) +
  labs(
    title = "Y 軸截斷，成長被誇大",
    x = "年份",
    y = "數值"
  ) +
  theme_minimal()

# 圖 2：完整 Y 軸
p2 <- ggplot(df, aes(x = year, y = value)) +
  geom_line(linewidth = 1) +
  geom_point(size = 3) +
  scale_y_continuous(limits = c(0, 110)) +
  labs(
    title = "完整期間 + Y 軸從 0 開始",
    x = "年份",
    y = "數值"
  ) +
  theme_minimal()

# 使用 patchwork 並排
p1 + p2

```

 

#### 長條圖的案例
<div style="text-align: center;">
  <img src="https://hackmd.io/_uploads/SydgZdDv-l.png" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">不同 Y 軸起點對視覺造成的影響</span>
</div>


```R
# 載入套件
library(ggplot2)
library(patchwork)

# 建立資料
df <- data.frame(
  城市 = c("桃園市", "新北市", "台南市", "高雄市", "台中市", "台北市"),
  COVID盛行率 = c(301, 300, 285, 275, 271, 223)
)

# 統一顏色
bar_color <- "#2c7fb8"

# 圖 1：完整 Y 軸從 0 開始
p1 <- ggplot(df, aes(x = reorder(城市, -COVID盛行率), y = COVID盛行率)) +
  geom_col(fill = bar_color) +
  geom_text(aes(label = COVID盛行率), vjust = -0.5) +
  scale_y_continuous(limits = c(0, 320)) +
  labs(
    title = "Y 軸從 0 開始",
    x = "城市",
    y = "盛行率 (每十萬人)"
  ) +
  theme_minimal() +
  theme(text = element_text(family = "PingFang TC"))

# 圖 2：Y 軸從 200 開始（截斷）
p2 <- ggplot(df, aes(x = reorder(城市, -COVID盛行率), y = COVID盛行率)) +
  geom_col(fill = bar_color) +
  geom_text(aes(label = COVID盛行率), vjust = -0.5) +
  coord_cartesian(ylim = c(200, 320)) +   # ← 改這裡
  labs(
    title = "Y 軸從 200 開始（截斷）",
    x = "城市",
    y = "盛行率 (每十萬人)"
  ) +
  theme_minimal() +
  theme(text = element_text(family = "PingFang TC"))


# 並排
p1 + p2
```




## 5.5.2 不要使用雙 Y 軸

在資料視覺化中，很多人會想把兩組數據放在同一張圖上，使用雙 Y 軸。然而，這種做法通常會**帶來誤導與理解成本**。以下是三個主要原因：

1. 容易製造「假相關」
2. 比例可被任意操弄
3. 增加讀者認知負擔

<div style="text-align: center;">
  <img src="https://image-cdn.learnin.tw/bnextmedia/image/album/2017-03/img-1490241486-76436.jpg?w=1200&output=webp" width="400">
  <br>
  <span style="color: gray; font-size: 0.8em;">雙軸圖與並排的差別<p>資料來源：《Google必修的圖表簡報術》，商業周刊出版</span>
</div>




# 參考資料
1. 陳旭昇(2024)，資料分析的統計學基礎:使用R語言，東華書局
2. 林建甫 Jeff Lin(2020)，[R 資料科學與統計](https://bookdown.org/jefflinmd38/r4biost)
3. Gareth James, Daniela Witten, Trevor Hastie, Robert Tibshirani. (2017). An Introduction to Statistical Learning: With Applications in R. New York: Springer.
4. 陳基國(2024). 基礎統計與R語言. 台北：五南圖書出版股份有限公司
5. 劉奕酉(2022)。[【數據思維】Chart.Guide 視覺化圖表的學習網站，告訴你如何正確的選擇與使用圖表？](https://vocus.cc/article/61d65794fd8978000153e662)
6. Phillips(2026). [YaRrr! The Pirate’s Guide to R](https://nathanieldphillips-yarrr.share.connect.posit.cloud/)


