Root branch length in ETE4

42 views
Skip to first unread message

Walker Azam

unread,
Mar 7, 2025, 3:52:46 AMMar 7
to The ETE toolkit
Hi,

I've been using your tools for ETE3 and am super excited about ETE4. 

I've started porting my old code over to ETE4 and noticed that the root branch lengths are too long. I was trying to look through the documentation to find a way to manually make them smaller, but none of the scale factors (ts.scale, ts.scale_length, etc...) seem to help. Is there a way to directly change the root branch line length?

For added context the diversity in the trees are very low (1 or 2 nucleotide differences), which causes the root node to be very long proportionally, and squishes rest of the tree. If there's a way to change proportional length of the branch that would be great. Thanks for you help!

Sincerely,
Walker

Jordi Burguet-Castell

unread,
Mar 7, 2025, 5:40:39 AMMar 7
to The ETE toolkit
Hi Walker,

Glad to hear about the port to ete4! Could you share a small example (tree and code) that shows the issue? I'm not that familiar with the treeview side but can have a look at it.

Also, if you prefer to use the "smartview", which is our preferred way in ete4, you can do it with t.explore() instead (see https://etetoolkit.github.io/ete/tutorial/tutorial_smartview.html if you want).

Cheers,
Jordi

Walker Azam

unread,
Mar 7, 2025, 12:23:43 PMMar 7
to The ETE toolkit
Hi Jordi,

I appreciate you taking a look! I wrote code that helps 'stack' sequences that are clonal using ETE3 -- which other tree making libraries weren't able to support. Here is an example of a small tree made using ETE3:
image.png

I updated script using ETE4 using the same parameters for the same tree newick file resulting in this tree (note: I had to make the scale smaller so rest of the tree would still be visible):
image.png

As you can see, the root node is incredible long in comparison to rest of the tree, making it difficult to view. Below I'll attach the code from ETE3 I used to make the first tree:
```
# Colormap for seq type
seqtype_cmap = {
"Rebound": "#ffa600",
"Control IgG Outgrowth": "#63bfcf",
"Autologous IgG Outgrowth": "#DC3F93",
"No IgG Outgrowth": "#2B488C",
}

# Setting node style
def set_seqtype_color(node):
if "SeqType" in node.features:
nstyle = NodeStyle()
nstyle["fgcolor"] = seqtype_cmap[node.SeqType]
nstyle["size"] = 8
nstyle["hz_line_width"] = 2
node.set_style(nstyle)
def make_branches_bigger(node, new_size):
node.img_style["size"] = 0
node.img_style["hz_line_width"] = new_size # Change the horizotal lines stroke size
node.img_style["vt_line_width"] = new_size # Change the vertical lines stroke size
for node in t.traverse():
set_seqtype_color(node)
for c in node.children:
make_branches_bigger(c, 2)

# plotting entire tree
ts = TreeStyle()
ts.show_leaf_name = False

def custom_layout(node):
"""
This function creates the stacking of clonal sequences
"""
if 'Weight' in node.features:
if node.Weight != -1:
for i in range(node.Weight):
faces.add_face_to_node(faces.CircleFace(4, seqtype_cmap[node.SeqType]),
node, column=i*2+1, position="branch-right")
spot_sum = node.Weight
# Go through the DF of different SeqTypes
if leaf_df.loc[node.name].sum() > 0:
for seq_type_i in df_cols:
range_val = leaf_df.loc[node.name, seq_type_i]
for i in range(range_val):
faces.add_face_to_node(faces.CircleFace(4, seqtype_cmap[seq_type_i]),
node, column=spot_sum*2+1, position="branch-right")
spot_sum += 1
ts.layout_fn = custom_layout

ts.margin_left = 20
ts.margin_right = 20
ts.margin_top = 20

# Setting root node
rootstyle = NodeStyle()
rootstyle["size"] = 3
rootstyle["fgcolor"] = 'black'
rootstyle["shape"] = "square"
rootstyle["vt_line_width"] = 2
rootstyle["hz_line_width"] = 2
t.set_style(rootstyle)

# LEGEND INFORMATION
for key, val in seqtype_cmap.items():
ts.legend.add_face(TextFace(f" {key}", fsize=10), column=1)
ts.legend.add_face(CircleFace(3, val), column=0)
ts.legend_position = 2

# more space between branches
ts.branch_vertical_margin = 2
ts.scale = 10000 # def = 10000

# showing plot
t.render("%%inline", tree_style=ts, w=4, dpi=200, units='in')
```
Here is the same code in ETE4. The only large change is changing ts.scale to a smaller number to fit in view and removing the root node style:
```
# Colormap for seq type
seqtype_cmap = {
"Rebound": "#ffa600",
"Control IgG Outgrowth": "#63bfcf",
"Autologous IgG Outgrowth": "#DC3F93",
"No IgG Outgrowth": "#2B488C",
}


# Setting node style
def set_seqtype_color(node):
if "SeqType" in node.props:
nstyle = NodeStyle()
nstyle["fgcolor"] = seqtype_cmap[node.get_prop('SeqType')]
nstyle["size"] = 8
nstyle["hz_line_width"] = 2
node.set_style(nstyle)
def make_branches_bigger(node, new_size):
node.img_style["size"] = 0
node.img_style["hz_line_width"] = new_size # Change the horizotal lines stroke size
node.img_style["vt_line_width"] = new_size # Change the vertical lines stroke size

for node in t.traverse():
set_seqtype_color(node)
for c in node.children:
make_branches_bigger(c, 2)

# plotting entire tree
ts = TreeStyle()
ts.show_leaf_name = False

def custom_layout(node):
"""
This function creates the stacking of clonal sequences
"""
if 'Weight' in node.props:
if node.get_prop('Weight') != -1:
for i in range(node.get_prop('Weight')):
faces.add_face_to_node(faces.CircleFace(4, seqtype_cmap[node.get_prop('SeqType')]),
node, column=i*2+1, position="branch-right")
spot_sum = node.get_prop('Weight')
# Go through the DF of different SeqTypes
if leaf_df.loc[node.name].sum() > 0:
for seq_type_i in df_cols:
range_val = leaf_df.loc[node.name, seq_type_i]
for i in range(range_val):
faces.add_face_to_node(faces.CircleFace(4, seqtype_cmap[seq_type_i]),
node, column=spot_sum*2+1, position="branch-right")
spot_sum += 1
ts.layout_fn = custom_layout

ts.margin_left = 20
ts.margin_right = 20
ts.margin_top = 20

# Setting root node
rootstyle = NodeStyle()
rootstyle["size"] = 0
rootstyle["fgcolor"] = 'black'
rootstyle["shape"] = "square"
rootstyle["vt_line_width"] = 2
rootstyle["hz_line_width"] = 2
t.set_style(rootstyle)

# LEGEND INFORMATION
for key, val in seqtype_cmap.items():
ts.legend.add_face(TextFace(f" {key}", fsize=10), column=1)
ts.legend.add_face(CircleFace(3, val), column=0)
ts.legend_position = 2

# more space between branches
ts.branch_vertical_margin = 2
# ts.scale_length = 0.0005
# ts.tree_width = 1
ts.scale = 1000 # def = 10000


# showing plot
t.render("%%inline", tree_style=ts, w=10, dpi=300, units='in')
```
I really love being able to create these kind of trees automatically using code, so any help you are able to provide would be fantastic! Thanks!

Sincerely,
Walker 

Jordi Burguet Castell

unread,
Mar 12, 2025, 8:01:27 AMMar 12
to eteto...@googlegroups.com
Hi Walter,

I found the problem and fixed it in the last commit. If you want to update ete4, it will probably work well for you now.

The problem happened because ete4 accepts branches with undefined distance, and doesn't automatically assign one if the user doesn't want it, but while rendering with treeview it was not taking it properly into account.

The way we prefer to visualize and explore trees in ete4 is with smartview instead of treeview (t.explore() instead of t.render()), so we haven't looked at the treeview code in quite a while. If you are interested in using it, this can be handy: https://etetoolkit.github.io/ete/tutorial/tutorial_smartview.html

Cheers,
Jordi

--
You received this message because you are subscribed to the Google Groups "The ETE toolkit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to etetoolkit+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/etetoolkit/e92a105b-8673-436c-8391-574edaf99198n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages