[R語言圖表]用ggplot畫圓餅圖與甜甜圈圖 pie chart and donut chart

0

Last Updated on 2023-10-31

Home » R語言教學 » 資料視覺化 » [R語言圖表]用ggplot畫圓餅圖與甜甜圈圖 pie chart and donut chart

你想畫圓餅圖(pie chart)和甜甜圈圖(donut chart)嗎?用 ggplot2 帶你畫!


想畫圓餅圖(pie chart)和甜甜圈圖(donut chart),但是不知道怎麼在R語言中使用相關函數嗎?什麼時候該用圓餅圖?為什麼很多人建議不要用圓餅圖?怎麼調整甜甜圈圖的寬度?

我會在這篇文章介紹如何活用R語言的套件ggplot2,配上實際程式碼,帶你畫圓餅圖與甜甜圈圖。

圓餅圖 pie chart 和甜甜圈圖 donut chart 是什麼?

圓餅圖和甜甜圈圖的用途?

圓餅圖(pie chart)和甜甜圈圖(donut chart)都用於呈現類別資料的比例分布(distribution)。

圓餅圖是一種生活中非常常見的圓形圖表,將資料分為不同的類別後,再將圓形切割成扇形。

利用扇形區域的面積大小,或者角度張開的幅度,對應每個類別所佔的比例。

甜甜圈圖則是圓餅圖的變體,具有中央的空心區域,因此看起來像甜甜圈。

以甜甜圈圖呈現比例分佈-multiple以甜甜圈圖呈現比例分佈-multiple

這個中央的空心區域可以用來添加額外的資訊或標籤,或者可以單純為了美觀而加入。

兩者的使用場景類似,主要用於呈現分類資料的相對比例。然而,甜甜圈圖相較於圓餅圖,能夠提供一些額外的視覺效果或增加其他資訊的展示,例如在圓環中間添加數值或比例,或展示兩層或多層的分類結構。

為什麼有人說不要用圓餅圖和甜甜圈圖?

不過,如果你有上過一些視覺化課程,可能會聽過老師說,沒事千萬不要亂用圓餅圖

這句話也是在此想要提醒各位的。為什麼?

因為圓餅圖的面積很容易誤導讀者!

舉另一個常見例子作為對比:長條圖。

我們可以從長條圖的長度判斷數值大小。

然而,圓餅圖會看到面積,而面積的尺度是平方項,相對沒有辦法輕易比較,所以我個人沒有很常用到圓餅圖或者甜甜圈圖。

以甜甜圈圖呈現比例分佈

以甜甜圈圖呈現比例分佈

真的逼不得已的時候會使用甜甜圈圖,因為甜甜圈圖的優勢在於,中央空心區域有更多呈現資訊的空間,而且相對在面積上的誤導性較小。

無論選擇甜甜圈圖或者圓餅圖,要記得標示數值或者比例,挑選顏色還有以標籤呈現資訊的時候也要注意,確保清晰度和易讀性。

在 R 語言中可以輕鬆地利用 ggplot2 套件繪製圓餅圖,甜甜圈圖相對不易,但還是可以完成。

底下會一步一步帶大家以R語言實作。

實作篇

資料長相

我們先來匯入一份由公平交易委員會提供、台灣傳銷商人數的類別資料

從底下的表格可以看到,這份資料已經整理好了,只有 1 筆資料。

資料共有 3 個欄位,分別代表沒有領取佣金(other)、有領取佣金的女性(f)、有領取佣金的男性(m)。

library(tidyverse)
df_revenue_people <- 
  tribble(
    ~other,~f,~m,
    266.37, 72.34,26.16
  )
df_revenue_people
#> # A tibble: 1 × 3
#>   other     f     m
#>   <dbl> <dbl> <dbl>
#> 1  266.  72.3  26.2

先從圓餅圖開始

無論是繪製圓餅圖或者甜甜圈圖,都要將資料轉換成長表格。

因此,我們先利用 pivot_longer() 轉換資料。

df_revenue_people %>% 
  rename(`沒有領取佣金` = 1, `有領取佣金之女性` = 2, `有領取佣金之男性` = 3) %>%
  pivot_longer(cols = 1:3, names_to = "people", values_to = "value")
#> # A tibble: 3 × 2
#>   people           value
#>   <chr>            <dbl>
#> 1 沒有領取佣金     266. 
#> 2 有領取佣金之女性  72.3
#> 3 有領取佣金之男性  26.2

下一步,因為圓餅圖呈現的是比例,而非絕對數值,所以我們要額外計算比例。

df_revenue_people %>% 
  rename(`沒有領取佣金` = 1, `有領取佣金之女性` = 2, `有領取佣金之男性` = 3) %>%
  pivot_longer(cols = 1:3, names_to = "people", values_to = "value") %>%
  mutate(per = value/sum(value)) %>% ungroup()
