How do I add edges between two subgraphs in pydot? - python

Does anyone know how to add an edge between two subgraphs (clusters) in pydot?
callgraph = pydot.Dot(graph_type='digraph',fontname="Verdana")
cluster_foo=pydot.Cluster('foo',label='foo')
cluster_foo.add_node(pydot.Node('foo_method_1',label='method_1'))
callgraph.add_subgraph(cluster_foo)
cluster_bar=pydot.Cluster('bar',label='Component1')
cluster_bar.add_node(pydot.Node('bar_method_a'))
callgraph.add_subgraph(cluster_bar)
I tried:
callgraph.add_edge(pydot.Edge("foo","bar"))
but doesn't work. It just creates two more nodes labeled "foo" and "bar" in the initial graph and puts an edge between them!
Can anyone help, please?

Graphviz demands that an edge be between nodes in the 2 clusters.
Add graph parameter compound='true'.
Use edge parameters lhead= and ltail=.
So your code would become:
callgraph = pydot.Dot(graph_type='digraph', fontname="Verdana", compound='true')
cluster_foo=pydot.Cluster('foo',label='foo')
callgraph.add_subgraph(cluster_foo)
node_foo = pydot.Node('foo_method_1',label='method_1')
cluster_foo.add_node(node_foo)
cluster_bar=pydot.Cluster('bar',label='Component1')
callgraph.add_subgraph(cluster_bar)
node_bar = pydot.Node('bar_method_a')
cluster_bar.add_node(node_bar)
callgraph.add_edge(pydot.Edge(node_foo, node_bar, ltail=cluster_foo.get_name(), lhead=cluster_bar.get_name()))

Related

How to display graph in Pyvis more clearly?

I want to visualize a graph in Pyvis which its nodes has labels. I am completely able to visualize it in Pyvis but my problem is about the ways of visualizing it. The graph displayed in Pyvis is not clear and edges are messed up. Is there any way to visualize the graph more clear?
The image below shows the graph.
For example in the graph, node 15 is displayed well. I want other nodes to be displayed in a clear way that the connections can be displayed more clearly
Update:
This is the code i use for drawing graph using Pyvis:
def showGraph(FileName, labelList):
Txtfile = open("./results.txt")
G = nx.read_weighted_edgelist(Txtfile)
Txtfile.close()
palette = (sns.color_palette("Pastel1", n_colors=len(set(labelList.values()))))
palette = palette.as_hex()
colorDict = {}
counter = 0
for i in palette:
colorDict[counter] = i
counter += 1
N = Network(height='100%', width='100%', directed=False, notebook=False)
for n in G.nodes:
N.add_node(n, color=(colorDict[labelList[n]]), size=5)
for e in G.edges.data():
N.add_edge(e[0], e[1], title=str(e[2]), value=e[2]['weight'])
N.show('result.html')
results.txt is my edge list file and labelList holds label of each node. Labels are numerical. For example label of node 48 is 5, it can be anything. I use labels to give different colors to nodes.
The NetworkX circular layouts tend to make individual nodes and the connections between them easier to see, so you could try that as long as you don't want nodes to move (without dragging) after you've drawn them.
Before creating your pyvis network, run the following on your NetworkX graph to create a dictionary that will be keyed by node and have (x, y) positions as values. You might need to mess around with the scale parameter a bit to see what works best for you.
pos = nx.circular_layout(G, scale = 1000)
You can then add x and y values from pos to your pyvis network when you add each node. Adding physics = False keeps the nodes in one place unless you click and drag them around.
for n in G.nodes:
N.add_node(n,
color=(colorDict[labelList[n]]),
size=5,
x = pos[n][0],
y = pos[n][1],
physics = False)
I'm not sure how the edge weights will play into things, so you should probably also add physics = False to the add_edge parameters to ensure that nothing will move.
Since I didn't have your original data, I just generated a random graph with 10 nodes and this was the result in pyvis.

Add node between existing edge in Networkx Graph generated by OSMnx

