Chapter 8 Bar

Reference for bar charts

長條圖主要用在

  • 比較不同類別/群體間的特徵差異

8.1 兩種長條圖:geom_bar 與 geom_col

長條圖分成兩類:

  • geom_bar(aes(x=...))只有x軸變數;y軸透過計算,如y為統計x變數分群下的統計值(內定是計數(即數每群有多少“個”),並用y軸長條高度代表。

    • x為男/女性:y軸為男/女性毎別資料筆數。
  • geom_col(aes(x=...,y=...))有x軸變數,也指定y軸變數;x代表類別,y軸代表長條高度。

    • x為不同職業,y為平均薪資:y也是搜集來的資料。

8.2 基本範例資料

範例:geom_col
初任人員平均經常性薪資

startSalaryTopCat<- read_csv("https://raw.githubusercontent.com/tpemartin/github-data/master/startSalaryTopCat.csv")

startSalaryTopCat %>% filter(
  str_detect(大職業別,"部門")
) -> dataTwoSectors
dataTwoSectors %>% ggplot(aes(x=大職業別))+
  geom_col(aes(y=`經常性薪資-薪資`))->p1
p1

若改成geom_bar()會變成什麼狀況?

範例:geom_bar
大一問卷資料

classSample<- read_csv("https://raw.githubusercontent.com/tpemartin/github-data/master/classSample.csv")
classSample %>%
  mutate_at(
    vars(-c(3,4)),
    funs(
      as.factor(.)
    )
  ) -> classSample

geom_bar()內訂y軸呈現x各類別樣本個數的計數(count)。

classSample %>% 
  filter(入學年=="107") ->
  freshmen

freshmen %>% 
  ggplot(aes(x=性別))+
  geom_bar()->p2

p2

上述geom_bar()若強行給y軸變數會如何呢?

8.3 labs():aes座標名稱

labs(..., title = waiver(), subtitle = waiver(), caption = waiver(),
  tag = waiver())

Reference

  • 所有的aes mapping元素均可以透過+labs(aesName="名稱")來變更。

  • 若不要有元素座標名稱:+labs(aesName=NULL)

p1+
  labs(x=NULL,y=NULL) -> p1
p1

p2+
  labs(x=NULL,y=NULL)-> p2
p2

8.4 Usage

geom_col(mapping = NULL, data = NULL, position = "stack", ...,
  width = NULL, na.rm = FALSE, show.legend = NA,
  inherit.aes = TRUE)
geom_bar(mapping = NULL, data = NULL, stat = "count",
  position = "stack", ..., width = NULL,
  na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)

8.5 aesthetics: fill (填色)

工業部門與服務業部門的初任薪資差異有多大?

startSalaryTopCat$部門<-"服務業部門"
startSalaryTopCat$部門[2:7]<-"工業部門"
startSalarySecCat <- startSalaryTopCat[-c(1,2,8),]
startSalarySecCat %>% ggplot(aes(x=大職業別))+
  geom_col(aes(y=`經常性薪資-薪資`,fill=部門))+
  theme(
    axis.text.x=
      element_text(
        angle=90,
        hjust=1,
        vjust=0.5)) -> p3
p3

大一男女性比例用不同顏色表示。

8.6 幾何位置(position)

geom_bar(position="XXX")其中XXX有以下選擇:

  • dodge:躲避
  • fill:填滿(標準化成同高度,呈現比重變化用)
  • stack:疊上

8.6.1 stack:堆疊

  • 強調各大類總額差別

  • 及各大類總額的次類組成份子大小

freshmen %>%
  mutate(有課外活動=(本學期目前已參加之課外活動!="無")) ->
  freshmen2

freshmen2 %>%
  ggplot(aes(x=有課外活動))+
  geom_bar(aes(fill=性別)) 

8.6.2 dodge:平行排列

  • 比較各大項內的小項差異

  • 也比較各小項在各大項內的差異

freshmen2 %>%
  ggplot(aes(x=有課外活動))+
  geom_bar(aes(fill=性別),
           position = "dodge"
           ) 

8.6.3 fill:百分比成份拆解

  • 強調組成份子比例的變化。

freshmen2 %>%
  ggplot(aes(x=有課外活動))+
  geom_bar(aes(fill=性別),
           position = "fill"
           )+
  scale_fill_brewer(type="qual")

8.7 類別排序

8.7.1 x mapping

將p3圖依薪資高低畫出(最高在左),要如何做?

  • 透過scale_x_discrete(limits=...)
startSalarySecCat$`經常性薪資-薪資` %>% 
  sort(., decreasing=T,
       index.return=T) -> sortOut
xlim <- startSalarySecCat$大職業別[sortOut$ix]

p3+scale_x_discrete(
  limits=xlim
)->p4
p4

8.7.2 fill mapping

先將性別排序可以改變fill長條呈現順序。

freshmen2 %>% 
  mutate(性別=ordered(性別,
                      levels=c("男","女"))) -> freshmen3

freshmen3 %>%
  ggplot(aes(x=有課外活動))+
  geom_bar(aes(fill=性別),
           position = "dodge"
           )+
  scale_fill_brewer(type="qual")

8.8 aesthetics:weight

內定是由系統來count(計算個數),可以透過weight來更改每筆計算權重。

freshmen2 %>%
  # 依**有課外活動**分群,群內的每筆權重為 1/群內人數
  group_by(有課外活動) %>% 
  mutate(權重=1/n()) -> freshmen4 # n(): 計算群內樣本數

freshmen4 %>% 
  ggplot(aes(x=有課外活動))+
  geom_bar(aes(fill=性別,weight=權重),
           position = "dodge"
           ) 

8.9 轉軸

+coord_flip()

p4+
  coord_flip()

原本的y軸可能需要換到「右側」:

p4+
  scale_y_continuous(position = "right")->p5
p5

原本設定的「由大到小」,變成反的,可透過rev()顛倒順序。

p5+scale_x_discrete(
  limits=rev(xlim) # xlim為先前定義的順序
)+coord_flip()

8.10 間距

與軸線的間距

scale_x/y_...(expand=expand_scale(...))

  • expand_scale(mult=..., add=...) mult: 要以limit往下/上限延伸「1+多少」比例 add: 要以limit往下/上限延伸「多少」單位
p4+scale_y_continuous(
  expand=expand_scale(mult = c(0,0.1))
)

長條粗細

geom_bar/col(width=...)

startSalarySecCat %>% 
  ggplot(aes(x=大職業別))+
  theme(
    axis.text.x=
      element_text(
        angle=90,
        hjust=1,
        vjust=0.5))-> p5base
p5base + geom_col(
    aes(y=`經常性薪資-薪資`,fill=部門),
    width=0.5)-> p7
p7

分類距離

建議用字體大小來控制,之後輸出時再輸成大張的圖即可。

p7 + 
  theme(axis.text.x = element_text(size=5)) ->
  p8
p8

成品

p5base + 
  geom_col( #基本長條圖含寬度設定
    aes(y=`經常性薪資-薪資`,fill=部門),
    width=0.8
    )+
  scale_y_continuous( #去除長條底部留白,並將軸線置右
    expand=expand_scale(mult = c(0,0.10)),
    position="right" 
    )+
  scale_x_discrete(limit=rev(xlim))+ #x分類排序
  theme(axis.text=element_text(size=7))+ #縮小軸線標示文字字體
  coord_flip()+ #翻轉兩軸
  labs(x=NULL,y=NULL,fill=NULL)+ #去除mapping標題
  theme(axis.text.x = #數字調整水平置中 #coord_flip之後的軸用**視覺上的軸**來定義x/y
          element_text(
            angle=0,
            hjust=0.5))-> p9
p9

值得注意的是:

  1. theme()是視覺美化的調整,所以x/y軸的設定是以視覺上看到的為準,因此一但做了coord_flip(),theme裡的x/y指的也會更著改變。

  2. scale_x/y_...()是aes mapping的函數定義調整,它並不是跟著視覺來走,所以即使視覺上做了coord_flip()scale_x/y_所指的x/y並沒有改變。

請進一步將p9改良成如下成品,並存成p10。(圖面標線請用“#adadad”顏色)

請從p5base架構如下成品。(圖面標線請用“#adadad”顏色)

8.11 scale_y_continuous(expand=...)

試著將p1的y軸範圍定在22000到30000元

p1+scale_y_continuous(limits=c(22000,30000))

scale內定對「不在limits範圍」的圖形均「不會畫出來」,以長條圖來說,一個高度為25000的長條,它是0與25000的一對y值來定義,故此長條代表的是0與25000(下限/上限)一對資料,而非只有25000;由於下限0無法被limits包含,所以這個長條不在limits範圍。這些資料稱為out-of-bound data,內定是排除不畫,除非透過oob=...設定。

解決方法
設定oob(out of bounds)圖形處理方式。並使用scales套件

8.11.0.1 scales::rescale_none()

強制保留圖形。

library(scales)
p1+scale_y_continuous(
  limits=c(22000,30000),
  oob=rescale_none)

8.11.0.2 scales::squish()

資料縮到limits範圍。

library(scales)
p1+scale_y_continuous(
  limits=c(22000,30000),
  oob=squish)

squish的圖20000以下沒有長條圖影,rescale_none則有,因為前者把資料縮到範圍內,所以20000以下沒有資料,後者是把資料原封不動保留,所以20000以下還是有長條圖影。

8.12 Daily Charts