#> # A tibble: 3 × 3
#>   people           value    per
#>   <chr>            <dbl>  <dbl>
#> 1 沒有領取佣金     266.  0.730 
#> 2 有領取佣金之女性  72.3 0.198 
#> 3 有領取佣金之男性  26.2 0.0717

因為繪圖時想調整 people 欄位的順序,所以我們先把它轉成 factor,並且依照 value 排序。

為了在圖表上呈現「萬人」這個額外資訊,我們也額外創建了 value2 欄位。

df_revenue_people %>% 
  rename(`沒有領取佣金` = 1, `有領取佣金之女性` = 2, `有領取佣金之男性` = 3) %>%
  pivot_longer(cols = 1:3, names_to = "people", values_to = "value") %>%
  mutate(per = value/sum(value)) %>% ungroup() %>%
  mutate(people = fct_reorder(as_factor(people), -value)) %>%
  mutate(value2 = str_c(round(value,0), "萬人"))
#> # A tibble: 3 × 4
#>   people           value    per value2 
#>   <fct>            <dbl>  <dbl> <chr>  
#> 1 沒有領取佣金     266.  0.730  266萬人
#> 2 有領取佣金之女性  72.3 0.198  72萬人 
#> 3 有領取佣金之男性  26.2 0.0717 26萬人

下一步就可以開始畫圖了!

我們利用 geom_bar() 為基底,搭配 coord_polar(),就能夠畫出圓餅圖。

你可以想像,原本是從 0% 到 100% 的長條圖,使用 coord_polar() 扭曲座標軸,將它變成 360度,就會變成圓形的長條圖。

df_revenue_people %>% 
  rename(`沒有領取佣金` = 1, `有領取佣金之女性` = 2, `有領取佣金之男性` = 3) %>%
  pivot_longer(cols = 1:3, names_to = "people", values_to = "value") %>%
  mutate(per = value/sum(value)) %>% ungroup() %>%
  mutate(people = fct_reorder(as_factor(people), -value)) %>%
  mutate(value2 = str_c(round(value,0), "萬人")) %>%
  ggplot(aes(x = "", y = value, fill = people)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0)

我們另外指定繪圖時的配色,也加上文字說明。

value_people_color = c("沒有領取佣金"="#FF6347","有領取佣金之女性"="#FFD700","有領取佣金之男性"="#FF8C00")

df_revenue_people %>% 
  rename(`沒有領取佣金` = 1, `有領取佣金之女性` = 2, `有領取佣金之男性` = 3) %>%
  pivot_longer(cols = 1:3, names_to = "people", values_to = "value") %>%
  mutate(per = value/sum(value)) %>% ungroup() %>%
  mutate(people = fct_reorder(as_factor(people), -value)) %>%
  mutate(value2 = str_c(round(value,0), "萬人")) %>%
  ggplot(aes(x = "", y = value, fill = people)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  labs(x= "",y= "", 
       title = "傳銷商人數達 360 萬人,27% 領取佣金或獎金",
       subtitle = "領取佣金與獎金傳銷商人數佔比, 2021",
       caption = "\n資料來源:公平交易委員會",
       fill = "產品類型") +
  scale_fill_manual(values = value_people_color) +
  theme_void() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 14))

現在這張圖有了標題、圖說,也有下方的資料來源補充說明。

不過,想要讓讀者看得更加清楚,可以在圖上標記出數字。

value_people_color = c("沒有領取佣金"="#FF6347","有領取佣金之女性"="#FFD700","有領取佣金之男性"="#FF8C00")

df_revenue_people %>% 
  rename(`沒有領取佣金` = 1, `有領取佣金之女性` = 2, `有領取佣金之男性` = 3) %>%
  pivot_longer(cols = 1:3, names_to = "people", values_to = "value") %>%
  mutate(per = value/sum(value)) %>% ungroup() %>%
  mutate(people = fct_reorder(as_factor(people), -value)) %>%
  mutate(value2 = str_c(round(value,0), "萬人")) %>%
  ggplot(aes(x = "", y = value, fill = people)) +
  geom_bar(stat = "identity", width = 1) +
  geom_text(aes(label = value2),
            position = position_stack(vjust = 0.5), family = "Noto Sans CJK TC Regular", size = 5) +
  coord_polar("y", start = 0) +
  labs(x= "",y= "", 
       title = "傳銷商人數達 360 萬人,27% 領取佣金或獎金",
       subtitle = "領取佣金與獎金傳銷商人數佔比, 2021",
       caption = "\n資料來源:公平交易委員會",
       fill = "產品類型") +
  scale_fill_manual(values = value_people_color) +
  theme_void() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 14))

