Multidirected Graph in NetworkX? - python-3.x

does anyone know how to make a multidirected graph in NetworkX? My problem is, that the node 3 here does not return to node 2 as I wrote in the edgelist... Here is my code:
import networkx as nx
G = nx.MultiDiGraph(directed = True)
G.add_nodes_from(range(1, 11))
edgelist = [(1, 2), (2, 3), (3, 2), (3, 9), (9, 4), (4, 5), (5, 7), (7, 6), (6, 4), (7, 8), (7, 10), (10, 11), (11, 5)]
G = nx.from_edgelist(edgelist)
nx.draw_networkx(G,
with_labels = True,
arrows = True,
arrowstyle = '-|>',
node_size = 100,
node_color = 'yellow',
edge_color = 'black',
width = 3,
arrowsize = 10)
This is the image I get from this code. There should be two edges on node 2 and 3, but there is just one

The G = nx.from_edgelist(edgelist) line uses a constructor that replaces the original object, so it's drawing a standard Graph instead of a multidigraph (but still mimics the arrows since forced in the draw-call).
Try replacing that line with G = nx.from_edgelist(edgelist, create_using = nx.MultiDiGraph()) and you should at least get two arrows drawn on the desired edge. You can also check the output of nx.info(G) to verify the graph type in case you're unsure if it worked.
You also don't need the initial variables as the constructor handles that as well. Minimal code to get this working:
import networkx as nx
edgelist = [(1, 2), (2, 3), (3, 2), (3, 9), (9, 4), (4, 5), (5, 7), (7, 6), (6, 4), (7, 8), (7, 10), (10, 11), (11, 5)]
G = nx.from_edgelist(edgelist, create_using = nx.MultiDiGraph())
nx.draw_networkx(G,
with_labels = True,
arrows = True,
arrowstyle = '-|>',
node_size = 100,
node_color = 'yellow',
edge_color = 'black',
width = 3,
arrowsize = 10)

Related

Removing duplicate elements in a list with sets of arrays in python

I have a list[(position,id)] from the user based on the ids it chooses which I collect in data
data[(position,id)] = [(1,0),(2,0),(7,3),(8,6),(3,11),(3,11),(4,0),(5,1),(5,1),(6,2),(9,5),(10,7),(15,0),(16,10),(11,0),(11,1),(12,15),(13,8),(13,8),(13,9),(14,9)]
There are some duplicate elements in the list which I sorted out using *set(list) command
listdata = data
res=[]
res = [*set(listdata)]
print(res)
I get the res list as follows:
output: res = [(11, 1), (13, 8), (6, 2), (4, 0), (16, 10), (11, 0), (2, 0), (5, 1), (10, 7), (7, 3), (9, 5), (15, 0), (13, 9), (14, 9), (8, 6), (12, 15), (1, 0), (3, 11)]
But what I want is only 16 elements in the list on first come basis with positions 1 to 16, if you see here I have got 2 entries with position 11 [(11,1),(11,0)] and position 13[(13,8),(13,9)]
Required output:
res=[(11, 1), (13, 8), (6, 2), (4, 0), (16, 10), (2, 0), (5, 1), (10, 7), (7, 3), (9, 5), (15, 0), (14, 9), (8, 6), (12, 15), (1, 0), (3, 11)]
Can anyone suggest alternate solution?
This should probably be done with a generator.
res = [(11, 1), (13, 8), (6, 2), (4, 0), (16, 10), (11, 0), (2, 0), (5, 1), (10, 7), (7, 3), (9, 5), (15, 0), (13, 9), (14, 9), (8, 6), (12, 15), (1, 0), (3, 11)]
def foo(_res, max_items):
keys = []
for k,v in _res:
if k not in keys:
yield (k,v)
keys.append(k)
if len(keys) > max_items:
return
print(list(foo(res, 16)))
output:
[(11, 1), (13, 8), (6, 2), (4, 0), (16, 10), (2, 0), (5, 1), (10, 7), (7, 3), (9, 5), (15, 0), (14, 9), (8, 6), (12, 15), (1, 0), (3, 11)]
you can use a map to record every position only once
data = [(1,0),(2,0),(7,3),(8,6),(3,11),(3,11),(4,0),(5,1),(5,1),(6,2),(9,5),(10,7),(15,0),(16,10),(11,0),(11,1),(12,15),(13,8),(13,8),(13,9),(14,9)]
bucket = {}
for d in data:
bucket[d[0]] = d[1]
print(list(bucket.items()))
output is:
[(1, 0), (2, 0), (7, 3), (8, 6), (3, 11), (4, 0), (5, 1), (6, 2), (9, 5), (10, 7), (15, 0), (16, 10), (11, 1), (12, 15), (13, 9), (14, 9)]
update:
I am sorry I missed the description: "But what I want is only 16 elements in the list on first come basis", my answer above keeps the later came value, if you want to keep the first came one, do something like this:
data = [(1,0),(2,0),(7,3),(8,6),(3,11),(3,11),(4,0),(5,1),(5,1),(6,2),(9,5),(10,7),(15,0),(16,10),(11,0),(11,1),(12,15),(13,8),(13,8),(13,9),(14,9)]
bucket = {}
for d in data:
if d[0] not in bucket:
# if you never seen it, keep it, else ingore it.
bucket[d[0]] = d[1]
print(list(bucket.items()))

