深入ggtree:ggtree()源碼解讀

GGTREE的快速使用函數就是ggtree,源碼如下,

function (tr, mapping = NULL, layout = "rectangular", open.angle = 0, 
    mrsd = NULL, as.Date = FALSE, yscale = "none", yscale_mapping = NULL, 
    ladderize = TRUE, right = FALSE, branch.length = "branch.length", 
    ndigits = NULL, ...) 
{
    layout %<>% match.arg(c("rectangular", "slanted", "fan", 
        "circular", "radial", "unrooted", "equal_angle", "daylight"))
    if (layout == "unrooted") {
        layout <- "daylight"
        message("\"daylight\" method was used as default layout for unrooted tree.")
    }
    if (is(tr, "r8s") && branch.length == "branch.length") {
        branch.length = "TREE"
    }
    if (yscale != "none") {
        layout <- "slanted"
    }
    if (is.null(mapping)) {
        mapping <- aes_(~x, ~y)
    }
    else {
        mapping <- modifyList(aes_(~x, ~y), mapping)
    }
    p <- ggplot(tr, mapping = mapping, layout = layout, mrsd = mrsd, 
        as.Date = as.Date, yscale = yscale, yscale_mapping = yscale_mapping, 
        ladderize = ladderize, right = right, branch.length = branch.length, 
        ndigits = ndigits, ...)
    if (is(tr, "multiPhylo")) {
        multiPhylo <- TRUE
    }
    else {
        multiPhylo <- FALSE
    }
    p <- p + geom_tree(layout = layout, multiPhylo = multiPhylo, 
        ...)
    p <- p + theme_tree()
    if (layout == "circular" || layout == "radial") {
        p <- layout_circular(p)
        p <- p + ylim(0, NA)
    }
    else if (layout == "fan") {
        p <- layout_fan(p, open.angle)
    }
    class(p) <- c("ggtree", class(p))
    return(p)
}

先有一個大致印象,然后看下面我的解讀。

設置布局

layout %<>% match.arg(c("rectangular", "slanted", "fan", 
    "circular", "radial", "unrooted", "equal_angle", "daylight"))

match.args(arg, choices, several.ok = FALSE): 將傳入的arg與choice的選項進行匹配。比如說match.args("A",c("ABC","BC","DE")), 返回的結果是"ABC"。
'%<>'操作符來自于magrittr包,一種管道操作符,用操作符后跟的函數更新操作符前的數據,比如說

> a <- c(1,4,9,16)
> a %<>% sqrt
> a
1,2,3,4

所以這行的代碼,保證了我們在輸入時可以偷懶,比如說ggtree(tree, 're'),等于是說定義layout為rectangular,也限定了輸入。我只是一般都是條件語句,實現這個功能, 不太優雅。

    if (layout == "unrooted") {
        layout <- "daylight"
        message("\"daylight\" method was used as default layout for unrooted tree.")
    }

這一行表明,'unrooted'的布局和'daylight'(日光?)一樣,下面的做的圖是一樣的,果然像太陽光。message()用于返回提示信息。

library("ggtree")
nwk <- system.file("extdata", "sample.nwk", package="treeio")
tree <- read.tree(nwk)
ggtree(tree, 'da')
ggtree(tree, 'un')
'daylight
if (is(tr, "r8s") && branch.length == "branch.length") {
    branch.length = "TREE"
}

is()判斷tree是不是'r8s'類,且 branch.length是“branch.length',則branch.length賦值為’TREE'。其中r8s文件通過read.r8s輸入,r8s文件的內容長如下樣子。

"node 127 (YGSIV1526_SW_HK_1284_2012) age= 0 | anc 91 () age= 3.6 | dur= 3.6 len= 3 rate= 6.09 nodeReal= 0 age bounds=[0..0]"

if (yscale != "none") {
    layout <- "slanted"
}

判斷yscale是不是為”none",如果隨便輸入字符,結果layout就是“slanted".但是yscale的用途我還沒有找到,還在努力尋找中。

