I am a newbie in Python. I have a map like this map, and I want to create the shortest paths from each node to every other nodes using network x. I've tried to write a simple code like this:
shp = nx.read_shp("../Shapefiles/Shapefiles/Station_in_Corridors/Group_1.shp")
G = nx.DiGraph()
for data in shp.edges(data = True):
G.add_edge(data[0],data[1],weight = data[2]["Length_Km"])
nx.floyd_warshall(G)
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos = pos, node_size=100)
nx.draw_networkx_edges(G, pos = pos)
plt.show()
Prior to calling the result of floyd warshall, I'd like to see the graph first. Turns out the graph return like this:result. I don't think that graph is similar to the input (or is it?).
Anyhow, I've also tried to manually append the points with this code:
cor1 = driver.Open(cor1Data)
cor2 = driver.Open(cor2Data)
ly1 = cor1.GetLayer()
ly2 = cor2.GetLayer()
allpoints = {}
kreuz = []
arcs = {}
for i in range(ly1.GetFeatureCount()):
for j in range(ly2.GetFeatureCount()): #Create road
feat1 = ly1.GetFeature(i)
geom1 = feat1.GetGeometryRef()
points1 = geom1.GetPoints()
feat2 = ly2.GetFeature(j)
geom2 = feat2.GetGeometryRef()
points2 = geom2.GetPoints()
arcs[i] = [(points1[0],points1[1],geom1.Length()),feat1]
arcs[len(ly1)+j] = [(points2[0],points2[1],geom2.Length()),feat2]
#Create OD trips
if not points1[0] in allpoints.values():
allpoints[i] = [points1[0],geom1.Length(),feat1]
else:
allpoints[i] = [points1[1],geom1.Length(),feat1]
if not points2[0] in allpoints.values():
allpoints[len(ly1)+j] = [points2[0],geom1.Length(),feat1]
else:
allpoints[len(ly1)+j] = [points2[1],geom1.Length(),feat1]
#append kreuz
if points1[0] == points2[0] or points1[0] == points2[1]:
kreuz.append(points1[0])
elif points1[1] == points2[0] or points1[1] == points2[1]:
kreuz.append(points1[1])
G = nx.DiGraph() #Set a directed graph
for k,v in arcs.items():
G.add_edge(v[0][0],v[0][1], weight = v[0][2])
G.add_nodes_from(allpoints.values())
nx.floyd_warshall(G)
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos = pos, node_size=100)
nx.draw_networkx_edges(G, pos = pos)
plt.show()
and the result:
Result of second code
Is it a normal graph? And can anybody give some insights on how to calculate the shortest path right?
The networkx floyd_warshall calculates the shortest path for all pair of node in a graph and returns a dictionary as per the documentation.
distance (dict) – A dictionary, keyed by source and target, of shortest paths distances between nodes.
The algorithm does not change the graph in any way so by not storing the returned dictionary in a variable will do nothing.
To your question, you've already calculated the shortest paths, you simple doesn't do anything with them. If you wish to position the nodes in the plot according to some path length I don't think you're using an appropriate algorithm.
Related
I am using this script from here: link
I want to add new functionality to it. I want it to define starting node on the graph not only by finding the closest node (because that produces odd results like finding the closest node on an other road) but finding the closest and and the closest node along that edge.
My code is available below. I created the findnearestnodeonnearestedge function which should do the work but it doesn't work.
It finds the same node for the starting and destination point, even though they are far from each other...
I am using the newest versions of all packages so you can try the code easily.
Thank you for help
import osmnx as ox
import networkx as nx
import plotly.graph_objects as go
import numpy as np
def findnearestnodeonnearestedge(Gr, pointin):
u, v, key = ox.distance.nearest_edges(G, pointin[0], pointin[1])
n1 = Gr.nodes[u]
n2 = Gr.nodes[v]
d1 = ox.distance.euclidean_dist_vec(pointin[0], pointin[1], n1['x'], n1['y'])
d2 = ox.distance.euclidean_dist_vec(pointin[0], pointin[1], n2['x'], n2['y'])
if d1 < d2:
nodeid = u
else:
nodeid = v
return nodeid
state = ox.geocode_to_gdf('Georgia, US')
ax = ox.project_gdf(state).plot(fc='gray', ec='none')
_ = ax.axis('off')
# Defining the map boundaries
north, east, south, west = 33.798, -84.378, 33.763, -84.422
# Downloading the map as a graph object
G = ox.graph_from_bbox(north, south, east, west, network_type = 'drive')
# Plotting the map graph
ox.plot_graph(G)
# Displaying the 3rd node
list(G.nodes(data=True))[2]
# Displaying the 1st edge
list(G.edges(data=True))[1]
# Displaying the shape of edge using the geometry
list(G.edges(data=True))[1][2]['geometry']
# define origin and desination locations
origin_point = (33.787201, -84.405076)
destination_point = (33.764135, -84.394980)
# get the nearest nodes to the locations
origin_node = findnearestnodeonnearestedge(G, origin_point)
destination_node = findnearestnodeonnearestedge(G, destination_point)
# printing the closest node id to origin and destination points
origin_node, destination_node
# Finding the optimal path
route = nx.shortest_path(G, origin_node, destination_node, weight = 'length')
route
# getting coordinates of the nodes
# we will store the longitudes and latitudes in following list
long = []
lat = []
for i in route:
point = G.nodes[i]
long.append(point['x'])
lat.append(point['y'])
def plot_path(lat, long, origin_point, destination_point):
"""
Given a list of latitudes and longitudes, origin
and destination point, plots a path on a map
Parameters
----------
lat, long: list of latitudes and longitudes
origin_point, destination_point: co-ordinates of origin
and destination
Returns
-------
Nothing. Only shows the map.
"""
# adding the lines joining the nodes
fig = go.Figure(go.Scattermapbox(
name="Path",
mode="lines",
lon=long,
lat=lat,
marker={'size': 10},
line=dict(width=4.5, color='blue')))
# adding source marker
fig.add_trace(go.Scattermapbox(
name="Source",
mode="markers",
lon=[origin_point[1]],
lat=[origin_point[0]],
marker={'size': 12, 'color': "red"}))
# adding destination marker
fig.add_trace(go.Scattermapbox(
name="Destination",
mode="markers",
lon=[destination_point[1]],
lat=[destination_point[0]],
marker={'size': 12, 'color': 'green'}))
# getting center for plots:
lat_center = np.mean(lat)
long_center = np.mean(long)
# defining the layout using mapbox_style
fig.update_layout(mapbox_style="stamen-terrain", mapbox_center_lat=30, mapbox_center_lon=-80)
fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0},
mapbox={
'center': {'lat': lat_center, 'lon': long_center},
'zoom': 13})
fig.show()
plot_path(lat, long, origin_point, destination_point)
# Getting the start and end node of this part
start_node=route[-7]
end_node=route[-6]
# Getting the edge connecting these nodes and storing it as a list in z to maintain the data structure of G.edges
z = []
for i in list(G.edges(data=True)):
if (i[0]==start_node) & (i[1]==end_node):
z.append(i)
z[0][2]['geometry']
def node_list_to_path(G, node_list):
"""
Given a list of nodes, return a list of lines that together follow the path
defined by the list of nodes.
Parameters
----------
G : networkx multidigraph
route : list
the route as a list of nodes
Returns
-------
lines : list of lines given as pairs ( (x_start, y_start), (x_stop, y_stop) )
"""
edge_nodes = list(zip(node_list[:-1], node_list[1:]))
lines = []
for u, v in edge_nodes:
# if there are parallel edges, select the shortest in length
data = min(G.get_edge_data(u, v).values(), key=lambda x: x['length'])
# if it has a geometry attribute (ie, a list of line segments)
if 'geometry' in data:
# add them to the list of lines to plot
xs, ys = data['geometry'].xy
lines.append(list(zip(xs, ys)))
else:
# if it doesn't have a geometry attribute, the edge is a straight
# line from node to node
x1 = G.nodes[u]['x']
y1 = G.nodes[u]['y']
x2 = G.nodes[v]['x']
y2 = G.nodes[v]['y']
line = [(x1, y1), (x2, y2)]
lines.append(line)
return lines
# getting the list of coordinates from the path (which is a list of nodes)
lines = node_list_to_path(G, route)
long2 = []
lat2 = []
for i in range(len(lines)):
z = list(lines[i])
l1 = list(list(zip(*z))[0])
l2 = list(list(zip(*z))[1])
for j in range(len(l1)):
long2.append(l1[j])
lat2.append(l2[j])
print("Length of lat: ", len(lat))
print("Length of lat2: ", len(lat2))
plot_path(lat2, long2, origin_point, destination_point)
Problem was that in the graph coordinates are stored in reverse order. So all pointin[0] and pointin[1] in the function should be reversed and then it will work
I'm trying to update graph plotting, every 5-7 second. so I tried matplotlib to refresh the plot figure. But by using this, the same graph plotted again and again.
for better understanding - I have two files one for node creation, which is changed by every 10 seconds using another program and another is edge creation, which is static. from this files trying to create one graph which is dynamic like this - https://drive.google.com/file/d/1snFITs4jvW5H8JSF-3pqFE2F1XjX88PD/view?usp=sharing
my network code is -
fig = plt.figure()
net = fig.add_subplot(111)
def update(it):
with open('node.csv', 'r') as nodecsv:
nodereader = csv.reader(nodecsv)
nodes = [n for n in nodereader][1:]
node_names = [n[0] for n in nodes]
with open('edge.csv', 'r') as edgecsv:
edgereader = csv.reader(edgecsv)
edges = [tuple(e) for e in edgereader][1:]
g = nx.Graph()
g.add_nodes_from(node_names)
g.add_edges_from(edges)
print(nx.info(g))
# * ******************** Node Color Part **************************** *
nx.draw(g,pos=pos,node_size=node_size,node_color=color, linewidths=2,**options)
ani = animation.FuncAnimation(fig, update, interval=1000)
plt.show()
and another thing is, I'm trying to apply node colour according to the node attribute that is working but also according to node degree means if node attribute colour is blue then apply degree separately in blue. If green then applies degree separately in green and so on.
My node colour code -
node_status = {}
for node in nodes:
node_status[node[0]] = node[1]
nx.set_node_attributes(g,node_status,'node_status')
color = []
for n in g.nodes():
#print(n,g.nodes[n]['node_status'])
if g.nodes[n]['node_status'] == 'A': color.append("blue")
if g.nodes[n]['node_status'] == 'B': color.append("yellow")
if g.nodes[n]['node_status'] == 'C': color.append("red")
if g.nodes[n]['node_status'] == 'D': color.append("green")
if g.nodes[n]['node_status'] == 'E': color.append("pink")
betCent = nx.betweenness_centrality(g, normalized=True, endpoints=True)
#node_color = [20000.0 * g.degree(v) for v in g]
node_size = [v * 10000 for v in betCent.values()]
Thanks in advance.
So far I have the following code:
source = ['0','0','0','0','0','0','0']
destination = ['1','2','3','4','5','6','7']
FB_network_graph = pd.DataFrame({ 'from':source, 'to':destination})
G=nx.from_pandas_edgelist(FB_network_graph, 'from', 'to')
plt.figure(figsize = (100,100))
nx.draw(G, with_labels=True)
I want to plot a graph whereby node '0' has a size of 7 and node '1'-'7' has a size of 1.
It looks like you want to adjust the node_size according to the degree. For that you can define a dictionary from the result of G.degree and set the size according to the corresponding node degree by looking up the dictionary:
scale = 300
d = dict(G.degree)
nx.draw(G, node_color='lightblue',
with_labels=True,
nodelist=d,
node_size=[d[k]*scale for k in d])
Alternatively you could just define your custom dictionary, to set the corresponding node sizes in node_size. For this specific case with something like:
d = {str(k):1 for k in range(1,8)}
d['0'] = 7
I want to color nodes and edges of Karate club graph. But some of nodes have more than one color. Is there any way to color a node with more than one color in python (especially with networkx)?
I need something like this:
This can be done but it will probably require a lot of work to obtain the exact result you want. You could start with networkx and pygraphviz like this:
import networkx as nx
karate = nx.generators.social.karate_club_graph()
karate_agr = nx.nx_agraph.to_agraph(karate)
karate_agr.node_attr['style'] = 'filled'
karate_agr.node_attr['shape'] = 'circle'
karate_agr.node_attr['gradientangle'] = 90
for i in karate_agr.nodes():
n = karate_agr.get_node(i)
n.attr['fillcolor'] = 'green;0.5:yellow'
karate_agr.draw('karate.png',prog='dot')
Pygraphviz makes use of graphviz which has really a lot of options. Most of them can be set either for individual nodes (or edges) or globally for all of them like in the example above. It is all well explained in the graphviz documentation.
The above snippet only shows how to make the nodes filled half with one color and half with the other. See the result below (not very beautiful, I know).
EDIT
Hmm, so this kinda grew on me, and I really wanted to make something more similar to what you posted. This is what I came up with:
# coding: utf-8
import networkx as nx
import itertools
from collections import Counter
def edge_in_com(nodes, graph):
edges = []
for (i, j) in itertools.combinations(nodes, 2):
if (i, j) in graph.edges():
edges.append((i, j))
return edges
karate = nx.generators.social.karate_club_graph()
karate_agr = nx.nx_agraph.to_agraph(karate)
karate_agr.graph_attr['dpi'] = 180
karate_agr.edge_attr.update(
dir='both', arrowhead='inv', arrowtail='inv', penwidth=2.0)
karate_agr.node_attr.update(
style='filled',
fontcolor='white',
shape='circle',
color='transparent',
gradientangle=90)
colors = ['grey', 'pink', 'blue', 'purple']
communities = list(nx.community.asyn_fluidc(karate, 4))
most_edges = []
for n, com in enumerate(communities):
edges = edge_in_com(com, karate)
most_edges.extend(edges)
for edge in edges:
e = karate_agr.get_edge(*edge)
e.attr['color'] = colors[n]
for node in com:
node = karate_agr.get_node(node)
node.attr['fillcolor'] = colors[n]
other = [e for e in karate.edges() if e not in most_edges]
for edge in other:
gn = karate_agr.get_node
color = gn(edge[0]).attr['fillcolor']
karate_agr.get_edge(*edge).attr['color'] = color
for n in karate_agr.nodes():
cls = [e.attr['color'] for e in karate_agr.in_edges(n)]
cls2 = [e.attr['color'] for e in karate_agr.out_edges(n)]
cls = set(cls + cls2)
if len(cls) > 1:
# if n.attr['fillcolor'] != cls[0]:
color1 = cls.pop()
color2 = cls.pop()
color_mix = ''.join([color1, ';', '0.5:', color2])
n.attr['fillcolor'] = color_mix
karate_agr.draw('karate.png', prog='neato')
The program definitely can be improved, and I'm still not very happy with the results but maybe you'll find it helpful.
setting attribute 'style' to 'filled' is not work. change it to 'wedged' will be fine.
karate_agr.node_attr['style'] = 'wedged'
I am trying to print the binary tree using networkx library in python.
But, I am unable to preserve the left and right childs. Is there a way to tell the Graph to print left child first and then the right child?
import networkx as nx
G = nx.Graph()
G.add_edges_from([(10,20), (11,20)])
nx.draw_networkx(G)