Find coordinates that are outside a radius

I'm a beginner with Python. I need to get the number of arrows (points) that are outside the radius. I do know what the answer is, but how do I get that output in Python?
This is what I have:
points = [(4, 5), (-0, 2), (4, 7), (1, -3), (3, -2), (4, 5), (3, 2), (5, 7), (-5, 7), (2, 2), (-4, 5), (0, -2),(-4, 7), (-1, 3), (-3, 2), (-4, -5), (-3, 2), (5, 7), (5, 7), (2, 2), (9, 9), (-8, -9)]
center = (0,0)
radius = 9
You could use a list comprehension:
import math
points = [(4, 5), (-0, 2), (4, 7), (1, -3), (3, -2), (4, 5), (3, 2), (5, 7),
(-5, 7), (2, 2), (-4, 5), (0, -2), (-4, 7), (-1, 3), (-3, 2), (-4, -5),
(-3, 2), (5, 7), (5, 7), (2, 2), (9, 9), (-8, -9)]
center = (0, 0)
radius = 9
points_outside_radius = [
p
for p in points
if math.sqrt((p[0] - center[0]) ** 2 + (p[1] - center[1]) ** 2) > radius
]
num_points_outside_radius = len(points_outside_radius)
print(f'There are {num_points_outside_radius} points outside the radius:')
print(points_outside_radius)
Output:
There are 2 points outside the radius:
[(9, 9), (-8, -9)]
Note I used the full euclidian distance formula in case you need to change the center to something other than the origin.

How to avoid inplace removal of modification of a Networkx graph

I've a netwrokx graph, I'm trying to remove the edges of the graph using remove_edges.
I want to remove each edge in the original graph and post-process H to get further stats like edges connected to the edge that has been removed.
import networkx as nx
import matplotlib.pyplot as plt
# fig 1
n=10
G = nx.gnm_random_graph(n=10, m=10, seed=1)
nx.draw(G, with_labels=True)
plt.show()
for e in [[5, 0], [3, 6]]:
H = G.remove_edge(e[0], e[1])
nx.draw(G, with_labels=True)
plt.show()
In the above, the edge is removed inplace in G. So for the second iteration, the original graph is no
longer present. How can this be avoided? I want to retain the original graph for every iteration and instead store the graph that results after edge removal in another copy, H.
Any suggestions will be highly appreciated.
EDIT: Based on what's suggested below
n=10
G = nx.gnm_random_graph(n=10, m=10, seed=1)
nx.draw(G, with_labels=True)
plt.show()
G_copy = G.copy()
for e in [[5, 0], [3, 6]]:
print(G_copy.edges())
H = G_copy.remove_edge(e[0], e[1])
nx.draw(G_copy, with_labels=True)
plt.show()
print(G_copy.edges())
Obtained output:
[(0, 6), (0, 7), (0, 5), (1, 4), (1, 7), (1, 9), (2, 9), (3, 6), (3, 4), (6, 9)]
[(0, 6), (0, 7), (1, 4), (1, 7), (1, 9), (2, 9), (3, 6), (3, 4), (6, 9)]
Expected:
[(0, 6), (0, 7), (0, 5), (1, 4), (1, 7), (1, 9), (2, 9), (3, 6), (3, 4), (6, 9)]
[(0, 6), (0, 7), (0, 5), (1, 4), (1, 7), (1, 9), (2, 9), (3, 6), (3, 4), (6, 9)]
Make a copy of the original graph and modify the copy:
H = G.copy()
...
H.remove_edge(e[0], e[1])

Matplotlib not showing point in PyCharm

