Transforming adjaceny-matrix into an adjaceny-list - python-3.x

For the given adjacency matrix below:
[[0, 1, 0, 4], [1, 0, 3, 1], [0, 3, 0, 2], [4, 1, 2, 0]]
How would I go about converting it into an adjacency list like below where each tuple is (node,weight)
[[(1, 1), (3, 4)], [(0, 1), (2, 3), (3, 1)], [(1, 3), (3, 2)], [(0, 4), (1, 1), (2, 2)]]
thanks a lot!

Use a double comprehension:
a=[[0, 1, 0, 4], [1, 0, 3, 1], [0, 3, 0, 2], [4, 1, 2, 0]]
print([[(i,x) for i,x in enumerate(y) if x!=0] for y in a])

Related

incorrect result showing in K nearest neighbour approach

I am reforming the 2D coordinate number in a aligned way which was not aligned (coordinate numbers were suffled) before.
I have below input coordinates,
X = [2, 2, 3, 4, 4, 4, 4, 5, 6, 6, 6, 6, 6, 5, 4, 3, 5, 5, 5]
Y = [2, 3, 3, 3, 4, 5, 6, 6, 6, 5, 4, 3, 2, 2, 2, 2, 3, 4, 5]
I have to make it aligned. Therefore, I first applied Sorted function on this coordinates. I got below output after it.
merged_list1 = sorted(zip(X, Y))
output
X1_coordinate_reformed = [2, 2, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6]
Y1_coordinate_reformed = [2, 3, 2, 3, 2, 3, 4, 5, 6, 2, 3, 4, 5, 6, 2, 3, 4, 5, 6]
Still it iot aligned properly. I want two consecutive nodes place next to each other. Therefore I am applying the approach to find the nearest coordinate from origin to find the very first node. Then from the first node, I found another nearest coordinate and so on...For that, I have applied below code,
First I wrote a function which calculates the distance and gives index of the nearest coordinate from the list.
def solve(pts, pt):
x, y = pt
idx = -1
smallest = float("inf")
for p in pts:
if p[0] == x or p[1] == y:
dist = abs(x - p[0]) + abs(y - p[1])
if dist < smallest:
idx = pts.index(p)
smallest = dist
elif dist == smallest:
if pts.index(p) < idx:
idx = pts.index(p)
smallest = dist
return idx
coor2 = list(zip(X1_coordinate_reformed, Y1_coordinate_reformed)) # make a list which contains tuples of X and Y coordinates
pts2 = coor2.copy()
origin1 = (0, 0)
new_coor1 = []
for i in range(len(pts2)):
pt = origin1
index_num1 = solve(pts2, pt)
print('index is', index_num1)
origin1 = pts2[index_num1]
new_coor1.append(pts2[index_num1])
del pts2[index_num1]
After running the code, I got below output,
[(6, 6), (5, 6), (4, 6), (4, 5), (4, 4), (4, 3), (3, 3), (2, 3), (2, 2), (3, 2), (4, 2), (5, 2), (5, 3), (5, 4), (5, 5), (6, 5), (6, 4), (6, 3), (6, 2)]
Which is not correct because it can be clearly understand that,
coor2 = [(2, 2), (2, 3), (3, 2), (3, 3), (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), (6, 2), (6, 3), (6, 4), (6, 5), (6, 6)]
origin = (0, 0)
if we find the distance between Origin which was (0, 0) in very first and from every coordinate from above coor2 list, we will get (2,2) is nearest coordinate. Then How come my code gives (6,6) is the nearest coordinate??
The interesting thing is, if I apply the same procedure (sorting followed by finding nearest coordinate) on below coordinates,
X2_coordinate = [2, 4, 4, 2, 3, 2, 4, 3, 1, 3, 4, 3, 1, 2, 0, 3, 4, 2, 0]
Y2_coordinate = [3, 4, 2, 1, 3, 2, 1, 0, 0, 2, 3, 4, 1, 4, 0, 1, 0, 0, 1]
After applying sorted function
X2_coordinate_reformed = [0, 0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4]
Y2_coordinate_reformed = [0, 1, 0, 1, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
After applying method of searching nearest coordinates mentioned above, the result I got
[(0, 0), (0, 1), (1, 1), (1, 0), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 4), (3, 3), (3, 2), (3, 1), (3, 0), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]
Kindly suggest me where I am doing wrong and what should I change??
It is better to use scipy for finding closest coordinate.
The code given below works.
from scipy import spatial
pts = merged_list1.copy()
origin = (0, 0)
origin = np.array(origin)
new_coordi = []
for i in range(len(pts)):
x = origin
distance,index = spatial.KDTree(pts).query(x)
new_coordi.append(pts[index])
origin = np.array(pts[index])
del pts[index]

