作者:Guangchuang Yu
原文鏈接:http:///2014/05/11/use-ggplot2/
ggplot2是我見過最human friendly的畫圖軟件,這得益于Leland Wilkinson在他的著作《The Grammar of Graphics》中提出了一套圖形語法,把圖形元素抽象成可以自由組合的成分,Hadley Wickham把這套想法在R中實現(xiàn)。
為什么要學習ggplot2,可以參考ggplot2: 數(shù)據(jù)分析與圖形藝術(shù)的序言(btw: 在序言的最后,我被致謝了)。
Hadley Wickham也給出一堆理由讓我們說服自己,我想再補充一點,Hadley Wickham是學醫(yī)出身的,做為學生物出身的人有什么理由不支持呢:)
ggplot2基本要素
- 數(shù)據(jù)(Data)和映射(Mapping)
- 幾何對象(Geometric)
- 標尺(Scale)
- 統(tǒng)計變換(Statistics)
- 坐標系統(tǒng)(Coordinante)
- 圖層(Layer)
- 分面(Facet)
- 主題(Theme)
這里將從這些基本要素對ggplot2進行介紹。
2、數(shù)據(jù)(Data)和映射(Mapping)
下面以一份鉆石的數(shù)據(jù)為例,這份數(shù)據(jù)非常大,隨機取一個子集來畫圖。
4 | small <- diamonds[ sample ( nrow (diamonds), 1000), ] |
1 | ## carat cut color clarity depth table price x y z |
2 | ## 49345 0.71 Very Good H SI1 62.5 60 2096 5.68 5.75 3.57 |
3 | ## 50545 0.79 Premium H SI1 61.8 59 2275 5.97 5.91 3.67 |
4 | ## 15434 1.03 Ideal F SI1 62.4 57 6178 6.48 6.44 4.03 |
5 | ## 44792 0.50 Ideal E VS2 62.2 54 1624 5.08 5.11 3.17 |
6 | ## 34614 0.27 Ideal E VS1 61.6 56 470 4.14 4.17 2.56 |
7 | ## 27998 0.30 Premium E VS2 61.7 58 658 4.32 4.34 2.67 |
01 | ## carat cut color clarity depth |
02 | ## Min. :0.220 Fair : 28 D:121 SI1 :258 Min. :55.2 |
03 | ## 1st Qu.:0.400 Good : 88 E:186 VS2 :231 1st Qu.:61.0 |
04 | ## Median :0.710 Very Good:227 F:164 SI2 :175 Median :61.8 |
05 | ## Mean :0.819 Premium :257 G:216 VS1 :141 Mean :61.7 |
06 | ## 3rd Qu.:1.070 Ideal :400 H:154 VVS2 : 91 3rd Qu.:62.5 |
07 | ## Max. :2.660 I:106 VVS1 : 67 Max. :72.2 |
10 | ## Min. :50.1 Min. : 342 Min. :3.85 Min. :3.84 |
11 | ## 1st Qu.:56.0 1st Qu.: 990 1st Qu.:4.74 1st Qu.:4.76 |
12 | ## Median :57.0 Median : 2595 Median :5.75 Median :5.78 |
13 | ## Mean :57.4 Mean : 4111 Mean :5.79 Mean :5.79 |
14 | ## 3rd Qu.:59.0 3rd Qu.: 5495 3rd Qu.:6.60 3rd Qu.:6.61 |
15 | ## Max. :65.0 Max. :18795 Max. :8.83 Max. :8.87 |
畫圖實際上是把數(shù)據(jù)中的變量映射到圖形屬性上。以克拉(carat)數(shù)為X軸變量,價格(price)為Y軸變量。
1 | p <- ggplot(data = small, mapping = aes(x = carat, y = price)) |
上面這行代碼把數(shù)據(jù)映射XY坐標軸上,需要告訴ggplot2,這些數(shù)據(jù)要映射成什么樣的幾何對象,下面以散點為例:

幾何對象將在下面的小節(jié)介紹,這一節(jié),關(guān)注的是數(shù)據(jù)和圖形屬性之間的映射。
如果想將切工(cut)映射到形狀屬性。只需要:
1 | p <- ggplot (data=small, mapping= aes (x=carat, y=price, shape=cut)) |

再比如我想將鉆石的顏色(color)映射顏色屬性:
1 | p <- ggplot (data=small, mapping= aes (x=carat, y=price, shape=cut, colour=color)) |

