[R語言圖表]用ggplot2畫長條圖 bar chart

4

Last Updated on 2023-10-31

Home » R語言教學 » 資料視覺化 » [R語言圖表]用ggplot2畫長條圖 bar chart

你想畫長條圖(bar chart)嗎?用 ggplot2 帶你畫出好看的長條圖!


想用R語言繪製長條圖,但是遇到問題嗎?長條的順序不受控制、不知道怎麼上色、標籤的中文字全部糊在一起,怎麼感覺R語言沒有想像中好用?

我會在這篇文章介紹如何活用R語言的套件ggplot2,配上實際程式碼,解決繪製長條圖時常會遇到的問題。

長條圖 bar chart 是什麼?

長條圖(bar chart)是一種生活中非常常見的圖表類型,它利用長方形的長度表示數值的大小。長條圖時常被用在比較不同類型事物的數值,例如台灣各個政黨在議會中的席次、台北市公立高中的招生名額、松山區各里的里民人數等。一般來說,事物類型會映射(mapping,指的是想呈現的變量對應到圖表中的表示方法)到 x 軸,事物的數值則會映射到 y 軸。

在 R 語言中,我們可以快速地利用 ggplot2 套件輕鬆簡單的畫出長條圖。不過,在未經修飾時,成品離理想有一段差距,實務上會遇到個問題會有幾個。

  1. 有可能 x 軸的文字太多,導致文字擠在一起,無法清楚讓讀者知道類別。
  2. 當不同類型的長條圖排排站時,高低錯落是正常現象,但繪圖者可能需要讓這些長條按照特定方法排序,例如由高到低或是由低到高
  3. 因為長條圖是以長條的長度為準比較大小,可是畫圖時 y 軸的起點常常不為 0,為了呈現差異反而忽略了長條圖的目的,這時候需要另外調整座標軸
  4. 為了傳遞意念或是方便向讀者溝通,有時候要將長條圖填上顏色,但內建的色調未必符合需求,這也要修正。

在這篇文章中,會分成幾個步驟,帶大家針對上面四個問題:x 軸文字擁擠、長條的排序、y 軸的起始點、長條的顏色,分別處理。我們會用2022年九合一選舉當中台灣各個政黨在議會中取得的席次作為範例,又為了比較,排除國民黨、民進黨、無黨籍的席次,只呈現剩下其他黨派,接著用
ggplot2 的程式碼向大家介紹如何實作。

實作篇

利用 ggplot 畫出第一張長條圖

我們先來匯入資料。從底下的表格可以看到,扣掉大黨與無黨籍之後,這次的九合一選舉當中,還有 11 個政黨拿到席次。

library(tidyverse)
df_viz_council_2022 <- read_csv("../data/ggplot-bar-chart.csv")
df_viz_council_2022
#> # A tibble: 11 × 2
#>    party            n
#>    <chr>        <dbl>
#>  1 台灣民眾黨      14
#>  2 無黨團結聯盟     7
#>  3 時代力量         6
#>  4 台灣團結聯盟     3
#>  5 台灣基進         2
#>  6 親民黨           2
#>  7 勞動黨           1
#>  8 新黨             1
#>  9 正神名黨         1
#> 10 社會民主黨       1
#> 11 綠黨             1

接著,我們利用library(ggplot2)繪製第一張長條圖。這張圖想呈現的是台灣小黨議會席次的差異,政黨映射到 x 軸,席次映射到 y 軸,所以我們在 ggplot() 裡面代表美學(aesthetics)的aes()中放入政黨(party)作為 x 軸以及席次(n)作為 y 軸,另外也將代表長條顏色的 fill 填入政黨。

為了呈現簡單清爽的風格,我們借助 library(ggthemes)
裡面提供的主題 theme_clean()

因為在沒有指定中文字體時,中文字在 mac 電腦中會變成無意義的方框,所以用 element_text() 告訴電腦自己要使用的字體(font),使用的參數為family

延伸閱讀:[R語言資源]在R語言的圖表中顯示中文

至於 legend.position = "none",是隱藏圖例的意思。好了,來看看成果吧!

df_viz_council_2022 %>% 
  ggplot(aes(x = party, y = n, fill = party)) + 
  geom_col(width = 0.75) +
  ggthemes::theme_clean() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 20),
        legend.position = "none")

問題一:x 軸文字擁擠

從上方圖表可以看到先前提過的問題。首先要面對的是 x 軸文字擁擠的問題,兩個字的新黨綠黨、三個字的國民黨民進黨,其實還好,但四個字以上就會和隔壁鄰居重疊。

若是想解決這個問題,我們可以利用 coord_flip()將 x、y 的位置軸翻轉,改成用橫向的方式呈現,其中的 coord_ 代表的是座標系統(coordinate),flip() 顧名思義就是翻轉的意思。如此一來,中文字不會糊成一團,座標軸還是可以映射到原先想查看的變數。

df_viz_council_2022 %>% 
  ggplot(aes(x = party, y = n, fill = party)) + 
  geom_col(width = 0.75) +
  coord_flip() +
  ggthemes::theme_clean() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 20),
        legend.position = "none")

問題二:長條的排序

改變 x、y 軸的位置之後,x 軸的政黨名稱清楚許多,不過圖中的長條參差不齊,當讀者想比較各黨議會席次的時候,會有些吃力。因此,我們要調整
x 軸的排序,讓政黨依照特定的順序排列。

以這張圖來說,因為想要比較席次,所以我們可以按照 y 軸的席次大小,決定 x 軸的順序。在 R 語言裡面處理「順序」的時候,常常會用到因子(factor)的資料型態,它在類別變數的文字以外,額外多上順序的性質。

