🏠 重點提要:
這一份程式筆記有兩個學習目標:
  ■ggplot2套件的介紹
  ■ 體驗資料視覺化的威力
ggplot是資料框視覺化的主流工具
  ■ 每一筆資料對應到一個繪圖元件(geom_...),如
      ◇ geom_point : 點狀圖
      ◇ geom_bar, geom_col : 柱狀圖
      ◇ geom_line : 折線圖
  ■ 每一個欄位對應到一個繪圖元件的屬性,
      ◇ 如點的座標、顏色、大小、形狀等等


🌻 ggplots中的基本程式碼元素為 …


載入library裡面的套件

pacman::p_load(dplyr,tidyr,ggplot2,plotly,gridExtra)
theme_set(theme_get() + theme(# set common plotting formats
  text=element_text(size=8), legend.key.size=unit(10,"points")
  ))

1. 一個簡單的例子

在R中繪圖很容易,讓我們使用R的內建數據iris 作為快速入門的例子。

head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
table(iris$Species)

    setosa versicolor  virginica 
        50         50         50 

iris資料框中,有3個不同種類,共150朵鳶尾花。

ggplot(iris, aes(x=Sepal.Width, y=Sepal.Length, color=Species)) + 
  geom_point(size=2, shape=18) + theme_bw()

使用geom_point()iris中的每一朵花都被繪製為一個點,它們的Sepal.WidthSepal.LengthSpecies 分別對應到每個點的x、y坐標和顏色。點的靜態屬性(The static attribute of the points),例如sizeshape,則直接在geom_()中指定。



2. 點狀圖的功能

🌻 探索性分析的主要目的是:

我們再次使用漫畫人物資料集來做例子

D = read.csv("data/comics1.csv",stringsAsFactors=F)
glimpse(D)
Rows: 7,250
Columns: 9
$ publisher   <chr> "dc", "dc", "dc", "dc", "dc", "dc", "dc", "dc", "dc", "dc"~
$ name        <chr> "Batman (Bruce Wayne)", "Superman (Clark Kent)", "Green La~
$ align       <chr> "Good", "Good", "Good", "Good", "Good", "Good", "Good", "G~
$ eye         <chr> "Blue", "Blue", "Brown", "Brown", "Blue", "Blue", "Blue", ~
$ hair        <chr> "Black", "Black", "Brown", "White", "Black", "Black", "Blo~
$ sex         <chr> "Male", "Male", "Male", "Male", "Male", "Female", "Male", ~
$ alive       <chr> "Living", "Living", "Living", "Living", "Living", "Living"~
$ appearances <int> 3093, 2496, 1565, 1316, 1237, 1231, 1121, 1095, 1075, 1028~
$ year        <int> 1939, 1986, 1959, 1987, 1940, 1941, 1941, 1989, 1969, 1956~

先畫一個簡單的點狀圖

ggplot(D, aes(year,appearances)) + geom_point() 

圖看起來很漂亮,但能看到什麼資訊呢?你能說出誰是出場次數最多的角色嗎?讓我們來做一些改善。

🚴 練習2A
逐步加入以下程式並觀察它們的效果…
  ■ 把 color=sex, shape=align 等參數,放入 aes()裡面
  ■ 把 + scale_x_log10() 放在後面
  ■ 把 + facet_wrap(~publisher) 放在後面
圖的資訊含量變大了,但是它是不是變得非常凌亂呢?

🌻 資料視覺化的兩個目標:「資訊含量」和「簡單清楚」常常是互相衝突的
🌻 想要同時滿足這兩項目標,關鍵在於「互動性」



2.1 忍者道場- 互動式圖形
# take 100 most appearing characters from each publisher
gg = D %>% group_by(publisher) %>%     # from each publisher   
  top_n(n=100, wt=appearances) %>%     # pick out 100 most appearing roles
  ggplot(aes(x=year,y=appearances,color=sex,shape=align, label=name)) +    
  scale_y_log10() + 
  facet_wrap(~publisher) +
  geom_point(alpha=0.8) +              # set transparency & size
  theme_bw() +                         # choose a theme for clarity
  theme(text=element_text(size=9)) +   # use smaller font
  labs(title="The Most Appearings",
       x="", y="", color="", shape="") #  set plot and the axis titles 

我們先將圖形物件保存在叫做gg的物件裡,接著我們利用plotly::ggplotly()讓圖形變成可以互動

ggplotly(gg)

🚴 讓我們來體驗一下圖形的互動式吧 …

  • 將鼠標停在標記上以查看工具提示
  • 單擊(或雙擊)上方工具例以選擇特定的功能
  • 在繪圖區域內拖曳想要放大的區塊
  • 單擊上方工具例中的 🏠 圖示即可恢復原狀


2.2 可互動式圖形的威力

在學習繪圖的更細節的語法之前,你應該已經體驗過視覺化的威力了

🌻 互動性是現代資料視覺化的重要功能

  • 縮小時,你可以縱觀整張圖,像是…
    • 每種類型的角色在時間上是如何分佈的
    • yearappearances之間的關係
    • sex, alignpublisher之間的關係是如何變化的
  • 當你放大時,你可以看到細節,像是…
    • 觀察離群值的資料點 和
    • 檢查每個角色的名字和特徵

🌻 直接從圖表中,您可以回答一些複雜的問題,像是…

  • DC 和 marvel最常出現的角色是誰?
  • 誰是最常出現的女性好人角色?
  • 中立角色的出現次數如何隨時間變化?

