視覺化的文法

The simple graph has brought more information to the data analyst’s mind than any other device.

John Tukey

基礎探索資料分析我們討論了如何透過 R 語言的內建繪圖系統:Base Plotting System 暸解資料的外觀、維度、變數分佈與相關等探索資料分析(Exploratory Data Analysis,EDA)的要素。除了內建繪圖系統以外,有極高比例的 R 語言使用者其實更依賴使用 ggplot2 這個套件,它以簡潔、彈性和美觀輸出快速擄獲資料科學團隊的芳心;命名之中 gg 指的是 grammer of graphics,套件作者是 Hadley Wickham 與 Winston Chang,核心理念是利用正規而有結構的文法來探索資料。

安裝與載入 ggplot2 套件

我們可以選擇透過命令列(Console)以 install.packages() 函數進行安裝。

# 安裝 ggplot2
install.packages("ggplot2")
1
2
## > # 安裝 ggplot2
## > install.packages("ggplot2")
## trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.5/ggplot2_3.1.0.tgz'
## Content type 'application/x-gzip' length 3622365 bytes (3.5 MB)
## ==================================================
## downloaded 3.5 MB
## 
## 
## The downloaded binary packages are in
##  /var/folders/0b/r__z5mpn6ldgb_w2j7_y_ntr0000gn/T//Rtmpw3pDJx/downloaded_packages
1
2
3
4
5
6
7
8
9
10

或是透過圖形化介面(Graphic User Interface, GUI)的方法安裝,在右下角的 packages 頁籤點選 install,再輸入套件名稱 ggplot2 點選安裝。

透過圖形化介面安裝

我們可以選擇透過命令列(Console)以 library() 函數將套件載入環境來使用。

# 載入 ggplot2
library(ggplot2)
1
2
## > # 載入 ggplot2
## > library(ggplot2)
1
2

或是透過圖形化介面(Graphic User Interface, GUI)在右下角的 packages 頁籤下搜尋 ggplot2 然後將前面的核取方框打勾。

透過圖形化介面載入

探索數值分佈

成功安裝且載入 ggplot2 套件後,我們利用探索數值分佈的直方圖暸解所謂的視覺化文法,與基礎探索資料分析同樣使用 R 語言的套件 gapminder 來載入一個簡易版 gapminder 資料作為多個視覺化圖形的示範資料。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 繪製 gapminder 資料中人均 GDP( gdpPercap 變數)之分佈
gapminder %>%
  ggplot(aes(x = gdpPercap)) +
  geom_histogram(bins = 30)
1
2
3
4
5
6
7
8
9
10
11
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 繪製 gapminder 資料中人均 GDP( gdpPercap 變數)之分佈
## > gapminder %>%
## +     ggplot(aes(x = gdpPercap)) +
## +     geom_histogram(bins = 30)
1
2
3
4
5
6
7
8
9
10
11

探索 gdpPercap 變數的分佈

這段程式會建立出一個 ggplot 物件並綁定 gapmindergdpPercap 變數到這個物件之上,這裡可以觀察到 ggplot2 套件的繪圖來源是資料框,素材則是資料框中的變數,aes() 負責將素材綁定至 X 軸與 Y 軸,這是 aesthetic mappings 的縮寫;接著我們還要告訴 ggplot 物件要用什麼樣的外觀呈現,這時接著利用 geom_() 指定繪圖的類型,這是 geometric objects 的縮寫,底線後面加上繪圖名稱,以現在繪製的直方圖而言,就要使用 geom_histogram() 加上直方圖外觀。在 ggplot 物件上疊加 geometric objects 的運算子是加號 + ,未來如果希望在圖形中添加其他自訂元件,都需要利用加號來連結這些函數呼叫。

直方圖的分箱數(bins)使用分箱數 30 這個預設值,可以透過調整 binwidthbins 參數來更改,增加 binwidth 與減少 bins 會減少分箱數;減少 binwidth 與增加 bins 則會增加分箱數。