如此一來,就完成圓餅圖 pie chart 了!

接著來畫甜甜圈圖

想要從圓餅圖,變動成甜甜圈圖,只要修改程式碼的 2 個地方。

首先,請增加一個 x 欄位,並且將原先 ggplot(aes(x = "", y = value)) 當中的 x,修改成新增的 x 欄位。

接著,請活用 xlim(),明確訂出 x 的範圍。

程式碼請看下方:

df_revenue_people %>% 
  rename(`沒有領取佣金` = 1, `有領取佣金之女性` = 2, `有領取佣金之男性` = 3) %>%
  pivot_longer(cols = 1:3, names_to = "people", values_to = "value") %>%
  mutate(per = value/sum(value)) %>% ungroup() %>%
  mutate(people = fct_reorder(as_factor(people), -value)) %>%
  mutate(value2 = str_c(round(value,0), "萬人")) %>%
  mutate(x = 4) %>%
  ggplot(aes(x = x, y = value, fill = people)) +
  geom_bar(stat = "identity", width = 1) +
  geom_text(aes(label = value2),
            position = position_stack(vjust = 0.5), family = "Noto Sans CJK TC Regular", size = 5) +
  coord_polar("y", start = 0) +
  xlim(c(0.2, 4 + 0.5)) +
  labs(x= "",y= "", 
       title = "傳銷商人數達 360 萬人,27% 領取佣金或獎金",
       subtitle = "領取佣金與獎金傳銷商人數佔比, 2021",
       caption = "\n資料來源:公平交易委員會",
       fill = "產品類型") +
  scale_fill_manual(values = value_people_color) +
  theme_void() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 14))

我們來調整 xlim 的範圍,來看會發生什麼事。

把原先 c(0.2, 4 + 0.5) 的值調大看看,可以發現整個甜甜圈都變寬了。

只要利用範圍,就能夠輕鬆控制甜甜圈的厚度了!

df_revenue_people %>% 
  rename(`沒有領取佣金` = 1, `有領取佣金之女性` = 2, `有領取佣金之男性` = 3) %>%
  pivot_longer(cols = 1:3, names_to = "people", values_to = "value") %>%
  mutate(per = value/sum(value)) %>% ungroup() %>%
  mutate(people = fct_reorder(as_factor(people), -value)) %>%
  mutate(value2 = str_c(round(value,0), "萬人")) %>%
  mutate(x = 4) %>%
  ggplot(aes(x = x, y = value, fill = people)) +
  geom_bar(stat = "identity", width = 1) +
  geom_text(aes(label = value2),
            position = position_stack(vjust = 0.5), family = "Noto Sans CJK TC Regular", size = 5) +
  coord_polar("y", start = 0) +
  xlim(c(3, 4 + 0.5)) +
  labs(x= "",y= "", 
       title = "傳銷商人數達 360 萬人,27% 領取佣金或獎金",
       subtitle = "領取佣金與獎金傳銷商人數佔比, 2021",
       caption = "\n資料來源:公平交易委員會",
       fill = "產品類型") +
  scale_fill_manual(values = value_people_color) +
  theme_void() +
  theme(text = element_text(family = "Noto Sans CJK TC Regular", size = 14))

繪製圓餅圖和甜甜圈圖的注意事項

實際繪製後,來盤點一下繪圖的重點。

第一,圓餅圖和甜甜圈圖的資料格式都是長表格,這也是 ggplot2 普遍的偏好。簡單來說,每一列資料,都對應到一個類別。

第二,資料類別不能太多,上面的例子只有 3 種類別,如果超過 6 種,圖表的可讀性就會大幅降低。

第三,注意標籤位置,如果有些類別的比例太小,會導致扇形區域跟著擠在一起,加上標籤時資訊會很難懂,可以考慮調整位置。

第四,確認用途,圓餅圖和甜甜圈圖能夠給你資料相對大小的概念,例如 A 比 B 多,但究竟多了多少?這並非圓餅圖專長,關注實際數字可以用長條圖或其他類型圖表。

第五,調整圖表外觀,這其實是所有圖表都該注意的,上面有用到的步驟包含加上標題、補上標籤、調整顏色,修改圖例位置等,都是畫好圖表的要點。

小結

在這篇文章中,我們嘗試畫了圓餅圖(pie chart)和甜甜圈圖(donut chart)。

它們同樣都可以讓讀者觀察類別資料的比例分布。

實際繪製時要先從調整資料格式開始,接著計算比例,再利用 geom_bar()coord_polar() 的搭配繪圖,並補上標題和圖表相關資訊。

希望你會喜歡這篇文章,也能夠增添對於 ggplot2 的認識,並且學到東西!

No Comments

Leave a Reply