1. 程式人生 > >ggplot2:初次見面,請多多關照!

ggplot2:初次見面,請多多關照!

作為一枚統計專業的學僧,首先需要掌握的程式語言一定是R。雖然自己對R談不上精通,但卻有著不一樣的熱愛,尤其熱衷於使用R語言繪製各種各樣覺得十分酷炫的圖。每每磕完一個繪圖作品,彷彿過了一個愉快的寒暑假,充實而滿足。而在R語言中,談到繪圖,就不得不聊聊ggplot2這個大神器。

剛開始接觸學習的時候,其實是非常抵觸,覺得非常非常難的,但隨著一張張圖片躍然於螢幕之上,心中的抵觸滿滿消失,取而代之的甚至多出了一絲喜愛。而這絲喜愛愈釀愈醇厚,愈釀愈香甜。這就促使我寫一寫這篇博文。回顧我之前學習ggplot2所走過的路,使大家能夠避免走上我曾經走過的死衚衕,從而走上一條康莊大道。也希望大家能夠愛上R這門程式語言,享受使用ggplot2

繪圖的樂趣。

本文其實借鑑了網上很多的教程與參考文獻,具體這些學習資料的網址,也會在文末進行說明。

ggplot2是什麼?

一句話概括:它是一個用來繪製統計圖形(不只是統計圖形)的R包!

為什麼叫ggplot呢?其中的gg當然不是Good Game的意思,而是Grammar of Graphics,直譯就是繪圖的語法,這是一門學問,繪圖的學問。後面會介紹其與眾不同的語法。

作者是Hadley Wickham,下面我們先瞻仰一下大神,沾點靈氣,為後續的ggplot學習之路打好基礎。


說起這位作者,可就厲害了。他是RStudio公司的首席科學家,在博士期間其實就已經開發出了ggplot

包,後面覺得寫的不行,於是推陳出新,天空一聲巨響,ggplot2閃亮登場。由於這個包實在是太好用了,所以漸漸的,如今Python等多個程式語言都有了其對應的庫。

當然,如果僅僅憑藉ggplot2,Wickham還無法成為RStudio公司的首席科學家,他還是dplyrdevtoolsreadxlrvest等包的作者,是一位全能型大神。

與此同時,在工作之外,他擅長烘焙和調製雞尾酒,並且還有自己的家庭食譜網站。真的非常的膩害!

好了,言歸正傳,我們接著介紹ggplot2

ggplot2能畫出什麼樣的圖?

前面一直在誇它的好,得看看它究竟能創作出什麼樣的圖!(由於部落格的限制,不能使用svg的高清無碼向量圖,故只能使用略帶模糊的jpg圖片)



前面兩個圖都是標準的統計學的我們需要畫的圖,只不過是將箱線圖、柱狀圖、直方圖、散點圖、擬合迴歸以及相關係數,全部融合在了一張圖上,資訊量非常豐富。集百家之所長,成一家之言!但這還沒什麼,ggplot2還能做出令你更加吃驚的統計圖。就比如下面的迴歸樹圖:


還有下面這個像岩漿一樣的族譜圖:從祖先到你,如何薪火相傳。


以及最後一個犯罪率的變化情況。


好看的圖片千千萬,有趣的繪圖包只有ggplot2。這裡只介紹極小的一部分情況,說明一下它能做些什麼。螢幕中的圖,如果不自己生成,就永遠只是別人家的圖,而不是自己的,所以下面我們就來講講如何像搭積木,組裝機器一樣,組裝成一個獨屬於你的妖嬈且風騷的圖片。

組裝機器

ggplot2繪圖三步走:

  • 按照“設計圖紙”
  • 用一個個“零件”
  • 自己進行“組裝”

後面會一一進行介紹,這三步。但其實只要有一個概念,用ggplot2就是像PS一樣,一個圖層一個圖層的疊加,最後組成我們的目標繪圖。

它的優勢非常明顯:

  1. 使用者能在更抽象層面上控制圖形,使創造性繪圖更容易
  2. 採用圖層的設計方式,使其更具靈活性
  3. 圖形美觀,同時避免繁瑣細節
  4. 將常見的統計變換融入到了繪圖中

設計圖紙