在繼續檢視不同外觀的資料視覺化之前,我們先以清單羅列這些 geom_() 藉此獲得清晰的概觀。

  • geom_histogram() :直方圖
  • geom_boxplot() :盒鬚圖
  • geom_line() :線圖
  • geom_point() :散佈圖
  • stat_function(fun, geom = "line") :曲線圖
  • geom_bar() :長條圖

探索不同類別與數值分佈的關係

使用 geom_boxplot() 函數繪製盒鬚圖來探索不同類別與數值分佈的關係,以探索 gapminder 資料中不同洲別( continent 變數)的人均 GDP( gdpPercap 變數)分布差異為例。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 探索不同洲別的人均 GDP 分布差異
gapminder %>%
  ggplot(aes(x = continent, y = gdpPercap)) + 
  geom_boxplot()
1
2
3
4
5
6
7
8
9
10
11
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 探索不同洲別的人均 GDP 分布差異
## > gapminder %>%
## +     ggplot(aes(x = continent, y = gdpPercap)) + 
## +     geom_boxplot()
1
2
3
4
5
6
7
8
9
10
11

探索不同洲別的人均 GDP 分布差異

探索數值與日期(時間)的關係

使用 geom_line() 函數繪製線圖來探索數值與日期(日期時間)的變化趨勢,以探索 gapminder 資料中台灣的人均 GDP( gdpPercap 變數)與年份( year 變數)之變化趨勢為例。

# 安裝 ggplot2, gapminder, dplyr
# install.packages(c("ggplot2", "gapminder", "dplyr"))
# 載入 ggplot2, gapminder, dplyr
library(ggplot2)
library(gapminder)
library(dplyr)

# 探索台灣的人均 GDP 與年份之變化趨勢
gapminder %>%
  filter(country == "Taiwan") %>%
  ggplot(aes(x = year, y = gdpPercap)) + 
  geom_line()
1
2
3
4
5
6
7
8
9
10
11
12
## > # 安裝 ggplot2, gapminder, dplyr
## > # install.packages(c("ggplot2", "gapminder", "dplyr"))
## > # 載入 ggplot2, gapminder, dplyr
## > library(ggplot2)
## > library(gapminder)
## > library(dplyr)
## 
## Attaching package: ‘dplyr’
## 
## The following objects are masked from ‘package:stats’:
## 
##     filter, lag
## 
## The following objects are masked from ‘package:base’:
## 
##     intersect, setdiff, setequal, union
## 
## > 
## > # 探索台灣的人均 GDP 與年份之變化趨勢
## > gapminder %>%
## +     filter(country == "Taiwan") %>%
## +     ggplot(aes(x = year, y = gdpPercap)) + 
## +     geom_line()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

探索人均 GDP 與年份之變化趨勢

探索兩個數值相關的關係

使用 geom_point() 函數繪製散佈圖來探索兩個數值相關的關係,以探索 gapminder 資料中人均 GDP( gdpPercap 變數)與預期壽命( lifeExp 變數)之相關為例。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 探索人均 GDP 與預期壽命之相關
gapminder %>%
  ggplot(aes(x = gdpPercap, y = lifeExp)) + 
  geom_point()
1
2
3
4
5
6
7
8
9
10
11
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 探索人均 GDP 與預期壽命之相關
## > gapminder %>%
## +     ggplot(aes(x = gdpPercap, y = lifeExp)) + 
## +     geom_point()
1
2
3
4
5
6
7
8
9
10
11

探索人均 GDP 與預期壽命之相關

探索類別的頻率

使用 geom_barplot() 函數繪製長條圖來探索類別的頻率,以探索 gapminder 資料中 1704 個觀測值中各大洲的資料筆數為例。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 探索各大洲的資料筆數
gapminder %>%
  ggplot(aes(x = continent)) + 
  geom_bar()