我們先用 as_factor() 將政黨轉換成因子,接著,再利用 fct_reorder()
,把已經變成因子類型的政黨,按照席次排序。fct_reorder() 函數裡面要放兩個參數,前者是放想要排序的變數,後者則是排序的依據。fct_reorder(party, n) 的意思是將政黨依照席次的大小,由小到大排列,也可以依照需求,修改成 fct_reorder(party, -n) 或是 fct_reorder(party, desc(n)),兩者的意思相同,都是改成由大到小排列。

有人可能會問,既然是由小到大排列,為什麼圖中卻是席次最多的台灣民眾黨排在最前面?原因就在於我們將 x、y 軸翻轉了,才會變成由大到小,若沒有翻轉,就會一如預期地由小到大排列喔。

df_viz_council_2022 %>% 
  mutate(party = as_factor(party)) %>%
  mutate(party = fct_reorder(party, n)) %>%
  ggplot(aes(x = party, y = n, fill = party)) + 
  geom_col(width = 0.75) +
  coord_flip() +
  ggthemes::theme_clean() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 20),
        legend.position = "none")

問題三:y 軸的起始點

上面提過,有些長條圖不會從 0 開始,比較上會有問題。不過,因為小黨的席次相去不遠,所以我們避免了這個問題。但還是在這邊介紹解決方法。

我們可以活用 scale_() 開頭的函數,以現在要應對的 y 軸為例,席次是一個連續性的數值變數,所以我們會用 scale_y_continuous() 函數,裡面可以放入許多參數,現在可以上場的是代表座標軸上下界線的 limits,還有代表座標軸上要顯示的值 breaks,至於 name 很單純,就是想要顯示在座標軸底下的變數名稱。另一個常用的參數是 labels,它可以讓我們指定如何呈現變數的內容,例如政黨中的台灣團結聯盟名字比較長,我們可以用這個參數,呈現台聯兩個字就好。

寫下 y 軸的名稱後,我們也在 scale_x_discrete() 加入了 x 軸的名稱,如此一來又了卻一樁心事。

df_viz_council_2022 %>% 
  mutate(party = as_factor(party)) %>%
  mutate(party = fct_reorder(party, n)) %>%
  ggplot(aes(x = party, y = n, fill = party)) + 
  geom_col(width = 0.75) +
  coord_flip() +
  scale_y_continuous(limits = c(0,15), breaks = seq(0,14,2), name = "席次") +
  scale_x_discrete(name = "政黨") +
  ggthemes::theme_clean() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 20),
        legend.position = "none")

問題四:長條的顏色

最後一個步驟,就是要調整長條的顏色了。我們事先準備了color_party 色票,這是一個向量(vector),左側放上名字,右側放上色碼,它其實就是一個 named vector,裡面的色碼是元素,政黨則是名字,我們可以將它印出看看。

color_party <- c("台灣民眾黨"="#00C3C1","台灣基進"="#A73f24","時代力量"="#F9BE01","社會民主黨"="#FF0088","新黨"="#FFDB00","親民黨"="#FF6310","正神名黨"="#8FD4F4","勞動黨"="#FF000A","綠黨"="#99E64D","無黨團結聯盟"="#C20F51","台灣團結聯盟"="#AB6300")
color_party
#>   台灣民眾黨     台灣基進     時代力量   社會民主黨         新黨       親民黨 
#>    "#00C3C1"    "#A73f24"    "#F9BE01"    "#FF0088"    "#FFDB00"    "#FF6310" 
#>     正神名黨       勞動黨         綠黨 無黨團結聯盟 台灣團結聯盟 
#>    "#8FD4F4"    "#FF000A"    "#99E64D"    "#C20F51"    "#AB6300"

有了政黨色彩後,我們使用 scale_fill_manual() 函數,裡面加上 values 參數,可以指定每個政黨要對應到的顏色,如此一來,便大功告成了!

df_viz_council_2022 %>% 
  mutate(party = as_factor(party)) %>%
  mutate(party = fct_reorder(party, n)) %>%
  ggplot(aes(x = party, y = n, fill = party)) + 
  geom_col(width = 0.75) +
  coord_flip() +
  scale_y_continuous(limits = c(0,15), breaks = seq(0,14,2), name = "席次") +
  scale_x_discrete(name = "政黨") +
  scale_fill_manual(values = color_party) +
  ggthemes::theme_clean() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 20),
        legend.position = "none")

小結

在這篇文章中,我們嘗試畫了一張長條圖(bar chart),依序處理 x 軸文字擁擠、長條的排序、y 軸的起始點、長條的顏色等問題。

當然,這張圖表距離完美還很遙遠,像是圖的大標題、小標題、註解,還有 y 軸的輔助線不見了、 y 軸的起點怎麼往前凸了一小塊,這些都會在之後的文章介紹,希望你喜歡這篇文章,增添對於 ggplot2 的認識,也有學到東西。

4 Comments

  1. Exceplent article. I will be going through
    many of thesse issues as well..

  2. Hi to every body, it’s my first ppay a quick viskt
    of tuis webpage; this webpaage contains remarkiable and acxtually fune mateial designbed ffor visitors.

  3. I may need your help. I’ve been doing research on gate io recently, and I’ve tried a lot of different things. Later, I read your article, and I think your way of writing has given me some innovative ideas, thank you very much.

  4. Thank you for your shening. I am worried that I lack creative ideas. It is your enticle that makes me full of hope. Thank you.

Leave a Reply