想要了解ggplot2,我們必須先了解下面這些概念:

  1. 資料(Data)和對映(Mapping):將資料映到影象
  2. 幾何物件(Geometric):代表在圖中看到的實際元素,如點、線、多邊形等
  3. 統計變換(Statistics):對資料進行某種彙總,如直方圖,或將二維關係用線性模型解釋
  4. 標度(Scale):將資料的取值對映到圖形空間,例如用:顏色、大小、形狀表示不同取值
  5. 座標系(Coordinate):資料如何對映到圖形所在平面,提供作圖所需的座標軸和網格線
  6. 分面(Facet):將資料分解為子集,進行聯合展示
  7. 圖層(Layer):對所需的繪圖操作進行一層一層疊加,最終得到所需圖形

這些概念能幫助你加深對其的理解,但是還是不懂?沒關係!我們剛開始只需要掌握下面的零件構造就好了!

機器的零件

在最最最開始,你需要安裝R並且建議安裝R Studio,都是免費的,並且都需要安裝,然後可以直接從R Studio開啟。

然後我們需要先安裝ggplot2包,在R或者R Studio中使用下述語句,即可完成安裝。

install.packages('ggplot2')

接著,我們就可以學習其中的每個零件的用法了。

零件——散點圖

如果我們想要看兩個變數之間的關係,最簡單,最方便的做法就是繪製散點圖,那我們如何使用ggplot2繪製這樣一個圖形呢?

library(ggplot2)
p <- ggplot(data = mpg, mapping = aes(x = cty, y = hwy))
p + geom_point()

首先需要使用library()ggplot2包進行載入(記得每次重新開啟都需要進行載入!),當然也可以使用require()。然後就可以愉快地進行繪圖了。

我們需要找到一個對映,也就是一對一的對應關係,這個我們在程式中如何給定呢?這就需要使用ggplot()這個函式。

首先在data =後面需要加上資料集的名稱,在例子中是mpg一個關於汽車的資料集(注意:這個資料集是ggplot2包中特有的,載入了包才會有這個資料集)。在這個資料集裡面有很多變數,其中有ctyhwy等一系列變數。 每個變數都有一列數值,也就是樣本的資訊,我們需要看ctyhwy兩個變數對應的資訊,就需要指定x軸是誰,y軸對應的是誰。在程式碼中,首先需要使用aes()將兩個座標軸的資訊對應起來。aes代表的是aesthetic mappings,具有美感的對映,然後在裡面分別使用x =y =來制定需要對映的變數。至此,我們的對應關係就找出來了。

但是!此時,你執行p這個變數是沒有影象資訊反饋出來的,因為你只是指定了關係,而沒有說明應該以什麼形式進行展現。可能是散點、折線、曲線等等。不過由於我們是進行散點圖的繪製,所以使用的是後面還要+ geom_point()就可以展現出散點圖了,其中geom表示的是geometric object,幾何物件的意思。

接著,我們再對散點圖進行一點點潤色。(黑黑的點真的好醜)

變換顏色

p <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(year)))
p + geom_point()

這裡我們指定了其散點的顏色,在aes()中使用了colour =,其後面的year也是mpg資料集中的一個變數。裡面只有1999和2008兩年,每個樣本都有一個年份。(colour =寫在aes()裡面可以自動分配顏色,寫在外面就是強制指定點的顏色,如加上‘blue’等)

不過為了將數字轉化成factor因子形式,我們這裡使用了factor()將數值變成了因子(可以自己嘗試不新增factor()看看會出現什麼樣的結果,其實會出現顏色的深淺表示數值的大小)。

這裡使用了p <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(year))),我們預設將後面這部分賦值給了變數p,後面的p全部都是指代這行語句。

這行程式碼還有另外一個地方發生了變化,data =消失,只是這個都可以省略,只要在對應的位置有資料集這個變數就好了(這裡是mpg)。同理,其實x =y = 在這裡都可以省略。

在出現了散點之後,還是覺得空蕩蕩的,我們再加點別的零件。

擬合曲線

p + geom_point() + stat_smooth()

相比於原先的操作,只是再加了一行 + stat_smooth(),stat表示的是statistical transformation,統計學變換。因為我們需要用插值的方法來繪製擬合直線,並且附帶置信區間。

變換大小

p + geom_point(aes(colour = factor(year), size = displ)) + 
  stat_smooth()   # 排量越大,點越大