所以ggtree(tree, yscale = "ha")ggtree(tree, layout = 'sl')結果是一致。

slanted

定義映射

    if (is.null(mapping)) {
        mapping <- aes_(~x, ~y)
    }
    else {
        mapping <- modifyList(aes_(~x, ~y), mapping)
    }

這個就涉及到ggplot2的圖形屬性映射。如果沒有特殊說明,就是用aes_()把傳入的tree對象的x和y映射到position(x,y).或者是用modifyList修改之前的mapping。
modifyList設計到一個知識點,R語言的S3類的面向對象編程(OOP).S3類的結構如同堆在一起的卵石。一個類的實例是通過構建一個列表的方式來創建的,這個列表的組件是該類的成員變量。
舉例說明:

> mapping <- aes_(~mpg, ~wt, col= ~cyl)
# 輸出的時候調用了泛型函數,保證輸出美觀
> mapping
* colour -> cyl
* x      -> mpg
* y      -> wt
# 其實真實內容如下
> str(mapping)
List of 3
 $ colour: symbol cyl
 $ x     : symbol mpg
 $ y     : symbol wt

如果我要添加新的映射,如'shape=carb',操作如下

> modifyList(mapping, aes_(shape = ~ carb))
* colour -> cyl
* x      -> mpg
* y      -> wt
* shape  -> carb

而tree里面剛好也有x和y變量,

data.frame(tree)
   node parent length  x         y label isTip branch     angle
1     1     20      4 48  1.000000     A  TRUE   46.0  27.69231
2     2     20      4 48  2.000000     B  TRUE   46.0  55.38462
3     3     19      5 43  3.000000     C  TRUE   40.5  83.07692
....

雖然目前還不知道x和y在read.tree是如何計算出來,正在努力讀源碼。但如果直接用ggplot作圖ggplot(data.frame(tree), aes(x=x,y=y)) + geom_point()能隱約發現slanted的布局的模樣。

ggplot2

生成ggplot實體

p <- ggplot(tr, mapping = mapping, layout = layout, mrsd = mrsd, 
    as.Date = as.Date, yscale = yscale, yscale_mapping = yscale_mapping, 
    ladderize = ladderize, right = right, branch.length = branch.length, 
    ndigits = ndigits, ...)

這個部分就是Y叔為什么說自己的ggtree是完美支持圖形語法的原因。ggplot()輸入參數有一個'...',這里表示可以輸入自己的額外參數,用于后續操作,比如說ggplot(tree, hello="world")中的hello參數可以沒有任何作用。

一些參數調整

首先判斷是否為multiPhylo

    if (is(tr, "multiPhylo")) {
        multiPhylo <- TRUE
    }
    else {
        multiPhylo <- FALSE
    }

這個就是判斷tr是否為multiPhylo.multiphylo {ape}類能用被來操作tree的列表。

在ggplot實體上加上圖層

p <- p + geom_tree(layout = layout, multiPhylo = multiPhylo, 
    ...)
p <- p + theme_tree()
if (layout == "circular" || layout == "radial") {
    p <- layout_circular(p)
    p <- p + ylim(0, NA)
}
else if (layout == "fan") {
    p <- layout_fan(p, open.angle)
}

這里涉及到另外幾個Y叔寫的函數geom_treetheme_treelayout_circularlayout_fan分別用于添加作圖層,修改主題,調整布局,限于篇幅,下次讀。

定義ggtree類

class(p) <- c("ggtree", class(p))

S3類的OOP就是attr()或者class()手動設置。

總結

總結一下這次讀源碼學習到的一些函數,以及回顧到的知識點。

  • message()
  • match.arg()
  • %<>%{magrittr}
  • modifyList()
  • ase_()
  • ...的含義是儲備變量,可以用..1,..2,..n來調用,或者是用變量名。
  • 面向對象編程OOP的基本概念重新回顧
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容