🌻 回答上述某些問題,過去需要接受認真的統計學訓練。現在,有了互動式圖表,即使你根本沒有學過統計學,也可以直接觀察到答案。

🌻 建立了資料欄位繪圖原件屬性之間的對應關係之後,我們可以在同一張圖上面同時比較高達七個變數,包括:x, y, size, color, shape 和兩個 facet dimensions.



3. 動態與互動式圖表

讓我們在另一個例子中示範互動式圖表的分析能力

👨 🏫 如果我們想調查

以往我們需要建一些複雜的模型才能回答這些研究問題,現在我們可以使用互動圖表中直接回答這些問題。 首先我們需要準備資料,讓我們把時間軸設定在 1980 年至 2010 年,並將這段時間以5年為一個區間

breaks=seq(1980,2010,5)
D2 = filter(D, year>=1980, year<=2010) %>%       # set the time period
  mutate(
    period = cut(year,breaks,breaks[-1],T) %>%   # cut them into 5yr period        
      as.character %>%  # by default cut() returns a factor, but 
      as.integer        # we'd liker to have an integer here 
  )

然後我們計算每種頭髮-眼睛顏色組合的數量,並計算它們的占比和累計占比。

outlooks = count(D2, hair, eye, sort=T) %>%      # count and sort 
  mutate(share=100*n/sum(n), cum=cumsum(share))  # shares and accumulation
head(outlooks, 20)
     hair    eye   n  share   cum
1   Black  Brown 770 15.625 15.62
2   Brown  Brown 563 11.425 27.05
3   Blond   Blue 548 11.120 38.17
4   Black   Blue 351  7.123 45.29
5   Brown   Blue 238  4.830 50.12
6   Black  Black 226  4.586 54.71
7  others   Blue 130  2.638 57.35
8     Red  Green 122  2.476 59.82
9     Red   Blue 119  2.415 62.24
10  White   Blue 112  2.273 64.51
11  Black  Green  77  1.562 66.07
12     No  Green  75  1.522 67.59
13   Bald  Brown  72  1.461 69.05
14  Blond  Green  70  1.420 70.47
15  Brown  Green  68  1.380 71.85
16  Blond  Brown  65  1.319 73.17
17  Black    Red  63  1.278 74.45
18  Black others  61  1.238 75.69
19 others  Green  59  1.197 76.89
20     No    Red  57  1.157 78.04

前 10 名的組合大概覆蓋了三分之二的人口。

資料裝配線: 在下面的程式區塊,我們示範如何運用管線符號(%>%)來建立一條資料裝配線。乍看之下,管線符號(%>%)可能會令人緊張,不過它不是一次就建立完成的,讓我們來介紹如何從頭開始逐步建立流水線吧!

inner_join(D2, outlooks[1:10,]) %>%    # filter for the top 10 outlooks 
  group_by(hair, eye) %>% summarise(   # group by hair and eye
    n = n(),                           # count the no. characters
    female=mean(sex=="Female"),        # the share of female
    bad=mean(align=="Bad"),            # the share of bad guys    
    dead=mean(alive=="Deceased"),      # the casualty ratio
    .groups='drop') %>%                # drop the remaining group
  ggplot(aes(bad, dead)) +             # map x and y coordinates
  geom_point(aes(col=female, size=n), alpha=0.8) +   # map size and color
  scale_color_gradientn(colors=c("seagreen","gold","red")) +  # set color scale
  scale_size_continuous(range=c(3,12)) +             # set size scale          
  geom_text(aes(label=paste(hair,eye,sep="\n")), size=3) # put on a text label
Joining, by = c("eye", "hair")

上面的圖表是有資訊含量的。 但是還不夠好…

動態和互動式圖表可以解決這些問題。 在下面的程式區塊中,我們做了一些修改…

gg = inner_join(D2, outlooks[1:18,-3]) %>% 
  group_by(period, hair, eye) %>% summarise(
    n = n(), bad=mean(align=="Bad"), female=mean(sex=="Female"),
    dead=mean(alive=="Deceased"), .groups='drop') %>% 
  mutate(hair.eye = paste(hair,eye,sep=".")) %>% 
  ggplot(aes(bad, dead, label=hair.eye)) + 
  scale_color_gradientn(colors=c("seagreen","gold","red")) +
  scale_size_continuous(range=c(2,12)) +
  geom_point(aes(col=female, size=n, frame=period), alpha=0.8)
Joining, by = c("eye", "hair")
Warning: Ignoring unknown aesthetics: frame
ggplotly(gg) %>% animation_opts(100)

按左下角的“播放”按鈕,看看會發生什麼。 如果單獨移動滑桿,我們會發現大部分時間、大多數泡泡都聚集在一個區域。但是這些區域的位置、形狀和顏色會隨著時間變化,例如:(a)在2000年期間,我們看到紅色氣泡明顯低於綠色氣泡,這表示男性角色的傷亡率更高;(b)在1990 年,dead似乎與bad呈負相關,這意味著壞角色比好角色傷亡率低。

除了群體現象,我們還可以追踪每個氣泡,看看它的陣營、性別和死亡率是如何隨時間變化的。例如,no-hair.green-eyes大多數情況下是男性和壞人,但它們的數量(泡泡大小)和死亡率在 30 年內發生了很大變化。

從社會學的觀點,上述現象和趨勢都可以算是重要的發現,但傳統的數學模型中很難出這些資訊,這種探索能力正是動態、互動式圖表最重要的價值。