3、幾何對象(Geometric)
在上面的例子中,各種屬性映射由ggplot函數(shù)執(zhí)行,只需要加一個圖層,使用geom_point()告訴ggplot要畫散點,于是所有的屬性都映射到散點上。
geom_point()完成的就是幾何對象的映射,ggplot2提供了各種幾何對象映射,如geom_histogram用于直方圖,geom_bar用于畫柱狀圖,geom_boxplot用于畫箱式圖等等。
不同的幾何對象,要求的屬性會有些不同,這些屬性也可以在幾何對象映射時提供,比如上一圖,也可以用以下語法來畫:
2 | p+ geom_point ( aes (x=carat, y=price, shape=cut, colour=color)) |
ggplot2支持圖層,我通常把不同的圖層中共用的映射提供給ggplot函數(shù),而某一幾何對象才需要的映射參數(shù)提供給geom_xxx函數(shù)。
這一小節(jié)我們來看一下各種常用的幾何對象。
直方圖
直方圖最容易,提供一個x變量,畫出數(shù)據(jù)的分布。
1 | ggplot (small)+ geom_histogram ( aes (x=price)) |

同樣可以根據(jù)另外的變量給它填充顏色,比如按不同的切工:
1 | ggplot (small)+ geom_histogram ( aes (x=price, fill=cut)) |

也可以將其分開,side-by-side地畫直方圖。
1 | ggplot (small)+ geom_histogram ( aes (x=price, fill=cut), position= "dodge" ) |
還可以使用position="fill",按照相對比例來畫。
1 | ggplot (small)+ geom_histogram ( aes (x=price, fill=cut), position= "fill" ) |

柱狀圖
柱狀圖非常適合于畫分類變量。在這里以透明度(clarity)變量為例。按照不同透明度的鉆石的數(shù)目畫柱狀圖。
1 | ggplot (small)+ geom_bar ( aes (x=clarity)) |

柱狀圖兩個要素,一個是分類變量,一個是數(shù)目,也就是柱子的高度。數(shù)目在這里不用提供,因為ggplot2會通過x變量計算各個分類的數(shù)目。
當然你想提供也是可以的,通過stat參數(shù),可以讓geom_bar按指定高度畫圖,比如以下代碼:
1 | ggplot ()+ geom_bar ( aes (x= c ( LETTERS [1:3]),y=1:3), stat= "identity" ) |

柱狀圖和直方圖是很像的,直方圖把連續(xù)型的數(shù)據(jù)按照一個個等長的分區(qū)(bin)來切分,然后計數(shù),畫柱狀圖。而柱狀圖是分類數(shù)據(jù),按類別計數(shù)。我們可以用前面直方圖的參數(shù)來畫side-by-side的柱狀圖,填充顏色或者按比例畫圖,它們是高度一致的。
柱狀圖是用來表示計數(shù)數(shù)據(jù)的,但在生物界卻被經(jīng)常拿來表示均值,加上誤差來表示數(shù)據(jù)分布,這可以通常圖層來實現(xiàn),我將在圖層一節(jié)中給出實例。
密度函數(shù)圖
說到直方圖,就不得不說密度函數(shù)圖,數(shù)據(jù)和映射和直方圖是一樣的,唯一不同的是幾何對象,geom_histogram告訴ggplot要畫直方圖,而geom_density則說我們要畫密度函數(shù)圖,在我們熟悉前面語法的情況下,很容易畫出:
1 | ggplot (small)+ geom_density ( aes (x=price, colour=cut)) |

1 | ggplot (small)+ geom_density ( aes (x=price,fill=clarity)) |

colour參數(shù)指定的是曲線的顏色,而fill是往曲線下面填充顏色。
箱式圖
數(shù)據(jù)量比較大的時候,用直方圖和密度函數(shù)圖是表示數(shù)據(jù)分布的好方法,而在數(shù)據(jù)量較少的時候,比如很多的生物實驗,很多時候大家都是使用柱狀圖+errorbar的形式來表示,不過這種方法的信息量非常低,被Nature Methods吐槽,這種情況推薦使用boxplot。
1 | ggplot (small)+ geom_boxplot ( aes (x=cut, y=price,fill=color)) |
geom_boxplot將數(shù)據(jù)映射到箱式圖上,上面的代碼,我們應(yīng)該很熟悉了,按切工(cut)分類,對價格(price)變量畫箱式圖,再分開按照color變量填充顏色。

