Plotting a Semi-Directed Network#

This tutorial walks through plotting a semi-directed phylogenetic network step by step, using the Xiphophorus swordfish reticulate phylogeny from [Holtgrefe et al., 2025] as a running example. It covers loading a network from eNewick, choosing a layout, customising the style, and saving a publication-ready figure.

Prerequisites: phylozoo[viz] installed (pip install phylozoo[viz]) and Graphviz available (pip install phylozoo[graphviz]).

Loading the Network#

Semi-directed networks can be loaded from an eNewick string directly:

from phylozoo.core.network.sdnetwork import SemiDirectedPhyNetwork

NEWICK = (
    "((Ps.jonesii,((((X.evelynae,(X.xiphidium,(X.maculatus)#H1)),"
    "(((X.meyeri,X.couchianus),X.gordoni),X.milleri)),X.variatus),"
    "((#H1,((((((X.multilineatus,X.nigrensis),(X.pygmaeus,X.continens)),"
    "((X.malinche,X.birchmanni),(X.cortezi)#H2)),(X.montezumae,"
    "(#H2,X.nezahualcoyotl))),((X.signum,(X.hellerii,(X.mayae,"
    "X.alvarezi))))#H3),(#H3,(X.clemenciae,X.monticolus)))),X.andersi))));"
)

net = SemiDirectedPhyNetwork.from_string(NEWICK, format="enewick")
print(net.number_of_nodes(), "nodes,", len(net.taxa), "taxa,", len(net.hybrid_nodes), "hybrid nodes")

Taxon names containing spaces must be replaced with underscores or dots (the parser does not accept quoted names). Back-references like #H1 link the two incoming edges of a hybrid node.

Basic Plot#

The simplest call uses all defaults:

from phylozoo.viz import plot

ax = plot(net)

The default layout for SemiDirectedPhyNetwork is 'neato' (Graphviz spring-embedder).

Hybrid edges are drawn in red with arrowheads pointing from parent to hybrid node. Undirected tree edges are drawn in gray.

Choosing a Layout#

Pass any supported layout name to override the default:

ax = plot(net, layout="fdp")       # force-directed, good for large networks
ax = plot(net, layout="twopi")     # radial, root at centre
ax = plot(net, layout="circo")     # all nodes on a circle
ax = plot(net, layout="spring")    # NetworkX Fruchterman-Reingold (no Graphviz needed)

Graphviz layouts accept extra attributes via the args keyword, which is forwarded directly to the Graphviz program as graph attributes:

# Deterministic neato layout (initial positions from graph topology)
ax = plot(net, layout="neato", args="-Gstart=self")

# Wider ring spacing in twopi
ax = plot(net, layout="twopi", args="-Granksep=2.0")

Customising the Style#

Pass an SDNetStyle instance to control colours, node sizes, and label placement:

from phylozoo.viz.sdnetwork.style import SDNetStyle

style = SDNetStyle(
    node_size=90,           # internal node size
    leaf_size=110,          # leaf node size (overrides node_size for leaves)
    node_color="white",     # internal node fill
    leaf_color="#0a0a0a",   # leaf fill
    hybrid_color="#fcc0bc", # hybrid node fill
    label_offset=0.01,      # distance from node centre to label
    label_font_size=13.2,
    label_rotation=-7,      # slight tilt to reduce label overlap
)
ax = plot(net, layout="neato", style=style, args="-Gstart=self")

Key style attributes:

  • node_size / leaf_size — size of node circles (arbitrary units; 100 ≈ small dot).

  • node_color / leaf_color / hybrid_color — any matplotlib colour string.

  • hybrid_edge_color — colour of hybrid edges (default "red").

  • label_offset — label distance from node centre in layout coordinates.

  • label_font_size — matplotlib font size.

  • label_rotation — rotation angle in degrees (None = auto-align with edge direction; a fixed value such as -7 gives a uniform slight tilt that reduces label overlap in dense regions).

Labels for leaf nodes are automatically placed on the side facing away from the connecting edge.

Saving to File#

Pass an existing axes to embed the plot in a figure, then save:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(10, 7))
plot(net, ax=ax, layout="neato", style=style, args="-Gstart=self")
fig.savefig("xiphophorus.png", dpi=150, bbox_inches="tight", pad_inches=0.05)

Use bbox_inches="tight" to crop whitespace and pad_inches to control the remaining margin. For a publication-quality PDF, replace .png with .pdf.

Result#

The figure below shows the Xiphophorus network with the settings above. Hybrid nodes are highlighted in pink; three reticulation events (H1–H3) are visible as directed (red) edges converging on hybrid nodes.

Semi-directed phylogenetic network of Xiphophorus swordfish species

Semi-directed phylogenetic network of 25 Xiphophorus species with three hybridisation events (H1–H3) [Holtgrefe et al., 2025].#

See Also#