想要變換點的大小,只需再在aes()中加上size =,後面接上一個新的變數名稱displ代表著汽車的排量,每個點的尺寸越大,代表著排量越大,其對應關係如右邊圖例所示。

在此基礎上我們還想在一張圖上新增更多的資訊,同時也可以解決點與點之間的重疊的問題,不會因為遮蓋而使得我們有部分點看不到。那麼我們應該怎麼做呢?

透明度!

修改透明度

p + geom_point(aes(colour = factor(year),
                   size = displ), alpha = 0.5) +
  stat_smooth() + scale_size_continuous(range = c(4, 10))

同樣,我們只需使用alpha =即可其範圍在0 — 1之間,越小表示越透明。注意,在這裡,我們是寫在aes()的外面,代表對所有的點都強制透明度為0.5。

在修改完透明度後,我們發現,影象已經有一點夢幻般的感覺了。但仔細一看,會發現,這張圖實在是太擁擠了,1999年與2008年全部擠在了一塊兒,那有沒有什麼簡單的方法可以將兩年直接分開呢?

使用分層操作!

分層

p + geom_point(aes(colour = class, size = displ), alpha = 0.5) +
  stat_smooth() + scale_size_continuous(range = c(4, 10)) +
  facet_wrap(~ year, ncol = 1)

這次變化的內容其實有點多,我們來細細捋一捋,首先是分層。

其實分層操作用起來非常的簡單,就一行程式碼:facet_wrap(~ year, ncol = 1)facet_wrap()是關鍵,facet與wrap兩個詞組合,就是逐面地包起來。裡面我們選擇按照year這個變數來分層,就可以將1999與2008分開。一定要注意!這裡在year前面有個~,迴歸中的用法。而最後的ncol = 1代表著我們的小視窗是1列,指定了1列之後,預設就是兩行啦。(因為年份一共只有兩種)如果不加這句,會預設橫著排列,或者想要指定幾行,則使用nrow = 1

第二個修改的地方是colour = class。同樣的,class也是資料集中的一個變數,代表不同種類的汽車,而因為汽車的種類非常多,所以顏色也就變的很多了。

第三個修改點是:添加了scale_size_continuous(range = c(4, 10)),也就是指定我們size的變化範圍。在本圖中,就是控制點的絕對大小的範圍, 不要太大,也不要太小。

至此,散點圖該潤色的地方,已經潤色的差不多了,唯獨還有一個小小的缺陷:能否加個標題,並且將這些英文簡寫的名稱改得更容易懂一些。畢竟做統計圖不是為了自娛自樂,而是要給別人看的,要讓大家明白你在做什麼,在分析什麼。

所以我們還需要進行一點點修改。

改中文

p + geom_point(aes(colour = class, size = displ), alpha=0.5) +
  stat_smooth() + scale_size_continuous(range = c(4, 10)) +
  facet_wrap(~ year,ncol = 1) +
  labs(y = '每加侖高速公路行駛距離', x = '每加侖城市公路行駛距離',
       title = '汽車油耗與型號', size = '排量', colour = '車型') +
  theme(text = element_text(family = "STHeiti"),
        plot.title = element_text(hjust = 0.5))

看到這麼長的程式碼,首先要做的就是淡定,不要虛!我們慢慢來解釋。

其實我們只添加了兩個地方,labs()theme()

labs()修改的是我們標籤的名稱。我們前面在aes()中用了x =y =size =以及colour =,這些都是會有標籤名稱的,預設都是前面顯示的一些英文。而這裡,我們只需要在labs()中,寫上其對應的名稱即可。另外我們還附送一個title =,就是我們影象的標題。

theme()更偏向于格式的修改。text = element_text(family = "STHeiti")是對字型進行修改,變為黑體。Windows系統的各位,可以不新增這行,一樣會顯示前面labs()中設定的中文。而如果是Mac或者Linux系統,由於字型的缺失,會顯示成一個一個的框框,在影象上顯示不了中文字。

第二行plot.title = element_text(hjust = 0.5)是調整標題的位置,不加這行,標題會居左,加上才會居中。hjust = 0.5其實就是左右移動的意思,0.5表示居中。

零件——直方圖與條形圖

學完了零件——散點圖,我們再來學學其它零件。

我們先著重介紹直方圖與條形圖。可能會有有人問,這兩個不都是方塊兒條嗎,有什麼區別。其實這兩個圖有本質的區別:一個是針對連續的變數(直方圖,histogram),將連續的變數砍成一段一段,再進行計數;一個是針對離散的變數(條形圖,bar),直接對每類的樣本來數數。

直方圖

p1 <- ggplot(mpg, aes(x = hwy))
p1 + geom_histogram()

這裡不再用前面的變數p,而是變成p1,其對映是不需要y的,只需要指定x =資料集中的一個變數即可。然後+ geom_histogram()會使用預設的引數進行直方圖的繪製。

潤色

p1 + geom_histogram(aes(fill = factor(year), y = ..density..),
                    alpha = 0.3, colour = 'black') +
  stat_density(geom = 'line', position = 'identity', size = 1.5,
               aes(colour = factor(year))) +
  facet_wrap(~ year, ncol = 1)

這裡又進行了很多操作,下面將其進行分解。

首先是facet_wrap(~ year, ncol = 1)按年分層,這裡不再詳述。我們來聊聊geom_histogram()中的引數。

fill = factor(year) 放在aes() 中表示按年進行填充顏色。為什麼不用前面說到的colour = ?這是ggplot繪圖中一個非常重要的點!(敲黑板!)對於線與點這種面積為0的結構,它的顏色就直接使用colour =進行指定,而對條形圖,柱狀圖,扇形圖(特殊的柱狀圖),箱線圖等等有面積的圖形,其面積的顏色,我們需要用fill =來指定。

新增y = ..density.. 是將原本的頻數直方圖變成頻率密度直方圖,目的是為了在直方圖上新增密度曲線,兩者可以同時在一張圖上展現。

alpha = 0.3 是指定填充顏色的透明度, colour = 'black'則是強制指定柱狀圖邊框的顏色為黑色。注意這兩個引數是放在aes()外面,就是強制設定的意思。

stat_density()表示新增統計學中的密度曲線,進行密度估計。geom = 'line'指定為線形, position = 'identity'表示一個一一對映, size = 1.5 是強制修改線的尺寸,為原先的1.5倍。最後的aes(colour = factor(year))為修改擬合密度曲線的顏色,這裡就是用的colour =而不是fill =

條形圖

p2 <- ggplot(mpg, aes(x=class))
p2 + geom_bar()

直接簡單地計算不同車種類在資料集中的數量,使用 + geom_bar(),用預設引數即可繪製。我們直接從圖上也可以看出,geom_bar()geom_histogram(),兩者繪製出來的圖形,一個有間隔,一個沒有間隔。這就正好對應了我們的離散與連續的區別。

我們想進一步比較不同年份,兩者的汽車種類的數量是否有差別,我們可以使用並立條形圖。

並立條形圖

p3 <- ggplot(mpg, aes(class, fill = factor(year)))
p3 + geom_bar(position = 'dodge')

只需新增引數position = 'dodge',並立條形圖帶回家!當然前面要說明用什麼變數來並立,這裡是年份:fill = factor(year)

同樣,我們可以做不同的條形圖。

堆疊條形圖

p3 + geom_bar(position = 'stack')

分麵條形圖

p3 + geom_bar(aes(fill = class)) + facet_wrap(~ year)

只需動一動手指,就可享受到買一送三的奢華體驗!

零件——餅圖

餅圖可以說是條形圖的一種特殊形式,其繪製方法可以從程式碼中看出。

p4 <- ggplot(mpg, aes(x = factor(1), fill = factor(class))) +
  geom_bar(width = 1)
p4 + coord_polar(theta = "y")

首先x軸,只有一個柱子,柱子的寬度我們設定為1,geom_bar(width = 1),也就是鋪滿整個圖,而柱子按照class變數來分類,並且是堆疊的(預設是堆疊條形圖)。這時的圖,也就是p4是一個“千層餅”。然後我們按照y軸為原點,用coord_polar(theta = "y"),把這個千層餅捲成千層卷,就成了我們的餅圖了。

後面會專門開個部落格來細講餅圖的一些細節操作。

零件——箱線圖

geom_boxplot()繪製箱線圖。

p5 <- ggplot(mpg, aes(class, hwy, fill = class))
p5 + geom_boxplot()

但是箱線圖怎麼看都覺得不夠炫酷,我們可以用小提琴圖來使繪圖變得更加美麗動人。

零件——小提琴圖

p5 + geom_violin(alpha = 0.3) + geom_jitter(shape = 21)