ggplot2提供了很多的geom_xxx函數(shù),可以滿足我們對各種圖形繪制的需求。
03 | geom_blank geom_boxplot |
04 | geom_contour geom_crossbar |
05 | geom_density geom_density2d |
06 | geom_dotplot geom_errorbar |
07 | geom_errorbarh geom_freqpoly |
08 | geom_hex geom_histogram |
10 | geom_line geom_linerange |
12 | geom_point geom_pointrange |
13 | geom_polygon geom_quantile |
16 | geom_segment geom_smooth |
4、標尺(Scale)
前面我們已經(jīng)看到了,畫圖就是在做映射,不管是映射到不同的幾何對象上,還是映射各種圖形屬性。這一小節(jié)介紹標尺,在對圖形屬性進行映射之后,使用標尺可以控制這些屬性的顯示方式,比如坐標刻度,可能通過標尺,將坐標進行對數(shù)變換;比如顏色屬性,也可以通過標尺,進行改變。
1 | ggplot (small)+ geom_point ( aes (x=carat, y=price, shape=cut, colour=color))+ scale_y_log10 ()+ scale_colour_manual (values= rainbow (7)) |

以數(shù)據(jù)(Data)和映射(Mapping)一節(jié)中所畫散點圖為例,將Y軸坐標進行l(wèi)og10變換,再自己定義顏色為彩虹色。
5、統(tǒng)計變換(Statistics)
統(tǒng)計變換對原始數(shù)據(jù)進行某種計算,然后在圖上表示出來,例如對散點圖上加一條回歸線。
1 | ggplot (small, aes (x=carat, y=price))+ geom_point ()+ scale_y_log10 ()+ stat_smooth () |

這里就不按顏色、切工來分了,不然ggplot會按不同的分類變量分別做回歸,圖就很亂,如果我們需要這樣做,我們可以使用分面,這個將在后面介紹。
這里,aes所提供的參數(shù),就通過ggplot提供,而不是提供給geom_point,因為ggplot里的參數(shù),相當于全局變量,geom_point()和stat_smooth()都知道x,y的映射,如果只提供給geom_point(),則相當于是局部變量,geom_point知道這種映射,而stat_smooth不知道,當然你再給stat_smooth也提供x,y的映射,不過共用的映射,還是提供給ggplot好。
ggplot2提供了多種統(tǒng)計變換方式:
1 | stat_abline stat_contour stat_identity stat_summary |
2 | stat_bin stat_density stat_qq stat_summary2d |
3 | stat_bin2d stat_density2d stat_quantile stat_summary_hex |
4 | stat_bindot stat_ecdf stat_smooth stat_unique |
5 | stat_binhex stat_function stat_spoke stat_vline |
6 | stat_boxplot stat_hline stat_sum stat_ydensity |
統(tǒng)計變換是非常重要的功能,我們可以自己寫函數(shù),基于原始數(shù)據(jù)做某種計算,并在圖上表現(xiàn)出來,也可以通過它改變geom_xxx函數(shù)畫圖的默認統(tǒng)計參數(shù)。
比如我在Proteomic investigation of the interactome of FMNL1 in hematopoietic cells unveils a role in calcium-dependent membrane plasticity的圖一中,就把boxplot的中位線替換成了平均值來作圖。
6、坐標系統(tǒng)(Coordinante)
坐標系統(tǒng)控制坐標軸,可以進行變換,例如XY軸翻轉(zhuǎn),笛卡爾坐標和極坐標轉(zhuǎn)換,以滿足我們的各種需求。
坐標軸翻轉(zhuǎn)由coord_flip()實現(xiàn)
1 | ggplot (small)+ geom_bar ( aes (x=cut, fill=cut))+ coord_flip () |

而轉(zhuǎn)換成極坐標可以由coord_polar()實現(xiàn):
1 | ggplot (small)+ geom_bar ( aes (x= factor (1), fill=cut))+ coord_polar (theta= "y" ) |

這也是為什么之前介紹常用圖形畫法時沒有提及餅圖的原因,餅圖實際上就是柱狀圖,只不過是使用極坐標而已,柱狀圖的高度,對應(yīng)于餅圖的弧度,餅圖并不推薦,因為人類的眼睛比較弧度的能力比不上比較高度(柱狀圖)
還可以畫靶心圖:
1 | ggplot (small)+ geom_bar ( aes (x= factor (1), fill=cut))+ coord_polar () |