EDIT 1: On using the pygraphwiz, it results in a directed graph atleast. So, I have better picture of the root node.
Below is the code I am using:
import pygraphviz as pgv
G = pgv.AGraph()
G.add_node('20')
G.add_node('10')
G.add_node('11')
G.add_edge('20','10')
G.add_edge('20','11')
G.add_edge('10','7')
G.add_edge('10','12')
G.layout()
G.draw('file1.png')
from IPython.display import Image
Image('file1.png')
But, this is still far from a structured format. I will post on what I find out next. The new graph looks like below (atleast we know the root):
EDIT 2: For those who are facing issues with installation, please refer to this post. The answer to this - its very helpful if you want to install pygraphviz on windows 64 bit.
I believe Networkx is not suited to binary trees but you can set the node positions yourself. I have wrote the following algorithm to setup node positions but it works fine for full or complete binary trees where key nodes are ordered [0,1,...].
def full_tree_pos(G):
n = G.number_of_nodes()
if n == 0 : return {}
# Set position of root
pos = {0:(0.5,0.9)}
if n == 1:
return pos
# Calculate height of tree
i = 1
while(True):
if n >= 2**i and n<2**(i+1):
height = i
break
i+=1
# compute positions for children in a breadth first manner
p_key = 0
p_y = 0.9
p_x = 0.5
l_child = True # To indicate the next child to be drawn is a left one, if false it is the right child
for i in xrange(height):
for j in xrange(2**(i+1)):
if 2**(i+1)+j-1 < n:
print 2**(i+1)+j-1
if l_child == True:
pos[2**(i+1)+j-1] = (p_x - 0.2/(i*i+1) ,p_y - 0.1)
G.add_edge(2**(i+1)+j-1,p_key)
l_child = False
else:
pos[2**(i+1)+j-1] = (p_x + 0.2/(i*i+1) ,p_y - 0.1)
l_child = True
G.add_edge(2**(i+1)+j-1,p_key)
p_key += 1
(p_x,p_y) = pos[p_key]
return pos
G = nx.Graph()
G.add_nodes_from(xrange(25))
pos = full_tree_pos(G)
nx.draw(G, pos=pos, with_labels=True)
plt.show()
which gave the following graph.
Note If you use networkx version 1.11 or earlier, see the note at the end.
The following assumes that each node has an attribute assigned to it telling whether it is the left or right child of its parent. So you'll have to assign this - by default a graph does not have any concept of this. Perhaps it might be possible to convince the networkx people to make a new class of graph which is a binary tree and stores this information automatically, but at present, it's not there. I don't know whether there would be enough interest to justify this.
import networkx as nx
def binary_tree_layout(G, root, width=1., vert_gap = 0.2, vert_loc = 0, xcenter = 0.5,
pos = None, parent = None):
'''If there is a cycle that is reachable from root, then this will see infinite recursion.
G: the graph
root: the root node of current branch
width: horizontal space allocated for this branch - avoids overlap with other branches
vert_gap: gap between levels of hierarchy
vert_loc: vertical location of root
xcenter: horizontal location of root
pos: a dict saying where all nodes go if they have been assigned
parent: parent of this branch.
each node has an attribute "left: or "right"'''
if pos == None:
pos = {root:(xcenter,vert_loc)}
else:
pos[root] = (xcenter, vert_loc)
neighbors = list(G.neighbors(root))
if parent != None:
neighbors.remove(parent)
if len(neighbors)!=0:
dx = width/2.
leftx = xcenter - dx/2
rightx = xcenter + dx/2
for neighbor in neighbors:
if G.nodes[neighbor]['child_status'] == 'left':
pos = binary_tree_layout(G,neighbor, width = dx, vert_gap = vert_gap,
vert_loc = vert_loc-vert_gap, xcenter=leftx, pos=pos,
parent = root)
elif G.nodes[neighbor]['child_status'] == 'right':
pos = binary_tree_layout(G,neighbor, width = dx, vert_gap = vert_gap,
vert_loc = vert_loc-vert_gap, xcenter=rightx, pos=pos,
parent = root)
return pos
Here is a sample call where I made even nodes into left children.
G= nx.Graph()
G.add_edges_from([(0,1),(0,2), (1,3), (1,4), (2,5), (2,6), (3,7)])
for node in G.nodes():
if node%2==0:
G.nodes[node]['child_status'] = 'left' #assign even to be left
else:
G.nodes[node]['child_status'] = 'right' #and odd to be right
pos = binary_tree_layout(G,0)
nx.draw(G, pos=pos, with_labels = True)
edit notes An earlier version of this answer worked for networkx version 1.11 and earlier. If you need it, please open the edit history and use the 2nd version of this answer.