0

I'm trying to create a data visualization, and maybe NetworkX isn't the best tool but I would like to have parallel columns of nodes (2 separate groups) which connect to each other. I can't figure out how to place the two groups of nodes in this layout. The different options I have tried always default to a more 'web-like' layout. I'm trying to create a visualization where customers/companies (the keys in a dictionary) would have edges drawn to product nodes (values in the same dictionary).

For example:

d = {"A":[ 1, 2, 3], "B": [2,3], "C": [1,3]

From dictionary 'd', we would have a column of nodes ["A", "B", "C"] and second column [1, 2, 3] and between the two edges would be drawn.

A                   1
B                   2
C                   3

UPDATE:

So the 'pos' argument suggested helped but I thought it was having difficulties using this on multiple objects. Here is the method I came up with:

nodes = ["A", "B", "C", "D"]
nodes2 = ["X", "Y", "Z"]

edges = [("A","Y"),("C","X"), ("C","Z")]

#This function will take a list of values we want to turn into nodes
# Then it assigns a y-value for a specific value of X creating columns
def create_pos(column, node_list):
    pos = {}
    y_val = 0
    for key in node_list:   
        pos[key] = (column, y_val)
        y_val = y_val+1
    return pos 



G.add_nodes_from(nodes)
G.add_nodes_from(nodes2)
G.add_edges_from(edges)

pos1 = create_pos(0, nodes)
pos2 = create_pos(1, nodes2)
pos = {**pos1, **pos2}

nx.draw(G, pos)
  • 1
    Did you consider simply using matplotlib for this and creating the lines manually between the points? would need some tricks for text annotations but I think that is easier. – Nico Albers Apr 02 '18 at 16:42
  • Maybe you want to consider this also: https://stackoverflow.com/a/11809184/6673446 – Nico Albers Apr 02 '18 at 16:43
  • Check the `pos` parameter of [draw_networkx](https://networkx.github.io/documentation/networkx-2.1/reference/generated/networkx.drawing.nx_pylab.draw_networkx.html#networkx-drawing-nx-pylab-draw-networkx). It takes a dict of node:position and a position is a plain tuple of coordinates. – wolfrevokcats Apr 02 '18 at 17:03
  • @wolfrevokcats, Thanks! I had to mess around with it but that set me on the right track for what I found to be a workable solution (above in UPDATE). Thoughts? – James Gregorie Apr 03 '18 at 12:55
  • @JamesGregorie, no, it's wrong that way, you'd better create pos in the function and return it: `def create_pos(..): pos={} ... return pos`. And then: `pos1 = create_pos(0, nodes); pos2 = create_pos(1, nodes1)`. And finally merging: `pos = { **pos1, **pos2 }` – wolfrevokcats Apr 03 '18 at 13:06
  • @wolfrevokcats Wrong how/why? I ran it both ways and the results were the same. – James Gregorie Apr 04 '18 at 14:37
  • @JamesGregorie, I'm not saying that your code produces incorrect results. The thing is using global variables and modifying them inside functions is a bad programming practice. There is a lot of material on that, [for example](http://wiki.c2.com/?GlobalVariablesAreBad). It would be ok if you had `pos` and the function inside a class as class members. – wolfrevokcats Apr 04 '18 at 15:00
  • @wolfrevokcats Gotcha. Yeah I am self taught so there are best practices that I am not familiar. do you have a resource on classes in python? That is something I have seen and wasn't sure what its purpose was – James Gregorie Apr 04 '18 at 16:42
  • @JamesGregorie, well, as you write more code some things become more obvious. If you haven't read anything about classes I think just about *any* tutorial on classes in Python will be helpful, [this one](https://www.hackerearth.com/practice/python/object-oriented-programming/classes-and-objects-i/tutorial/), for example. – wolfrevokcats Apr 04 '18 at 16:58

1 Answers1

0

Here is the code I wrote with the help of @wolfevokcats to create columns of nodes which are connected.

G = nx.Graph()

nodes = ["A", "B", "C", "D"]
nodes2 = ["X", "Y", "Z"]

edges = [("A","Y"),("C","X"), ("C","Z")]

    #This function will take a list of values we want to turn into nodes
    # Then it assigns a y-value for a specific value of X creating columns
def create_pos(column, node_list):
    pos = {}
    y_val = 0
    for key in node_list:   
        pos[key] = (column, y_val)
        y_val = y_val+1
    return pos 



G.add_nodes_from(nodes)
G.add_nodes_from(nodes2)
G.add_edges_from(edges)

pos1 = create_pos(0, nodes)
pos2 = create_pos(1, nodes2)
pos = {**pos1, **pos2}

nx.draw(G, pos, with_labels = True)