以及風玫瑰圖(windrose)
1 | ggplot (small)+ geom_bar ( aes (x=clarity, fill=cut))+ coord_polar () |

7、圖層(Layer)
photoshop流行的原因在于PS 3.0時引入圖層的概念,ggplot的牛B之處在于使用+號來疊加圖層,這堪稱是泛型編程的典范。
在前面散點圖上,我們已經(jīng)見識過,加上了一個回歸線擬合的圖層。
有了圖層的概念,使用ggplot畫起圖來,就更加得心應(yīng)手。
做為圖層的一個很好的例子是蝙蝠俠logo,batman logo由6個函數(shù)組成,在下面的例子中,我先畫第一個函數(shù),之后再加一個圖層畫第二個函數(shù),不斷重復這一過程,直到六個函數(shù)全部畫好。
07 | x1data.frame (x2=x2, y2=y2) |
08 | p2data.frame (x3=x3, y3=y3) |
09 | p3data.frame (x4=x4,y4=y4) |
10 | p4data.frame (x5=x5,y5=y5) |
11 | p5data.frame (x6=x6,y6=y6) |

下面再以生物界中常用的柱狀圖+誤差圖為實例,展示ggplot2非常靈活的圖層。以我2011年發(fā)表的文章Phosphoproteome profile of human lung cancer cell line A549中的westernblot數(shù)據(jù)為例。
1 | Normaldata.frame (V= c ( "Normal" , "Cancer" ), mean=m, sd=s) |
8、分面(Facet)
分面可以讓我們按照某種給定的條件,對數(shù)據(jù)進行分組,然后分別畫圖。
在統(tǒng)計變換一節(jié)中,提到如果按切工分組作回歸線,顯然圖會很亂,有了分面功能,我們可以分別作圖。
1 | ggplot (small, aes (x=carat, y=price))+ geom_point ( aes (colour=cut))+ scale_y_log10 () + facet_wrap (~cut)+ stat_smooth () |

9、主題(Theme)
通過ggplot畫圖之后,我們可能還需要對圖進行定制,像title, xlab, ylab這些高頻需要用到的,自不用說,ggplot2提供了ggtitle(), xlab()和ylab()來實現(xiàn)。
比如:

但是這個遠遠滿足不了需求,我們需要改變字體,字體大小,坐標軸,背景等各種元素,這需要通過theme()函數(shù)來完成。
ggplot2提供一些已經(jīng)寫好的主題,比如theme_grey()為默認主題,我經(jīng)常用的theme_bw()為白色背景的主題,還有theme_classic()主題,和R的基礎(chǔ)畫圖函數(shù)較像。
別外ggthemes包提供了一些主題可供使用,包括:
1 | theme_economist theme_economist_white |
3 | theme_few theme_foundation |
4 | theme_igray theme_solarized |

在2013年發(fā)表的文章Putative cobalt- and nickel-binding proteins and motifs in Streptococcus pneumoniae中的圖3就是使用theme_stata來畫的。
至于如何改變這些元素,我覺得我之前畫囧字的博文可以做為例子:

詳細的說明,可以參考?theme的幫助文檔。
10、二維密度圖
在這個文檔里,為了作圖方便,我們使用diamonds數(shù)據(jù)集的一個子集,如果使用全集,數(shù)據(jù)量太大,畫出來散點就糊了,這種情況可以使用二維密度力來呈現(xiàn)。
1 | ggplot (diamonds, aes (carat, price))+ stat_density2d ( aes (fill = ..level..), geom= "polygon" )+ scale_fill_continuous (high= 'darkred' ,low= 'darkgreen' ) |

11、ggplot2實戰(zhàn)
果殼知性里有帖子介紹了個猥瑣邪惡的曲線,引來無數(shù)宅男用各種工具來畫圖,甚至于3D動態(tài)圖都出來了。這里用ggplot2來畫。3D版本請猛擊此處。
1 | fdata.frame (x= c (x1,x2,x3), y= rep (y,3), type= rep ( LETTERS [1:3], each= length (y))) |
再來一個蝴蝶圖,詳見《Modern Applied Statistics with S-PLUS》第一章。
1 | theta data.frame (x=radius* sin (theta), y=radius* cos (theta)) |
2 | ggplot (dd, aes (x, y))+ geom_path ()+ theme_null ()+ xlab ( "" )+ ylab ( "" ) |

|