Plotly Tree Graph Not Creating The Root Node

Viewed 790

I'm trying to to show plotly tree graph with strings as nodes. I'm adding the root node on top as to follow the sequence inside the list "l" with "david" as to be the root node. however the graph shows root node to be something else.

Code generating the graph:

import igraph
from igraph import Graph, EdgeSeq
nr_vertices = 25

import plotly.offline as pyo
pyo.init_notebook_mode()


l=[('david','john'),
   ('david','jenni'), 
    ('john','jenni'),
   ('john','david'),
   ('john','mavri'),
   ('john','claire'),
   ]

    
vertices = set()
for line in l:
    vertices.update(line)
vertices = sorted(vertices)

print(len(l))

v_label = list(map(str, range(len(vertices))))
v2 = list(map(str, vertices))
#g = Graph.TupleList(directed=True, edges = l) 
G=Graph()

#G.add_vertices(l)
G.add_vertices(vertices)

# add edges to the graph
G.add_edges(l)


lay = G.layout_reingold_tilford(mode="in", root=0)
print(G)

position = {k: lay[k] for k in range(len(vertices))}

print(position)

Y = [lay[k][1] for k in range(len(vertices))]

M = max(Y)


es = EdgeSeq(G) # sequence of edges
E = [e.tuple for e in G.es] # list of edges
print(E)

L = len(position)

Xn = [position[k][0] for k in range(L)]

Yn = [2*M-position[k][1] for k in range(L)]
Xe = []
Ye = []

for edge in E:
    Xe+=[position[edge[0]][0],position[edge[1]][0], None]
    Ye+=[2*M-position[edge[0]][1],2*M-position[edge[1]][1], None]

labels = v2
print(Xe)

import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(x=Xe,
                   y=Ye,
                   mode='lines',
                   line=dict(color='rgb(210,210,210)', width=1),
                   hoverinfo='none'
                   ))
fig.add_trace(go.Scatter(x=Xn,
                  y=Yn,
                  mode='markers',
                  name='bla',
                  marker=dict(symbol='circle-dot',
                                size=30,
                                color='#6175c1',    #'#DB4551',
                                line=dict(color='rgb(50,50,50)', width=1)
                                ),
                  text=labels,
                  hoverinfo='text',
                  opacity=0.8
                  ))

def make_annotations(pos, text, font_size=10, font_color='rgb(250,250,250)'):
    L=len(pos)
    if len(text)!=L:
        raise ValueError('The lists pos and text must have the same len')
    annotations = []
    for k in range(L):
        annotations.append(
            dict(
                text=labels[k], # or replace labels with a different list for the text within the circle
                x=pos[k][0], y=2*M-position[k][1],
                xref='x1', yref='y1',
                font=dict(color=font_color, size=font_size),
                showarrow=False)
        )
    return annotations

axis = dict(showline=False, # hide axis line, grid, ticklabels and  title
            zeroline=False,
            showgrid=False,
            showticklabels=False,
            )

fig.update_layout(title= 'Tree with Reingold-Tilford Layout',
              annotations=make_annotations(position, v_label),
              font_size=12,
              showlegend=False,
              xaxis=axis,
              yaxis=axis,
              margin=dict(l=40, r=40, b=85, t=100),
              hovermode='closest',
              plot_bgcolor='rgb(248,248,248)'
              )
fig.show()

Tree Output:

enter image description here

1 Answers

I'm not an igraph specialist, so my suggestion may have some flaws, but it will enable you to set 'david' as the root node by simply changing the line:

lay = G.layout_reingold_tilford(mode="in", root=0)

to:

lay = G.layout_reingold_tilford(mode="in", root=1)

This seems to be because G is constructed using:

G=Graph()

#G.add_vertices(l)
G.add_vertices(vertices)

# add edges to the graph
G.add_edges(l)

And vertices is:

['claire', 'david', 'jenni', 'john', 'mavri']

And 0 in lay = G.layout_reingold_tilford(mode="in", root=0) sets 'claire' as the root node. So it turns out that you can set thee root to any element in ['claire', 'david', 'jenni', 'john', 'mavri'] by specifying the corresponding index. So lay = G.layout_reingold_tilford(mode="in", root=1) produces the plot below. And you can verify my findings by changing 1 to something else.

enter image description here

Complete code:

import igraph
from igraph import Graph, EdgeSeq
nr_vertices = 25

import plotly.offline as pyo
pyo.init_notebook_mode()


l=[('david','john'),
   ('david','jenni'), 
    ('john','jenni'),
   ('john','david'),
   ('john','mavri'),
   ('john','claire'),
   ]

    
vertices = set()
for line in l:
    vertices.update(line)
vertices = sorted(vertices)

print(len(l))

v_label = list(map(str, range(len(vertices))))
v2 = list(map(str, vertices))
#g = Graph.TupleList(directed=True, edges = l) 
G=Graph()

#G.add_vertices(l)
G.add_vertices(vertices)

# add edges to the graph
G.add_edges(l)


lay = G.layout_reingold_tilford(mode="in", root=1)
print(G)

position = {k: lay[k] for k in range(len(vertices))}

print(position)

Y = [lay[k][1] for k in range(len(vertices))]

M = max(Y)


es = EdgeSeq(G) # sequence of edges
E = [e.tuple for e in G.es] # list of edges
print(E)

L = len(position)

Xn = [position[k][0] for k in range(L)]

Yn = [2*M-position[k][1] for k in range(L)]
Xe = []
Ye = []

for edge in E:
    Xe+=[position[edge[0]][0],position[edge[1]][0], None]
    Ye+=[2*M-position[edge[0]][1],2*M-position[edge[1]][1], None]

labels = v2
print(Xe)

import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(x=Xe,
                   y=Ye,
                   mode='lines',
                   line=dict(color='rgb(210,210,210)', width=1),
                   hoverinfo='none'
                   ))
fig.add_trace(go.Scatter(x=Xn,
                  y=Yn,
                  mode='markers',
                  name='bla',
                  marker=dict(symbol='circle-dot',
                                size=30,
                                color='#6175c1',    #'#DB4551',
                                line=dict(color='rgb(50,50,50)', width=1)
                                ),
                  text=labels,
                  hoverinfo='text',
                  opacity=0.8
                  ))

def make_annotations(pos, text, font_size=10, font_color='rgb(250,250,250)'):
    L=len(pos)
    if len(text)!=L:
        raise ValueError('The lists pos and text must have the same len')
    annotations = []
    for k in range(L):
        annotations.append(
            dict(
                text=labels[k], # or replace labels with a different list for the text within the circle
                x=pos[k][0], y=2*M-position[k][1],
                xref='x1', yref='y1',
                font=dict(color=font_color, size=font_size),
                showarrow=False)
        )
    return annotations

axis = dict(showline=False, # hide axis line, grid, ticklabels and  title
            zeroline=False,
            showgrid=False,
            showticklabels=False,
            )

fig.update_layout(title= 'Tree with Reingold-Tilford Layout',
              annotations=make_annotations(position, v_label),
              font_size=12,
              showlegend=False,
              xaxis=axis,
              yaxis=axis,
              margin=dict(l=40, r=40, b=85, t=100),
              hovermode='closest',
              plot_bgcolor='rgb(248,248,248)'
              )
fig.show()
Related