14
$\begingroup$

Having recently constructed a lot of phylogenetic trees with the module TreeConstruction from Phylo package from Biopython, I've been asked to replace the branch tip labels by the corresponding sequence logos (which I have in the same folder). I thought that it would be more efficient to make a code to generate the logo-trees automatically, as I would have to make a lot of them.

The first idea that I came up with was to see whether the functions used to build the tree had an argument to replace the branch tip labels or to remove them, which I couldn't find. Therefore, I removed the branch tip labels by setting their font size to 0: (following is the code to build the tree)

# Modules to build the tree
from Bio.Phylo.TreeConstruction import DistanceCalculator, DistanceTreeConstructor
from Bio.Phylo import draw
from Bio import Phylo, AlignIO
import subprocess
import matplotlib
import matplotlib.pyplot as plt

alignment = AlignIO.read('MotifSeqAligned.fasta', 'fasta') # reading the alignment file

calculator = DistanceCalculator('ident')
dm = calculator.get_distance(alignment) # distance matrix

constructor = DistanceTreeConstructor()
tree = constructor.nj(dm) # build with neighbour joining algorithm a tree from dm

Phylo.write(tree, 'TreeToCutOff.nwk', 'newick')

plt.rc('font', size=0)          # controls default text sizes #HERE IS THE SETTING FOR THAT ALLOWS ME TO HIDE THE BRANCH TIP LABELS
plt.rc('axes', titlesize=14)     # fontsize of the axes title
plt.rc('xtick', labelsize=10)    # fontsize of the tick labels
plt.rc('ytick', labelsize=10)    # fontsize of the tick labels
plt.rc('figure', titlesize=18)   # fontsize of the figure title

draw(tree, do_show=False)
plt.savefig("TreeToCutOff.svg", format='svg', dpi=1200)

From this code I could get the tree: enter image description here

Since I don't know how to get the y coordinates of the branches to add the logos one by one, I built a column of logos with matplotlib, that I intended then to paste on the tree in python. The code to build the column of logos is the following:

#Extract filename from newick
newickFile = open("TreeToCutOff.nwk", 'r').read()
orderedLogos = ["{}.eps".format(i) for i in re.split('(\W)', newickFile) if "Profile" in i]


#Initialize the figure
fig = plt.figure()


#Add each image one after the other in the right order
for i, files in enumerate(orderedLogos):
    img1 = mpimg.imread(files)
    ax1 = fig.add_subplot(len(orderedLogos), 1, 1+i)
    ax1.imshow(img1)
    ax1.set_xticks([])
    ax1.set_yticks([])

# plt.show()
plt.savefig("RowsOfLogos.svg", format='svg', dpi=1200)
plt.clf()
plt.cla()

enter image description here

Having my tree and the column of logos in .svg or .png, I couldn't find any way to stack them properly. My first idea was to use the library svgutils which seemed to be easy to handle, with the following code: (taken from svgutils tutorials)

import svgutils.transform as sg
# Assemble
#create new SVG figure
fig = sg.SVGFigure("14cm", "14cm")

# load figures
fig1 = sg.fromfile('TreeToCutOff.svg')
fig2 = sg.fromfile('RowsOfLogos.svg')

# get the plot objects
plot1 = fig1.getroot()
plot2 = fig2.getroot()
plot2.moveto(280, 100, scale=0.05)

# append plots and labels to figure
fig.append([plot1, plot2])

But the issue with the output was that the background of the column of logos was white and thus, I was pasting a huge white image with a thin column of logos on the tree. And I couldn't find a way to crop the column of logos with svgutils. I tried the module Image from PIL package to build a tree of logos from .png files but couldn't see the tree used as background after pasting the column of logos.

There may be a way to do what I'm aiming for with matplotlib (which would be to stack 2 files .png, and place the logos all the time at the same distance), but I couldn't work it out.

Does anyone know what the best solution is to make a tree of logos (as in the following image which I could only build manually with inkscape) with python libraries, allowing to automate the process without having to adapt the code depending on the number of branches?

enter image description here

Following is a subset of "MotifSeqAligned.fasta" containing the aligned sequences used to build the trees:

>ProfileCluster0.meme
---SSNDTTTCCAGGAAD-
>ProfileCluster1.meme
YBNRD---TTCYYGGAAT-
>ProfileCluster10.meme
---VDKDWTTCTYGGAAT-

With the 3 corresponding logos: enter image description here enter image description here enter image description here

The full "MotifSeqAligned.fasta" and all the logos.eps (as I used them instead of .png which is the format asked by the forum) can be found here.

$\endgroup$
3
  • 2
    $\begingroup$ Thank you for putting so much effort into your question! If I could ask for one more thing though, it would be great if you could give us a minimal, complete and verifiable example, code that we can use directly on our system to replicate your issue so we can play with it and see if we can find a solution. $\endgroup$
    – terdon
    May 21, 2018 at 13:59
  • $\begingroup$ Thank you for the suggestion, I added the inputs at the end of the topic. Note that the logos are .eps files and here I added them as images (.png). The full inputs are on the link at the end of the topic. $\endgroup$ May 21, 2018 at 14:53
  • 1
    $\begingroup$ If there's no obvious biopython solution have a look at the ETE Toolkit $\endgroup$ May 21, 2018 at 15:23

1 Answer 1

8
$\begingroup$

To expand on my comment from yesterday. You could do this with the ETE Toolkit (I just copied one logo file rather than converting all 26 to png):

from ete3 import Tree, TreeStyle, faces
def mylayout(node):
    if node.is_leaf():
        logo_face = faces.ImgFace(str.split(node.name, '.')[0] + ".png") # this doesn't seem to work with eps files. You could try other formats
        faces.add_face_to_node(logo_face, node, column=0)
    node.img_style["size"] = 0  # remove blue dots from nodes

t = Tree("tree.nwk", format=3)
ts = TreeStyle()
ts.layout_fn = mylayout
ts.show_leaf_name=False # remove sequence labels
ts.scale =  10000 # rescale branch lengths so they are longer than the width of the logos
t.render("formatted.png", tree_style = ts, h=3000, w=3000) # you may need to fiddle with dimensions and scaling to get the look you want

enter image description here

If you want all of the logos lined up in a column add aligned=True to faces.add_face_to_node

$\endgroup$
1
  • $\begingroup$ Thanks! That's exactly what I was looking for! I will try with all my logos and let you know if I have further issues. $\endgroup$ May 22, 2018 at 11:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.