Creating a column of edges

I need to concatenate a uid from uids column to each of the uids in the list of the friends column, as shown in the following example:
Given a pandas.DataFrame object A:
uid friends
0 1 [10, 2, 1, 5]
1 2 [1, 2]
2 3 [5, 4]
3 4 [10, 5]
4 5 [1, 2, 5]
the desired output is:
uid friends in_edges
0 1 [10, 2, 1, 5] [(1, 10), (1, 2), (1, 1), (1, 5)]
1 2 [1, 2] [(2, 1), (2, 2)]
2 3 [5, 4] [(3, 5), (3, 4)]
3 4 [10, 5] [(4, 10), (4, 5)]
4 5 [1, 2, 5] [(5, 1), (5, 2), (5, 5)]
I use the following code to achieve this outcome:
import numpy as np
import pandas as pd
A = pd.DataFrame(dict(uid=[1, 2, 3, 4, 5], friends=[[10, 2, 1, 5], [1, 2], [5, 4], [10, 5], [1, 2, 5]]))
A.loc[:, 'in_edges'] = A.loc[:, 'uid'].apply(lambda uid: [(uid, f) for f in A.loc[A.loc[:, 'uid']==uid, 'friends'].values[0]])
but it the A.loc[A.loc[:, 'uid']==uid, 'friends'] part looks kind of cumbersome to me, so I wondered if there is an easier way to accomplish this task?
Thanks in advance.
You can use .apply() with axis=1 parameter:
df["in_edges"] = df[["uid", "friends"]].apply(
lambda x: [(x["uid"], f) for f in x["friends"]], axis=1
)
print(df)
Prints:
uid friends in_edges
0 1 [10, 2, 1, 5] [(1, 10), (1, 2), (1, 1), (1, 5)]
1 2 [1, 2] [(2, 1), (2, 2)]
2 3 [5, 4] [(3, 5), (3, 4)]
3 4 [10, 5] [(4, 10), (4, 5)]
4 5 [1, 2, 5] [(5, 1), (5, 2), (5, 5)]
Why not try product
import itertools
A['in_edges'] = A.apply(lambda x : [*itertools.product([x['uid']], x['friends'])],axis=1)
A
Out[50]:
uid friends in_edges
0 1 [10, 2, 1, 5] [(1, 10), (1, 2), (1, 1), (1, 5)]
1 2 [1, 2] [(2, 1), (2, 2)]
2 3 [5, 4] [(3, 5), (3, 4)]
3 4 [10, 5] [(4, 10), (4, 5)]
4 5 [1, 2, 5] [(5, 1), (5, 2), (5, 5)]

Networkx: How to find all unique paths of max given length in an unweighted, undirected, unlabeled, connected graph?

