Last Updated on 2023-10-31
你想畫長條圖(bar chart)嗎?用 ggplot2 帶你畫出好看的長條圖!
想用R語言繪製長條圖,但是遇到問題嗎?長條的順序不受控制、不知道怎麼上色、標籤的中文字全部糊在一起,怎麼感覺R語言沒有想像中好用?
我會在這篇文章介紹如何活用R語言的套件ggplot2,配上實際程式碼,解決繪製長條圖時常會遇到的問題。
文章目錄
Toggle長條圖 bar chart 是什麼?
長條圖(bar chart)是一種生活中非常常見的圖表類型,它利用長方形的長度表示數值的大小。長條圖時常被用在比較不同類型事物的數值,例如台灣各個政黨在議會中的席次、台北市公立高中的招生名額、松山區各里的里民人數等。一般來說,事物類型會映射(mapping,指的是想呈現的變量對應到圖表中的表示方法)到 x 軸,事物的數值則會映射到 y 軸。
在 R 語言中,我們可以快速地利用 ggplot2 套件輕鬆簡單的畫出長條圖。不過,在未經修飾時,成品離理想有一段差距,實務上會遇到個問題會有幾個。
- 有可能 x 軸的文字太多,導致文字擠在一起,無法清楚讓讀者知道類別。
- 當不同類型的長條圖排排站時,高低錯落是正常現象,但繪圖者可能需要讓這些長條按照特定方法排序,例如由高到低或是由低到高
- 因為長條圖是以長條的長度為準比較大小,可是畫圖時 y 軸的起點常常不為 0,為了呈現差異反而忽略了長條圖的目的,這時候需要另外調整座標軸
- 為了傳遞意念或是方便向讀者溝通,有時候要將長條圖填上顏色,但內建的色調未必符合需求,這也要修正。
在這篇文章中,會分成幾個步驟,帶大家針對上面四個問題: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 的認識,也有學到東西。
相關
-
[R語言應用] 利用R語言分析YouTube頻道
2024-04-05 0 -
[R語言專題] 利用R語言爬蟲抓取網路資料:觀念篇
2023-12-10 0 -
[R語言專題]用ggplot畫地圖 – 基礎篇
2023-10-26 3
2022-12-22 at 21:56 //
Exceplent article. I will be going through
many of thesse issues as well..
2023-03-03 at 18:10 //
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.
2023-05-23 at 14:42 //
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.
2023-05-26 at 03:23 //
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.