1
2
3
4
5
6
7
8
9
10
11
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 探索各大洲的資料筆數
## > gapminder %>%
## +     ggplot(aes(x = continent)) + 
## +     geom_bar()
1
2
3
4
5
6
7
8
9
10
11

探索 1704 個觀測值中各大洲的資料筆數

繪畫函數

使用 stat_function(fun, geom = "line") 能夠將內建或者自訂的函數外觀繪製出來,但由於函數輸入的值域很廣(可能為負無限大至正無限大),我們需要在資料框中設定輸入的值域,舉例來說可以將 sin() 函數在 -pipi 之間描繪出來。

# 安裝 ggplot2
# install.packages("ggplot2")
# 載入 ggplot2
library(ggplot2)

sin_df <- data.frame(x = c(-pi, pi))
ggplot(sin_df, aes(x = x)) + stat_function(fun = sin, geom = "line")
1
2
3
4
5
6
7
## > # 安裝 ggplot2
## > # install.packages("ggplot2")
## > # 載入 ggplot2
## > library(ggplot2)
## > 
## > sin_df <- data.frame(x = c(-pi, pi))
## > ggplot(sin_df, aes(x = x)) + stat_function(fun = sin, geom = "line")
1
2
3
4
5
6
7

描繪 sin() 函數在 -pi 與 pi 之間的外觀

如果是自訂函數 squared() : f(x) = x² 亦可以用 stat_function(fun, geom = "line") 函數將它在 -3 與 3 之間描繪出來。

# 安裝 ggplot2
# install.packages("ggplot2")
# 載入 ggplot2
library(ggplot2)

squared <- function(x) {
  return(x**2)
}

sqr_df <- data.frame(x = c(-3, 3))
ggplot(sqr_df, aes(x = x)) + stat_function(fun = squared, geom = "line")
1
2
3
4
5
6
7
8
9
10
11
## > # 安裝 ggplot2
## > # install.packages("ggplot2")
## > # 載入 ggplot2
## > library(ggplot2)
## > 
## > squared <- function(x) {
## +     return(x**2)
## + }
## > 
## > sqr_df <- data.frame(x = c(-3, 3))
## > ggplot(sqr_df, aes(x = x)) + stat_function(fun = squared, geom = "line")
1
2
3
4
5
6
7
8
9
10
11

描繪 squared() 函數在 -3 與 3 之間的外觀

ggplot2 常用的自訂元件

我們已經快速地瀏覽過使用 ggplot2 套件繪畫的基礎圖形,有時候可能基於美觀或解釋性,需要在作圖時加入一些自訂元件,ggplot2 圖形常見會調整的部分像是:

  • 自訂標題、X 軸標籤與 Y 軸標籤
  • 隱藏格線
  • 調整圖形為水平方向
  • 在直方圖上加上密度曲線
  • 調整資料點的形狀與顏色
  • 繪畫多個圖形

疊加 ggtitle()xlab()ylab() 這三個函數就能夠分別指定圖形的標題、X 軸標籤與 Y 軸標籤,其中 lab 是 label 的縮寫。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 在直方圖加上標題與標籤
gapminder %>%
  ggplot(aes(x = gdpPercap)) + 
  geom_histogram(bins = 30) +
  ggtitle("GDP Per Capita is left-skewed") +
  xlab("GDP Per Capita") + 
  ylab("Freq")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 在直方圖加上標題與標籤
## > gapminder %>%
## +     ggplot(aes(x = gdpPercap)) + 
## +     geom_histogram(bins = 30) +
## +     ggtitle("GDP Per Capita is left-skewed") +
## +     xlab("GDP Per Capita") + 
## +     ylab("Freq")
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在直方圖加上標題與軸標籤

