:Author: Jaime Huerta-Cepas .. currentmodule:: ete3 The Programmable Tree Drawing Engine ************************************ .. _drawing: .. contents:: Overview ================= ETE's treeview extension provides a highly programmable drawing system to render any hierarchical tree structure as PDF, SVG or PNG images. Although several predefined visualization layouts are included with the default installation, custom styles can be easily created from scratch. Image customization is performed through four elements: **a)** :class:`TreeStyle`, setting general options about the image (shape, rotation, etc.), **b)** :class:`NodeStyle`, which defines the specific aspect of each node (size, color, background, line type, etc.), **c)** node :class:`faces.Face` which are small pieces of extra graphical information that can be added to nodes (text labels, images, graphs, etc.) **d)** a :attr:`layout` function, a normal python function that controls how node styles and faces are dynamically added to nodes. Images can be rendered as **PNG**, **PDF** or **SVG** files using the :func:`TreeNode.render` method or interactively visualized using a built-in Graphical User Interface (GUI) invoked by the :func:`TreeNode.show` method. Interactive visualization of trees ================================== ETE's tree drawing engine is fully integrated with a built-in graphical user interface (GUI). Thus, ETE allows to visualize trees using an interactive interface that allows to explore and manipulate node's properties and tree topology. To start the visualization of a node (tree or subtree), you can simply call the :func:`TreeNode.show` method. One of the advantages of this on-line GUI visualization is that you can use it to interrupt a given program/analysis, explore the tree, manipulate them, and continuing with the execution thread. Note that **changes made using the GUI will be kept after quiting the GUI**. This feature is specially useful for using during python sessions, in which analyses are performed interactively. The GUI allows many operations to be performed graphically, however it does not implement all the possibilities of the programming toolkit. :: from ete3 import Tree t = Tree( "((a,b),c);" ) t.show() Rendering trees as images ============================= Tree images can be directly written as image files. SVG, PDF and PNG formats are supported. Note that, while PNG images are raster images, PDF and SVG pictures are rendered as `vector graphics `_, thus allowing its later modification and scaling. To generate an image, the :func:`TreeNode.render` method should be used instead of :func:`TreeNode.show`. The only required argument is the file name, whose extension will determine the image format (.PDF, .SVG or .PNG). Several parameters regarding the image size and resolution can be adjusted: .. table:: ================= ============================================================== Argument Description ================= ============================================================== :attr:`units` "**px**": pixels, "**mm**": millimeters, "**in**": inches :attr:`h` height of the image in :attr:`units`. :attr:`w` width of the image in :attr:`units`. :attr:`dpi` dots per inches. ================= ============================================================== .. note:: If :attr:`h` and :attr:`w` values are both provided, image size will be adjusted even if it requires to break the original aspect ratio of the image. If only one value (:attr:`h` or :attr:`w`) is provided, the other will be estimated to maintain aspect ratio. If no sizing values are provided, image will be adjusted to A4 dimensions. :: from ete3 import Tree t = Tree( "((a,b),c);" ) t.render("mytree.png", w=183, units="mm") Customizing the aspect of trees ================================== Image customization is performed through four main elements: Tree style ------------------- The :class:`TreeStyle` class can be used to create a custom set of options that control the general aspect of the tree image. Tree styles can be passed to the :func:`TreeNode.show` and :func:`TreeNode.render` methods. For instance, :class:`TreeStyle` allows to modify the scale used to render tree branches or choose between circular or rectangular tree drawing modes. :: from ete3 import Tree, TreeStyle t = Tree( "((a,b),c);" ) circular_style = TreeStyle() circular_style.mode = "c" # draw tree in circular mode circular_style.scale = 20 t.render("mytree.png", w=183, units="mm", tree_style=circular_style) .. warning:: A number of parameters can be controlled through custom tree style objects, check :class:`TreeStyle` documentation for a complete list of accepted values. Some common uses include: Show leaf node names, branch length and branch support ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: ../ex_figures/show_info.png Automatically adds node names and branch information to the tree image: :: from ete3 import Tree, TreeStyle t = Tree() t.populate(10, random_dist=True) ts = TreeStyle() ts.show_leaf_name = True ts.show_branch_length = True ts.show_branch_support = True t.show(tree_style=ts) Change branch length scale (zoom in X) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: ../ex_figures/scale_x.png Increases the length of the tree by changing the scale: :: from ete3 import Tree, TreeStyle t = Tree() t.populate(10, random_dist=True) ts = TreeStyle() ts.show_leaf_name = True ts.scale = 120 # 120 pixels per branch length unit t.show(tree_style=ts) Change branch separation between nodes (zoom in Y) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: ../ex_figures/scale_y.png Increases the separation between leaf branches: :: from ete3 import Tree, TreeStyle t = Tree() t.populate(10, random_dist=True) ts = TreeStyle() ts.show_leaf_name = True ts.branch_vertical_margin = 10 # 10 pixels between adjacent branches t.show(tree_style=ts) Rotate a tree ^^^^^^^^^^^^^^^ .. figure:: ../ex_figures/rotated_tree.png Draws a rectangular tree from top to bottom: :: from ete3 import Tree, TreeStyle t = Tree() t.populate(10) ts = TreeStyle() ts.show_leaf_name = True ts.rotation = 90 t.show(tree_style=ts) circular tree in 180 degrees ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: ../ex_figures/semi_circular_tree.png Draws a circular tree using a semi-circumference: :: from ete3 import Tree, TreeStyle t = Tree() t.populate(30) ts = TreeStyle() ts.show_leaf_name = True ts.mode = "c" ts.arc_start = -180 # 0 degrees = 3 o'clock ts.arc_span = 180 t.show(tree_style=ts) Add legend and title ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: from ete3 import Tree, TreeStyle, TextFace t = Tree( "((a,b),c);" ) ts = TreeStyle() ts.show_leaf_name = True ts.title.add_face(TextFace("Hello ETE", fsize=20), column=0) t.show(tree_style=ts) Node style ------------------- Through the :class:`NodeStyle` class the aspect of each single node can be controlled, including its size, color, background and branch type. A node style can be defined statically and attached to several nodes: .. figure:: ../ex_figures/node_style_red_nodes.png Simple tree in which the same style is applied to all nodes: :: from ete3 import Tree, NodeStyle, TreeStyle t = Tree( "((a,b),c);" ) # Basic tree style ts = TreeStyle() ts.show_leaf_name = True # Draws nodes as small red spheres of diameter equal to 10 pixels nstyle = NodeStyle() nstyle["shape"] = "sphere" nstyle["size"] = 10 nstyle["fgcolor"] = "darkred" # Gray dashed branch lines nstyle["hz_line_type"] = 1 nstyle["hz_line_color"] = "#cccccc" # Applies the same static style to all nodes in the tree. Note that, # if "nstyle" is modified, changes will affect to all nodes for n in t.traverse(): n.set_style(nstyle) t.show(tree_style=ts) If you want to draw nodes with different styles, an independent :class:`NodeStyle` instance must be created for each node. Note that node styles can be modified at any moment by accessing the :attr:`TreeNode.img_style` attribute. .. figure:: ../ex_figures/node_style_red_and_blue_nodes.png Simple tree in which the different styles are applied to each node: :: from ete3 import Tree, NodeStyle, TreeStyle t = Tree( "((a,b),c);" ) # Basic tree style ts = TreeStyle() ts.show_leaf_name = True # Creates an independent node style for each node, which is # initialized with a red foreground color. for n in t.traverse(): nstyle = NodeStyle() nstyle["fgcolor"] = "red" nstyle["size"] = 15 n.set_style(nstyle) # Let's now modify the aspect of the root node t.img_style["size"] = 30 t.img_style["fgcolor"] = "blue" t.show(tree_style=ts) Static node styles, set through the :func:`set_style` method, will be attached to the nodes and exported as part of their information. For instance, :func:`TreeNode.copy` will replicate all node styles in the replicate tree. Note that node styles can be also modified on the fly through a :attr:`layout` function (see :ref:`sec:layout_functions`) .. _sec:node_faces: Node faces ------------- .. currentmodule:: ete3.treeview.faces Node faces are small pieces of graphical information that can be linked to nodes. For instance, text labels or external images could be linked to nodes and they will be plotted within the tree image. Several types of node faces are provided by the main :mod:`ete3` module, ranging from simple text (:class:`TextFace`) and geometric shapes (:class:`CircleFace`), to molecular sequence representations (:class:`SequenceFace`), heatmaps and profile plots (:class:`ProfileFace`). A complete list of available faces can be found at the :mod:`ete3.treeview` reference page.. Faces position ^^^^^^^^^^^^^^^^ Faces can be added to different areas around the node, namely **branch-right**, **branch-top**, **branch-bottom** or **aligned**. Each area represents a table in which faces can be added through the :func:`TreeNode.add_face` method. For instance, if two text labels want to be drawn bellow the branch line of a given node, a pair of :class:`TextFace` faces can be created and added to the columns 0 and 1 of the **branch-bottom** area: :: from ete3 import Tree, TreeStyle, TextFace t = Tree( "((a,b),c);" ) # Basic tree style ts = TreeStyle() ts.show_leaf_name = True # Add two text faces to different columns t.add_face(TextFace("hola "), column=0, position = "branch-right") t.add_face(TextFace("mundo!"), column=1, position = "branch-right") t.show(tree_style=ts) If you add more than one face to the same area and column, they will be piled up. See the following image as an example of face positions: .. figure:: ../ex_figures/face_positions.png :alt: possible face positions :download:`Source code <../face_grid.py>` used to generate the above image. .. note:: Once a face object is created, it can be linked to one or more nodes. For instance, the same text label can be recycled and added to several nodes. Face properties ^^^^^^^^^^^^^^^^^^ Apart from the specific config values of each face type, all face instances contain same basic attributes that permit to modify general aspects such as margins, background colors, border, etc. A complete list of face attributes can be found in the general :class:`Face` class documentation. Here is a very simple example: .. figure:: ../ex_figures/face_borders.png Basic use of face general attributes :: from ete3 import Tree, TreeStyle, TextFace t = Tree( "(a,b);" ) # Basic tree style ts = TreeStyle() ts.show_leaf_name = True # Creates two faces hola = TextFace("hola") mundo = TextFace("mundo") # Set some attributes hola.margin_top = 10 hola.margin_right = 10 hola.margin_left = 10 hola.margin_bottom = 10 hola.opacity = 0.5 # from 0 to 1 hola.inner_border.width = 1 # 1 pixel border hola.inner_border.type = 1 # dashed line hola.border.width = 1 hola.background.color = "LightGreen" t.add_face(hola, column=0, position = "branch-top") t.add_face(mundo, column=1, position = "branch-bottom") t.show(tree_style=ts) .. _sec:layout_functions: layout functions ------------------- Layout functions act as pre-drawing `hooking functions `_. This means, when a node is about to be drawn, it is first sent to a layout function. Node properties, style and faces can be then modified on the fly and return it to the drawer engine. Thus, layout functions can be understood as a collection of rules controlling how different nodes should be drawn. :: from ete3 import Tree t = Tree( "((((a,b),c), d), e);" ) def abc_layout(node): vowels = set(["a", "e", "i", "o", "u"]) if node.name in vowels: # Note that node style are already initialized with the # default values node.img_style["size"] = 15 node.img_style["color"] = "red" # Basic tree style ts = TreeStyle() ts.show_leaf_name = True # Add two text faces to different columns t.add_face(TextFace("hola "), column=0, position = "branch-right") t.add_face(TextFace("mundo!"), column=1, position = "branch-right") t.show(tree_style=ts) Combining styles, faces and layouts ===================================== Examples are probably the best way to show how ETE works: Fixed node styles ------------------- .. figure:: ../../examples/treeview/node_style.png .. literalinclude:: ../../examples/treeview/node_style.py Node backgrounds ------------------- .. figure:: ../../examples/treeview/node_background.png .. literalinclude:: ../../examples/treeview/node_background.py Img Faces ------------------- .. figure:: ../../examples/treeview/img_faces/img_faces.png Note that images are attached to terminal and internal nodes. .. literalinclude:: ../../examples/treeview/img_faces/img_faces.py Bubble tree maps ------------------- .. figure:: ../../examples/treeview/bubble_map.png .. literalinclude:: ../../examples/treeview/bubble_map.py Trees within trees ------------------- .. figure:: ../../examples/treeview/tree_faces.png .. literalinclude:: ../../examples/treeview/tree_faces.py Phylogenetic trees and sequence domains -------------------------------------------- .. figure:: ../../examples/treeview/seq_motif_faces.png .. literalinclude:: ../../examples/treeview/seq_motif_faces.py Creating your custom interactive Item faces ------------------------------------------- .. figure:: ../../examples/treeview/item_faces.png Note that item faces shown in this image are not static. When the tree is view using the tree.show() method, you can interact with items. .. literalinclude:: ../../examples/treeview/item_faces.py