I have gotten sensor location data from Highway England. I want to add these sensor locations to OSM multidigraph. How to do that?
import numpy as np
import pandas as pd
import networkx as nx
from shapely.geometry import Point, Polygon, LineString
import geopandas as gpd
import osmnx as ox
Graph data is
graph = ox.graph.graph_from_bbox(52.2, 51.85, -.6, -0.9, network_type='drive', simplify=False)
I want to add sensor = Point(-0.6116768, 51.8508765) on the edge nearest to it. Nearest edges to this sensor is n_edge = osmnx.distance.nearest_edges(graph, -0.6116768, 51.8508765, return_dist=False). Now, I need to bend this n_edge such that it passes through the given sensor point.
I found a way to solve this issue by creating a new node in graph, graph.add_node('sensor25', y= 51.8508765, x= -0.6116768, street_count = 2) then graph.add_edges_from([(n_edge[0], 'sensor25'), ('sensor25', n_edge[1)]). However, the node created by me (sensor25) is not identical to other nodes. How to make this node similar to existing nodes?
I have went through following questions
add attribute to node
add new node to existing edge in networkx
add random nodes on edges manually.
I'm not 100% certain what you need, what I understand: You want to add new edges with attributes: speed_limit, length, street number one way, copied from the edge you delete?
I assume that some of these attributes can be copied 1:1, like one way, while others will have to be recalulated. For simplicity, let's assume we have a function d(a, b) that takes (graph) nodes a and b, extracts their position, and calculates the air distance between them. Define other functions as required.
Then you could e.g. define the new edge like this:
# Get from/to id of closest edge
f, t = osmnx.distance.nearest_edges(graph, -0.6116768, 51.8508765, return_dist=False)[0]
c = 'sensor25' # Id of new node, c as in 'center'
edge_attrs = g[f][t] # Copy edge attributes
g.remove_edge(f, t) # Remove edge from graph
graph.add_node(c, y= 51.8508765, x= -0.6116768, street_count = 2)
# Add new edges, recalculating atttributes as required
g.add_edge(f, c, **{**edge_attrs, 'length': d(f, c)})
g.add_edge(c, t, **{**edge_attrs, 'length': d(c, t)})
Hope the syntax is clear, otherwise ask. It copies edge_attrs 1:1, except for attributes you specify after, like lenght. Probably you will have to define multiple functions like d, that also calculate the geometry etc.
The code isn't tested.

Adding specific weights to edges through dictionary

I have two dictionaries of nodes with edges like below:
all_nodes_edges = {20101: (20102, 20201), 20102: (20101, 20103), 20103: (20102, 20104)}
nodes_with_weights = {20101: 20201, 20119: 20219, 20201: (20301, 20101)}
I've created the graph and default weights for all the edges in the graph with the below code:
g = nx.Graph(all_nodes_edges)
nx.set_edge_attributes(g, 1, 'weight')
I'm trying to use the nodes_with_weights dict to create weights of 2 on specific edges. How do I achieve this?. Do I have to loop through the dictionary or can I just use a specific nx function?
Sorry kinda new to graphs.
I finally figured it out!
for u,v,a in g.edges(data=True):
if u and v in nodes_with_weights:
a = 2

Check if a random graph coloring is correct (Python)

So i'm trying a different approach to graph coloring, what i did basically is assign randomly colors to the nodes of a graph and what i want to do is, after assigning those colors, check if that coloring is correct (no adjacent nodes having the same color) in other words, going through the nodes and their respective colors and make sure no adjacent nodes have that same color.
Here is what i've done so far :
def approx_color(graph):
colors = [1,2,3,4,5,6,7,8,9]
xr = random.randint(0, len(graph.nodes))
s_c = []
for i in range(len(graph.nodes)):
s_c.append(random.choice(colors))
colored = dict(zip(graph.nodes,s_c))
print(colored)
EDIT :
The "graph" variable is a graph generated by networkx library, and graph.nodes() graph.edges() being a list of the nodes and edges of the graph
For the first part you can directly use random.choices()
def approx_color(graph):
colors = [1,2,3,4,5,6,7,8,9]
s_c = random.choices(colors, k=graph.order())
colored = dict(zip(graph.nodes(), s_c))
A graph coloring is correct if no two adjacent vertices are of the same color.
So you just have to iterate over the nodes and check their neighbors.
So you can just define a function that check for the condition to be valid.
for u in graph.nodes():
for v in graph.neighbors(u):
if colored[u] == colored[v]:
return False
return True

Python Igraph community cluster colors

I want to have clusters in different colours after community infomap, but problem is when I deleted single nodes it makes a mess an each node is different color or everything is red. How to do that in python?
Code:
E = ig.Graph(edges)
E.vs\['label'\] = labels
degree = 0
community = E.community_infomap()
cg = community.graph
singletons = cg.vs.select(_degree = 0)
cg.delete_vertices(singletons)
color_list =['red','blue','green','cyan','pink','orange','grey','yellow','white','black','purple' ]
ig.plot(cg)
It is not clear how did you try to assign colors to vertices. You should be aware that igraph reindexes vertices and edges upon deletion and addition of either vertices or edges. This reindexing should be considered unpredictable, the only things we know that indices go from 0 to n-1 at all time, and attributes remain assigned to the correct vertex or edge. Considering these, you can either do the deletion before or after the community detection, only you need to assign colors to a vertex attribute:
import igraph
g = igraph.Graph.Barabasi(n = 20, m = 1)
i = g.community_infomap()
pal = igraph.drawing.colors.ClusterColoringPalette(len(i))
g.vs['color'] = pal.get_many(i.membership)
igraph.plot(g)
Now let's see what happens if we delete a vertex:
colors_original = pal.get_many(i.membership)
g.delete_vertices([7])
# the clustering object is still the same length
# (so it is not valid any more, you can't be sure
# if the i.th vertex in the clustering is the
# i.th one in the graph)
len(i) # 20
# while the graph has less vertices
g.vcount() # 19
# if we plot this with the original colors, indeed we get a mess:
igraph.plot(g, vertex_color = colors_original)
But the colors in the g.vs['color'] vertex attribute are still correct, they show the clusters, only the deleted vertex is missing (from the dark blue cluster):
igraph.plot(g,
vertex_color = g.vs['color']) # this is automatic, I am
# writing here explicitely only to be clear
I found solution. First delete single nodes than convert to igraph and do community.

Categories