ggplot2 繪製的圖形預設都有格線,我們可以疊加 theme() 函數進行非常細緻的格線隱藏設定。

  • 隱藏主要格線:panel.grid.major = element_blank()
  • 隱藏次要格線:panel.grid.minor = element_blank()
  • 隱藏 X 軸主要格線:panel.grid.major.x = element_blank()
  • 隱藏 Y 軸主要格線:panel.grid.major.y = element_blank()
  • 隱藏 X 軸次要格線:panel.grid.minor.x = element_blank()
  • 隱藏 Y 軸次要格線:panel.grid.minor.y = element_blank()
# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 隱藏主要與次要格線
gapminder %>%
  ggplot(aes(x = gdpPercap)) + 
  geom_histogram(bins = 30) +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())
1
2
3
4
5
6
7
8
9
10
11
12
13
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 隱藏主要與次要格線
## > gapminder %>%
## +     ggplot(aes(x = gdpPercap)) + 
## +     geom_histogram(bins = 30) +
## +     theme(panel.grid.major = element_blank(),
## +           panel.grid.minor = element_blank())
1
2
3
4
5
6
7
8
9
10
11
12
13

隱藏主要與次要格線

疊加 coord_flip() 函數調整圖形為水平方向,這是 coordinate flip 的縮寫。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 調整圖形為水平方向
gapminder %>%
  ggplot(aes(x = continent)) + 
  geom_bar() +
  coord_flip()
1
2
3
4
5
6
7
8
9
10
11
12
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 調整圖形為水平方向
## > gapminder %>%
## +     ggplot(aes(x = continent)) + 
## +     geom_bar() +
## +     coord_flip()
1
2
3
4
5
6
7
8
9
10
11
12

調整圖形為水平方向

geom_histogram() 中加入 aes(y = ..density..) 以及疊加 geom_density() 可以在直方圖上加上密度曲線。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 在直方圖上加上密度曲線
gapminder %>%
  ggplot(aes(x = gdpPercap)) + 
  geom_histogram(bins = 30, aes(y = ..density..), alpha = 0.5) +
  geom_density()
1
2
3
4
5
6
7
8
9
10
11
12
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 在直方圖上加上密度曲線
## > gapminder %>%
## +     ggplot(aes(x = gdpPercap)) + 
## +     geom_histogram(bins = 30, aes(y = ..density..), alpha = 0.5) +
## +     geom_density()
1
2
3
4
5
6
7
8
9
10
11
12

在直方圖上加上密度曲線

geom_point() 函數中加入 shapecolour 的設定調整資料點的形狀與顏色。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 調整資料點為紅色的空心三角形
gapminder %>%
  ggplot(aes(x = gdpPercap, y = lifeExp)) + 
  geom_point(shape = 2, colour = "red")
1
2
3
4
5
6
7
8
9
10
11
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 調整資料點為紅色的空心三角形
## > gapminder %>%
## +     ggplot(aes(x = gdpPercap, y = lifeExp)) + 
## +     geom_point(shape = 2, colour = "red")
1
2
3
4
5
6
7
8
9
10
11

調整資料點為紅色的空心三角形

使用不同類別區分資料點的形狀與顏色在 ggplot2 可以很容易辦到,將 shapecolour 的對應到指定變數即可。

# 安裝 ggplot2, gapminder, magrittr
# install.packages(c("ggplot2", "gapminder", "magrittr"))
# 載入 ggplot2, gapminder, magrittr
library(ggplot2)
library(gapminder)
library(magrittr)

# 以洲別區分資料點形狀顏色
gapminder %>%
  ggplot(aes(x = gdpPercap, y = lifeExp)) + 
  geom_point(aes(shape = continent, colour = continent))
1
2
3
4
5
6
7
8
9
10
11
## > # 安裝 ggplot2, gapminder, magrittr
## > # install.packages(c("ggplot2", "gapminder", "magrittr"))
## > # 載入 ggplot2, gapminder, magrittr
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > 
## > # 以洲別區分資料點形狀顏色
## > gapminder %>%
## +     ggplot(aes(x = gdpPercap, y = lifeExp)) + 
## +     geom_point(aes(shape = continent, colour = continent))
1
2
3
4
5
6
7
8
9
10
11