使用geom_violin()繪製小提琴圖,同樣改變透明度。小提琴圖相比於箱線圖多了各個類別分佈的資訊,是影象變得更加漂亮。

然後我們用geom_jitter()新增擾動點,其實就是將資料點等間隔的排列,顯得更加高大上。其中的shape = 21指定的是擾動點的形狀,我們用21號,也就是空心點(預設是實心點)。

至此,主要的零件都已經介紹完成了。但我們可以看到,ggplot繪製出來的圖,背景都是灰格子,我們可不可以將其去掉呢?

當然可以!

零件打磨

下面的一系列變化都基於前面繪製的箱線圖。我們通過修改theme()中一系列引數,來對背景進行修改。

# 不要灰底
p5 <- ggplot(mpg, aes(class, hwy, fill = class))
p6 <- p5 + geom_boxplot()
p6 + theme_bw()

# 不要網格線
p6 + theme_bw() + theme(panel.grid = element_blank())

# 不要刻度標籤
p6 + theme_bw() + theme(panel.grid = element_blank(),
                        axis.text = element_blank())

# 不要刻度線
p6 + theme_bw() + theme(panel.grid = element_blank(), 
                        axis.text = element_blank(),
                        axis.ticks = element_blank())

# 不要xy軸標題
p6 + theme_bw() + theme(panel.grid = element_blank(), 
                        axis.text = element_blank(),
                        axis.ticks = element_blank(),
                        axis.title = element_blank())

# 不要外層邊框
p6 + theme_bw() + theme(panel.grid = element_blank(), 
                        axis.text = element_blank(),
                        axis.ticks = element_blank(),
                        axis.title = element_blank(),
                        panel.border = element_blank())

# 不要圖例
p7 <- p6 + theme_bw() + theme(panel.grid = element_blank(), 
                              axis.text = element_blank(),
                              axis.ticks = element_blank(),
                              axis.title = element_blank(),
                              panel.border = element_blank(),
                              legend.position = "none")
p7

超級變變變

到這裡還有沒有進一步的提升空間呢?比如定製自己喜歡的配色?當然可以!

這裡我們使用scale_fill_manual()函式,來對箱線圖中填充的顏色進行修改。觀察這個函式,可以發現,由於是箱線圖中填充的顏色,所以函式中間是fill。如果將中間的fill替換成colour,也就是scale_colour_manual(),就可以對點與線的顏色進行定製了!

關於具體的配色,在文末會有推薦的網站,大家只需選中自己喜歡的配色,複製貼上即可。

# 黃棕變
color = c('#ffffd4', '#fee391', '#fec44f', '#fe9929',
          '#ec7014', '#cc4c02', '#8c2d04')
p7 + scale_fill_manual(values = color)

變!

# 紅藍變
color = c('#b2182b', '#ef8a62', '#fddbc7', '#f7f7f7', 
          '#d1e5f0', '#67a9cf', '#2166ac')
p7 + scale_fill_manual(values = color)

變!!

# 清新脫俗變
color = c('#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4',
          '#fed9a6', '#ffffcc', '#e5d8bd')
p7 + scale_fill_manual(values = color)

其他常用零件

最後我們再介紹一些其他常用的零件:

函式 圖形 選項
geom_bar() 條形圖 color, fill, alpha
geom_boxplot() 箱線圖 color, fill, alpha, notch, width
geom_density() 密度圖 color, fill, alpha, linetype
geom_histogram() 直方圖 color, fill, alpha, linetype, binwidth
geom_hline() 水平線 color, alpha, linetype, size
geom_jitter() 抖動點 color, alpha, size, shape
geom_line() 線圖 color, alpha, linetype, size
geom_point() 散點圖 color, alpha, size, shape
geom_rug() 地毯圖 color, sides
geom_smooth() 擬合曲線 method, formula, color, fill, linetype, size
geom_text() 文字註解 … …
geom_violin() 小提琴圖 color, fill, alpha, linetype
geom_vline() 垂線 color, alpha, linetype, size
… … … … … …

實踐出真知!

別人家的男票再帥,也是別人家的。

同理可證,程式碼一定要自己一行一行的敲,並且每次可以自己多試幾種類似的情況,看看圖形會怎麼變。帶著問題去學習,去探索,一定會很快入門ggplot2這個大殺器!

學習資源

參考資料