Circular layout with edge bundling and labels in graph-tool

瘦欲@ 提交于 2021-02-19 05:39:05

问题


I am very new to graph visualizations and software like graph-tool (gt). My main field is mathematics, but I am somewhat familiar with Python and programming in general. However, I'm not a programmer, so my code may be less than elegant; any suggestions of improvement will be gratefully appreciated.

Description:

I am very fond of the circular layout with edge bundling used to visualize very large graphs. As an example, I am trying to plot the C.Elegans connectome using the wonderful Python module graph-tool by Tiago Peixoto. For this I use the following:

import graph_tool.all as gt
g_celegans = gt.load_graph("c.elegans_neural.male_1.graphml")
v_prop = g_celegans.vertex_properties
celegans_state = gt.minimize_nested_blockmodel_dl(g_celegans)
celegans_state.draw(vertex_text = v_prop['name'], bg_color = 'w')

which produces:

Questions:

  1. How could I (in gt) place the vertex labels on a line drawn from the center? Like this
  2. Can I plot a similar layout, i.e. circular and edge bundling, but using a clustering (partition of the vertices) of my own, instead of the stochastic block model? In a sense, I guess I would just like to be able to use the circular + edge bundling feature on its own. Or am I missing the whole point somewhat?
  3. (Would anyone recommend a good introductory treatment on graph visualizations including this type (connectogram)?)

Attempt at Question 1: So, I managed to at least get somewhere with this:

def getAngle(vec):
    norm_vec = vec / np.linalg.norm(vec)
    one_vec = np.array([1,0])
    dot_product = np.dot(norm_vec, one_vec)
    return np.arccos(dot_product)
    
text_rot = [0]*len(list(text_pos))
for i, p in enumerate(text_pos):
    if p[0]>=0 and p[1]>=0:
        text_rot[i] = getAngle(p)
    elif p[0]>=0 and p[1]<0:
        text_rot[i] = -getAngle(p)
    elif p[0]<0 and p[1]>=0:
        text_rot[i] = getAngle(p)-np.pi
    elif p[0]<0 and p[1]<0:
        text_rot[i] = -getAngle(p)+np.pi

text_rot = np.asarray(text_rot)
t_rot = g_celegans.new_property('v','float', vals = text_rot)

options = {'pos': pos,
           'vertex_text': v_prop['name'],
           'vertex_text_rotation':t_rot,
           'bg_color': 'w',
           'vertex_shape': 'none',
           'vertex_font_size': 5,
           'edge_end_marker': 'none'
          }

celegans_state.draw(**options)

which produces:

So, the rotation is fine, but I would like to offset the labels a bit further out. Now they're in the center of an invisible vertex. There are two vertex properties called 'text_position' and 'text_offset', which you may read about here.

Now, any value for 'vertex_text_position', such as -1 or 'centered' or if I pass a VertexPropertyMap object like for 'vertex_text_rotation' above, generates an IndexError:

    ---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-197-d529fcf5647e> in <module>
      9           }
     10 
---> 11 celegans_state.draw(**options)

~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/inference/nested_blockmodel.py in draw(self, **kwargs)
    986         draws the hierarchical state."""
    987         import graph_tool.draw
--> 988         return graph_tool.draw.draw_hierarchy(self, **kwargs)
    989 
    990 

~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in draw_hierarchy(state, pos, layout, beta, node_weight, vprops, eprops, hvprops, heprops, subsample_edges, rel_order, deg_size, vsize_scale, hsize_scale, hshortcuts, hide, bip_aspect, empty_branches, **kwargs)
   2121             kwargs[k] = u.own_property(v.copy())
   2122 
-> 2123     pos = graph_draw(u, pos, vprops=t_vprops, eprops=t_eprops, vorder=tvorder,
   2124                      **kwargs)
   2125 

~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in graph_draw(g, pos, vprops, eprops, vorder, eorder, nodesfirst, output_size, fit_view, fit_view_ink, adjust_aspect, ink_scale, inline, inline_scale, mplfig, output, fmt, bg_color, **kwargs)
   1074                       vprops.get("fill_color", _vdefaults["fill_color"]),
   1075                       vcmap)
-> 1076         vprops["text_color"] = auto_colors(g, bg,
   1077                                            vprops.get("text_position",
   1078                                                       _vdefaults["text_position"]),

~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in auto_colors(g, bg, pos, back)
    724             return color_contrast(back)
    725     c = g.new_vertex_property("vector<double>")
--> 726     map_property_values(bgc_pos, c, conv)
    727     return c
    728 

~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/__init__.py in map_property_values(src_prop, tgt_prop, map_func)
   1189     u = GraphView(g, directed=True, reversed=g.is_reversed(),
   1190                   skip_properties=True)
-> 1191     libcore.property_map_values(u._Graph__graph,
   1192                                 _prop(k, g, src_prop),
   1193                                 _prop(k, g, tgt_prop),

~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in conv(x)
    722             return color_contrast(bgc)
    723         else:
--> 724             return color_contrast(back)
    725     c = g.new_vertex_property("vector<double>")
    726     map_property_values(bgc_pos, c, conv)

~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in color_contrast(color)
    694 def color_contrast(color):
    695     c = np.asarray(color)
--> 696     y = c[0] * .299 + c[1] * .587 + c[2] * .114
    697     if y < .5:
    698         c[:3] = 1

IndexError: too many indices for array: array is 0-dimensional, but 1 were indexed
  • If I do 'vertex_text_offset = pos', this would mean that I offset each vertex by its own coordinates, and I could then just scale by say 0.1 to get them appropriately far out, which actually DID work great without rotation. Then I rotated the text, which yielded this (without scaling):

The problem seems to be that the center for rotation is the center of the vertex, which is not ideal if the text is moved out from the vertex. So, even if 'vertex_text_position' above would have worked, I'm guessing the rotation would have messed that up as well.

  • Weirdly enough, If I rotate the vertices using 'vertex_rotation' instead, the labels are rotated along with them (great!), but when I offset the text with vertex position (which should "push" outwards), I get the same faulty plot as above.

  • Next I tried 'vertex_shape = circle', and filling the vertices with white using 'vertex_fill_color = 'w''. Thus I would push the text out a bit from the edge by increasing the size of the vertex. For some reason this made all the edges of the graph white as well; so no colors at all in the plot. I guess the edges are thus colored based on the vertex colors.

What I ended up doing is to use the vertex properties 'text_out_color' and 'text_out_width', with a width of 0.003. This gives a nice bold style to the text, which makes it more readable against the colored background.

But, now I'm pretty much out of ideas.

Do anyone know a solution to my problem? i.e. placing the labels like I have them, but moving them further out (in the direction outwards from the center) or framing them in white so that they're more readable, but so that the plot still looks nice; as well as to question 2 above.

来源:https://stackoverflow.com/questions/66062025/circular-layout-with-edge-bundling-and-labels-in-graph-tool

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!