以洲別區分資料點形狀顏色

ggplot2 套件本身並沒有針對繪畫多個圖形這部分設計函數,但我們可以利用另一個套件 gridExtra 所提供的 grid.arrange() 函數來達到像是 Base Plotting System 中 par(mfrow = c(m, n)) 所呈現的效果。

# 安裝 ggplot2, gapminder, magrittr, gridExtra
# install.packages(c("ggplot2", "gapminder", "magrittr", "gridExtra"))
# 載入 ggplot2, gapminder, magrittr, gridExtra
library(ggplot2)
library(gapminder)
library(magrittr)
library(gridExtra)

# 垂直的長條圖
g1 <- gapminder %>%
  ggplot(aes(x = continent)) + 
  geom_bar()
# 水平的長條圖
g2 <- gapminder %>%
  ggplot(aes(x = continent)) + 
  geom_bar() +
  coord_flip()
# 水平的長條圖無主要格線
g3 <- gapminder %>%
  ggplot(aes(x = continent)) + 
  geom_bar() + 
  coord_flip() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())
# 垂直的長條圖無主要格線
g4 <- gapminder %>%
  ggplot(aes(x = continent)) + 
  geom_bar() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())
# 繪畫多個圖形於一個畫布上
grid.arrange(g1, g2, g3, g4, nrow = 2, ncol = 2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
## > # 安裝 ggplot2, gapminder, magrittr, gridExtra
## > # install.packages(c("ggplot2", "gapminder", "magrittr", "gridExtra"))
## > # 載入 ggplot2, gapminder, magrittr, gridExtra
## > library(ggplot2)
## > library(gapminder)
## > library(magrittr)
## > library(gridExtra)
## 
## Attaching package: ‘gridExtra’
## 
## The following object is masked from ‘package:dplyr’:
## 
##     combine
## 
## > 
## > # 垂直的長條圖
## > g1 <- gapminder %>%
## +     ggplot(aes(x = continent)) + 
## +     geom_bar()
## > # 水平的長條圖
## > g2 <- gapminder %>%
## +     ggplot(aes(x = continent)) + 
## +     geom_bar() +
## +     coord_flip()
## > # 水平的長條圖無主要格線
## > g3 <- gapminder %>%
## +     ggplot(aes(x = continent)) + 
## +     geom_bar() + 
## +     coord_flip() +
## +     theme(panel.grid.major = element_blank(),
## +           panel.grid.minor = element_blank())
## > # 垂直的長條圖無主要格線
## > g4 <- gapminder %>%
## +     ggplot(aes(x = continent)) + 
## +     geom_bar() +
## +     theme(panel.grid.major = element_blank(),
## +           panel.grid.minor = element_blank())
## > # 繪畫多個圖形於一個畫布上
## > grid.arrange(g1, g2, g3, g4, nrow = 2, ncol = 2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

繪畫多個圖形於一個畫布上

我們雖然已經介紹了一定程度關於 ggplot2 的技巧,但是這些功能都只佔其中非常小的一部分,因為繪圖可以調整的參數與功能實在太豐富了,在有限的章節內容我們只能夠探討探索性分析常用的繪圖需求。實務上在使用繪圖功能時多半會伴隨大量的官方文件查詢、Google 搜尋與 StackOverflow 搜尋,所以如果您在一開始遭遇到很多的困難,這是再正常不過的情形了,請耐住性子,多畫幾次就會漸入佳境的!

小結

在這個小節中我們簡介以 R 語言實踐視覺化的文法 Grammar of Graphics,以 ggplot2 套件作資料視覺化,包含基礎的視覺化圖形與常用的自訂元件。

練習

將一個畫布切割成為 2X2 個區塊,並使用 ggplot2 繪製任意四種圖形。

延伸閱讀