Using Python 3 in PyCharm on Windows 10
I have a list of tuples that I need to plot. But the matplotlib is showing an empty graph:
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.use('TkAgg')
input = [(1, 6), (4, 15), (7, 7), (10, 13), (11, 6),
(11, 18), (11, 21), (12, 10), (15, 18),
(16, 6), (18, 3), (18, 12), (19, 15), (22, 19)]
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
input_points = []
for array_x, array_y in input:
input_points.append(Point(array_x, array_y))
plt.plot(array_x, array_y)
plt.show()
How to get the points to show up and plotted in the graph?
To draw lines, plt.plot needs a list (or numpy array) of x-positions and a list of y-positions. The documentation lists the different options to draw markers and/or lines.
List comprehension is a handy way to extract x or y positions from a list of xy-coordinates.
import matplotlib.pyplot as plt
input_points = [(1, 6), (4, 15), (7, 7), (10, 13), (11, 6),
(11, 18), (11, 21), (12, 10), (15, 18),
(16, 6), (18, 3), (18, 12), (19, 15), (22, 19)]
array_x = [x for x, y in input_points]
array_y = [y for x, y in input_points]
plt.plot(array_x, array_y, marker='o', color='crimson', linestyle='-')
plt.show()

(Algorithms) Finding the shortest path that passes through a required set of nodes (possibly with BFS) and returns to the origin in Python

I am trying to find a shortest path that passes through a set of nodes [4,7,9] (order does not need to be preserved) and then returns to the origin (node 1). I have the set of edges:
E = [(1, 10), (1, 11), (2, 3), (2, 10), (3, 2), (3, 12), (4, 5), (4, 12), (5, 4), (5, 14), (6, 7), (6, 11), (7, 6), (7, 13), (8, 9), (8, 13), (9, 8), (9, 15), (10, 1), (10, 11), (10, 2), (11, 1), (11, 10), (11, 6), (12, 13), (12, 3), (12, 4), (13, 12), (13, 7), (13, 8), (14, 15), (14, 5), (15, 14), (15, 9)]
and I tried adapting the answer at How can I use BFS to get a path containing some given nodes in order? but yielded the error:
Traceback (most recent call last):
File "C:/Users/../rough-work.py", line 41, in <module>
graph[edge[0]].link(graph[edge[-1]])
KeyError: 15
My adapted code is as follows:
class Node:
def __init__(self, name):
self.name = name
self.neighbors = []
def link(self, node):
# The edge is undirected: implement it as two directed edges
self.neighbors.append(node)
node.neighbors.append(self)
def shortestPathTo(self, target):
# A BFS implementation which retains the paths
queue = [[self]]
visited = set()
while len(queue):
path = queue.pop(0) # Get next path from queue (FIFO)
node = path[-1] # Get last node in that path
for neighbor in node.neighbors:
if neighbor == target:
# Found the target node. Return the path to it
return path + [target]
# Avoid visiting a node that was already visited
if not neighbor in visited:
visited.add(neighbor)
queue.append(path + [neighbor])
###
n = 15
nodes = list(range(1,n))
E = [(1, 10), (1, 11), (2, 3), (2, 10), (3, 2), (3, 12), (4, 5), (4, 12), (5, 4), (5, 14), (6, 7), (6, 11), (7, 6), (7, 13), (8, 9), (8, 13), (9, 8), (9, 15), (10, 1), (10, 11), (10, 2), (11, 1), (11, 10), (11, 6), (12, 13), (12, 3), (12, 4), (13, 12), (13, 7), (13, 8), (14, 15), (14, 5), (15, 14), (15, 9)]
# Create the nodes of the graph (indexed by their names)
graph = {}
for letter in nodes:
graph[letter] = Node(letter)
print(graph)
# Create the undirected edges
for edge in E:
graph[edge[0]].link(graph[edge[-1]])
# Concatenate the shortest paths between each of the required node pairs
start = 1
path = [graph[1]]
for end in [4,7,9,1]:
path.extend( graph[start].shortestPathTo(graph[end])[1:] )
start = end
# Print result: the names of the nodes on the path
print([node.name for node in path])
What could possibly be the problem with the code? I will like to extend the graph to a arbitrarily large number of nodes, greater than 26 - the number of alphabets (as I infer that the previous implementation was only for character-based nodes). Or, if there is a more straightforward way in doing this that will be great!
Thanks and some help will be deeply appreciated!
The KeyError: 15 and your line print(graph) should have given you the clue: the latter shows that your graph dictionary contains only 14 entries, whereas your edges in E clearly make reference to 15 separate indices.
Change n = 15 to n = 16 and it works:
[1, 10, 2, 3, 12, 4, 12, 13, 7, 13, 8, 9, 8, 13, 7, 6, 11, 1]
Remember that:
>>> len(list(range(1,16)))
15

Resources