深入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的基本概念重新回顾
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容