Let's say I have the following unweighted (all edges weight = 1), undirected, unlabeled, connected graph and I want to find all unique paths of maximum given length. Also, nodes cannot appear twice in a path. I cannot find a routine that does this in networkx atm.
Does anyone knows if any such thing exists ?
Or what could be a good solution for this problem ?
import networkx as nx
G = nx.Graph()
G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8, 9])
G.add_edges_from([(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (2, 4), (6, 9), (8, 9), (9, 6)])
The exemple graph looks like this
Let's say I require max length = 2, I would like this output
[1 2]
[2 3]
[2 4]
[3 4]
[4 5]
[5 6]
[6 7]
[7 8]
[8 9]
[6 9]
[1 2 3]
[1 2 4]
[2 3 4]
[2 4 5]
[3 4 5]
[4 5 6]
[5 6 7]
[5 6 9]
[6 7 9]
[6 7 8]
[7 8 9]
[6 9 8]
EDIT: I'm looking for a better solution than using itertools to generate all nodes combinations of required_max_path_length-1 number of nodes + checking for connectivity using G.has_edge(node_1, node_2) within the combinations groups or something similar, which seems like a super bad solution.
So now I'm doing this thx to #user3483203 and it yields the expected output. Itertools usage can be avoided but I don't mind in my specific case.
I still feel like it would scale a bit worst than something else for larger graphs though, I will change the accepted answer if someone finds a better solution.
import networkx as nx
import itertools
required_max_path_length = 2 # (inferior or equal to)
G = nx.Graph()
G.add_nodes_from([1, 2, 3, 4, 5, 6, 7, 8, 9])
G.add_edges_from([(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (2, 4), (6, 9), (8, 9), (9, 6)])
all_paths = []
nodes_combs = itertools.combinations(G.nodes, 2)
for source, target in nodes_combs:
paths = nx.all_simple_paths(G, source=source, target=target, cutoff=required_max_path_length)
for path in paths:
if path not in all_paths and path[::-1] not in all_paths:
all_paths.append(path)
for path in all_paths:
print(path)
In case you want the paths as lists of edges you can do:
for path in map(nx.utils.pairwise, all_paths):
print(list(path))
And you will get:
[(1, 2)]
[(1, 2), (2, 3)]
[(1, 2), (2, 4)]
[(2, 3)]
[(2, 3), (3, 4)]
[(2, 4)]
[(2, 4), (4, 5)]
[(3, 4)]
[(3, 4), (4, 5)]
[(4, 5)]
[(4, 5), (5, 6)]
[(5, 6)]
[(5, 6), (6, 7)]
[(5, 6), (6, 9)]
[(6, 7)]
[(6, 7), (7, 8)]
[(6, 8), (8, 9)]
[(6, 9)]
[(7, 8)]
[(6, 7), (7, 9)]
[(7, 8), (8, 9)]
[(8, 9)]
The following code should solve your task, but it outputs more paths than you have given (e.g. [1,2] and [2,1]):
def find_all_simple_paths(graph, cutoff):
if cutoff == 0:
return [[node] for node in graph]
else:
all_paths = []
current_paths = [[node] for node in graph]
# If you want to include paths of length 0
# all_paths.extend(current_paths)
for _ in range(min(cutoff, len(graph))):
next_paths = []
for path in current_paths:
#print(path)
for neighbor in graph.neighbors(path[-1]):
if neighbor not in path:
new_path = path[:] + [neighbor]
next_paths.append(new_path)
all_paths.append(new_path)
current_paths = next_paths
return all_paths
find_all_simple_paths(G,2)
Output
[[1, 2],
[2, 1],
[2, 3],
[2, 4],
[3, 2],
[3, 4],
[4, 3],
[4, 5],
[4, 2],
[5, 4],
[5, 6],
[6, 5],
[6, 7],
[6, 9],
[7, 6],
[7, 8],
[8, 7],
[8, 9],
[9, 6],
[9, 8],
[1, 2, 3],
[1, 2, 4],
[2, 3, 4],
[2, 4, 3],
[2, 4, 5],
[3, 2, 1],
[3, 2, 4],
[3, 4, 5],
[3, 4, 2],
[4, 3, 2],
[4, 5, 6],
[4, 2, 1],
[4, 2, 3],
[5, 4, 3],
[5, 4, 2],
[5, 6, 7],
[5, 6, 9],
[6, 5, 4],
[6, 7, 8],
[6, 9, 8],
[7, 6, 5],
[7, 6, 9],
[7, 8, 9],
[8, 7, 6],
[8, 9, 6],
[9, 6, 5],
[9, 6, 7],
[9, 8, 7]]

How to assign a random weight to edges in networkx, like weight of edge (a, a) = 0 and weight of edge (a, b) = K, where K is some random number

I am working on weighted graphs and I would like to assign a random weight for the edges of the graph, such that,
weight of edge(a, a) = 0
weight of (a, b) = weight of edge(b, a) = K
where K is some random number. This goes on for all the edges of the graphs.
For that, I am using random.randint() method. I am actually using the logic of sum. If sum of both the edges is same, then assign some random integer.
Here is my code,
nodelist = list(range(1, num_nodes + 1))
edgelist = []
for i in nodelist:
for j in nodelist:
if i == j:
edgelist.append((i, j, 0))
if (i != j and sum((i, j)) == sum((j, i))):
rand = random.randint(5, 25)
edgelist.append((i, j, rand))
print(edgelist)
Actual result,
[(1, 1, 0), (1, 2, 18), (1, 3, 6), (2, 1, 13), (2, 2, 0), (2, 3, 21), (3, 1, 20), (3, 2, 17), (3, 3, 0)]
Expected result,
[(1, 1, 0), (1, 2, K), (1, 3, H), (2, 1, K), (2, 2, 0), (2, 3, P), (3, 1, H), (3, 2, P), (3, 3, 0)]
where, K, H, P are some random integers.
If the ordering of the result is not important following code gives the desired output:
import random
num_nodes = 3
nodelist = list(range(1, num_nodes + 1))
edgelist = []
for i in nodelist:
for j in nodelist:
if j > i:
break
if i == j:
edgelist.append((i, j, 0))
else:
rand = random.randint(5, 25)
edgelist.append((i, j, rand))
edgelist.append((j, i, rand))
print(edgelist)
# [(1, 1, 0), (2, 1, 7), (1, 2, 7), (2, 2, 0), (3, 1, 18), (1, 3, 18), (3, 2, 13), (2, 3, 13), (3, 3, 0)]
In case you need the edges sorted, simply use:
print(sorted(edgelist))
# [(1, 1, 0), (1, 2, 20), (1, 3, 16), (2, 1, 20), (2, 2, 0), (2, 3, 23), (3, 1, 16), (3, 2, 23), (3, 3, 0)]
Just a little change in your code will do the trick.
Here is the solution I found to obtain your expected output
num_nodes = 3
nodelist = list(range(1, num_nodes + 1))
edgelist = []
for i in nodelist:
for j in nodelist:
if i == j:
edgelist.append((i, j, 0))
elif i < j:
rand = random.randint(5, 25)
edgelist.append((i, j, rand))
edgelist.append((j, i, rand))
print(sorted(edgelist))
This code outputs :
[(1, 1, 0), (1, 2, 15), (1, 3, 15), (2, 1, 15), (2, 2, 0), (2, 3, 21), (3, 1, 15), (3, 2, 21), (3, 3, 0)]
So I figured out something interesting. Say below matrix shows edges in a complete graph of 5 nodes,
[1, 1] [1, 2] [1, 3] [1, 4] [1, 5]
[2, 1] [2, 2] [2, 3] [2, 4] [2, 5]
[3, 1] [3, 2] [3, 3] [3, 4] [3, 5]
[4, 1] [4, 2] [4, 3] [4, 4] [4, 5]
[5, 1] [5, 2] [5, 3] [5, 4] [5, 5]
now, moving right side from principal diagonal, we have lists whose first element is less than second element. We just got to target them and append new random weight to it.
Here is my code,
nodelist = list(range(1, num_nodes + 1))
edgelist = []
for i in nodelist:
for j in nodelist:
edgelist.append([i, j])
p = 0
eff_edgelist = []
while p < len(edgelist):
if edgelist[p][0] <= edgelist[p][1]:
eff_edgelist.append(edgelist[p])
p += 1
for i in eff_edgelist:
if i[0] == i[1]:
i.append(0)
else:
i.append(random.randint(5, 50))
eff_edgelist = [tuple(i) for i in eff_edgelist]
for i in list(G.edges(data=True)):
print([i])
and the result,
[(1, 1, {'weight': 0})]
[(1, 2, {'weight': 12})]
[(1, 3, {'weight': 37})]
[(1, 4, {'weight': 38})]
[(1, 5, {'weight': 6})]
[(2, 2, {'weight': 0})]
[(2, 3, {'weight': 12})]
[(2, 4, {'weight': 40})]
[(2, 5, {'weight': 8})]
[(3, 3, {'weight': 0})]
[(3, 4, {'weight': 15})]
[(3, 5, {'weight': 38})]
[(4, 4, {'weight': 0})]
[(4, 5, {'weight': 41})]
[(5, 5, {'weight': 0})]
and if you check, print(G[2][1]), the output will be {'weight': 12},
which means weight of edge(a, b) = weight of edge(b, a).

Efficient way to iterate over sublists of a list grouped by duplicates

The title may be unclear, but I can't think of a better way to phrase it.
I mean a "list grouped by duplicates" as:
[2, 2, 1]
where each number in this list represents the number of duplicates of an element linked to the ith position of the list. For example, the above list could represent:
[0, 0, 1, 1, 2]
or
["a", "a", "b", "b", "c"]
or anything like that.
What would be an efficient way to generate/iterate over all the sublists of such a list?
As an example, the iterator of the above list should produce (in some order):
[[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [1, 0, 1], [0, 1, 1], [2, 0, 0], [0, 2, 0], [1, 1, 1], [2, 1, 0], [2, 0, 1], [0, 2, 1], [2, 1, 1], [2, 2, 0], [1, 2, 1], [2, 2, 1]]
I think the above list is complete, if it's not, please point it out. Regardless, I hope you have an idea of what I'm trying to do. Thanks in advance
For clarity, first we can generate the list of options for possible counts of each element, then take the cartesian product to get the desired result.
import itertools # for itertools.product
a = [2, 2, 1]
options = [list(range(x+1)) for x in a]
result = list(itertools.product(*options))
print(result)
[(0, 0, 0),
(0, 0, 1),
(0, 1, 0),
(0, 1, 1),
(0, 2, 0),
(0, 2, 1),
(1, 0, 0),
(1, 0, 1),
(1, 1, 0),
(1, 1, 1),
(1, 2, 0),
(1, 2, 1),
(2, 0, 0),
(2, 0, 1),
(2, 1, 0),
(2, 1, 1),
(2, 2, 0),
(2, 2, 1)]
Note that the * is an argument unpacking operator here, which allows the itertools.product function to take the product of all the (arbitrarily many) sublists we gave it in the options list.

Resources