1
votes

I used previously the function distance() in igraph which computes the distance between two nodes or two vectors of nodes. Now I am writing a code in python using NetworkX 2.2 and I am trying to find also the distance between two lists of nodes (not two nodes).

It seems that there is no function can do this in NetworkX. In fact I used shortest_path_length() but it didn't work. What I am doing here is : 1. read the graph edge by edge, 2. then for each edge to select the first vertex v1 and the second vertex v2, 3. find the neighbors connected to the first vertex and also find the connected neighbors to the second vertex, 4. finally compute the distance between the neighbors of v1 and neighbors of v2.

At the end I want to get,for each edge, a vector contains the distances between the neighbors for both vertices v1 and v2. My code in R

library(igraph)
graph<-matrix(c(4,3,4,1,4,2,3,2,3,1),ncol=2,byrow=TRUE)
g<-graph.data.frame(d = graph, directed = FALSE)

v1<-c()
v2<-c()
n1<-list()
n2<-list()
distance<-list()
distance.bt.neighbors<-list()

for(edge in 1:length(E(g))){
  v1[edge]<-ends(graph = g, es = edge)[1]
  v2[edge]<-ends(graph = g, es = edge)[2]
  n1<-neighbors(g,v1[edge],mode=c("all"))
  n2<-neighbors(g,v2[edge],mode=c("all"))
  distance[[edge]]<-distances(g, v = n1, to = n2, mode = c("all"))
  distance.bt.neighbors[[edge]]<-c(distance[[edge]])
                           }
distance.bt.neighbors
[[1]]
[1] 1 1 1 1 0 2 1 2 0

[[2]]
[1] 1 1 1 0 1 1

[[3]]
[1] 1 1 1 0 1 1

[[4]]
[1] 0 1 1 1 1 1

[[5]]
[1] 0 1 1 1 1 1

To do this in python, I wrote this code

import os
import igraph
import numpy as np
import networkx as nx

os.chdir('Desktop')

graph = nx.read_edgelist("attempt") # the file attempt contains the same data as in the R code.

neighbor1 = []
neighbor2 = []
distance = []

for edge in list(graph.edges):
  neighbor1.append(list(graph.neighbors(edge[0])))
  neighbor2.append(list(graph.neighbors(edge[1])))
  distance.append(nx.shortest_path_length(graph, source=neighbor1, target= neighbor2))

But I got this error which states that the neighbors are not defined as vertices since they are lists not single values

Traceback (most recent call last):
File "<stdin>", line 4, in <module>
File "/home/abdelrahman/anaconda/lib/python3.7/site-packages/networkx/algorithms/shortest_paths/generic.py", line 312, in shortest_path_length
p = nx.bidirectional_shortest_path(G, source, target)
File "/home/abdelrahman/anaconda/lib/python3.7/site-packages/networkx/algorithms/shortest_paths/unweighted.py", line 223, in bidirectional_shortest_path
raise nx.NodeNotFound(msg.format(source, target))
networkx.exception.NodeNotFound: Either source [['3', '4']] or target [['1', '2']] is not in G

Is there a possibility in python to get a list for the distances between lists of of vertices, not single values, as I did in R ? Is there such a function and if not is it possible to modify the current function?

Note: I didn' use igraph-python to get the required list for two reasons: there is no such function , according to my search , in igraph and to stay away from the problem of loosing the names of vertices produced when tryinh to get neighbors for vertices.

2

2 Answers

1
votes

You were close, except in the last loop where you have to iterate over the neighbours lists again and then store the distances

import numpy as np
import networkx as nx

# Since I didn't have your data, I simply recreated from your R code
graph = nx.Graph()
for i in range(1, 5):
  graph.add_node(i)

for x,y in [(4, 3), (4, 1), (4, 2), (3, 2), (3, 1)]:
  graph.add_edge(x, y)

# print(graph.edges())
# Output EdgeView([(4, 3), (4, 1), (4, 2), (3, 2), (3, 1)])

distance_neighbors = {}

for edge in list(graph.edges):
  neighbor1 = tuple(graph.neighbors(edge[0]))
  neighbor2 = tuple(graph.neighbors(edge[1]))

  distance_list = []
  for v1 in neighbor1:
    for v2 in neighbor2:
      distance_list.append(nx.shortest_path_length(graph, source=v1, target=v2))
  distance_neighbors[edge] = distance_list

The distance_neighbours contains the following data:

{(1, 3): [0, 1, 1, 1, 1, 1],
 (1, 4): [1, 1, 1, 0, 1, 1],
 (2, 3): [0, 1, 1, 1, 1, 1],
 (2, 4): [1, 1, 1, 0, 1, 1],
 (3, 4): [1, 1, 1, 1, 2, 0, 1, 0, 2]}

The ordering of the value in the last edge(3,4) is different because Python is ordering the neighbours different from how R does it. In order to make sure the behaviour is same, run the following code:

import os

import numpy as np
import networkx as nx

# Since I didn't have your data, I simply recreated from your R code
graph = nx.Graph()
for i in range(1, 5):
  graph.add_node(i)

for x,y in [(4, 3), (4, 1), (4, 2), (3, 2), (3, 1)]:
  graph.add_edge(x, y)

# print(graph.edges())
# Output EdgeView([(4, 3), (4, 1), (4, 2), (3, 2), (3, 1)])

distance_neighbors = {}

for edge in list(graph.edges):
  # Just sort the neighbours list in reverse order
  neighbor1 = tuple(sorted(graph.neighbors(edge[0]), reverse=True))
  neighbor2 = tuple(sorted(graph.neighbors(edge[1]), reverse=True))

  distance_list = []
  for v1 in neighbor1:
    for v2 in neighbor2:
      distance_list.append(nx.shortest_path_length(graph, source=v1, target=v2))
  distance_neighbors[edge] = distance_list

Now the distance_neighbors has the same output as your R code:

{(1, 3): [0, 1, 1, 1, 1, 1],
 (1, 4): [1, 1, 1, 0, 1, 1],
 (2, 3): [0, 1, 1, 1, 1, 1],
 (2, 4): [1, 1, 1, 0, 1, 1],
 (3, 4): [1, 1, 1, 1, 0, 2, 1, 2, 0]}

Here is the link to the Google Colab notebook with the above code.

Hope this helps!

0
votes

Last line of your code giving the error. Inside For neighbor1 and neighbor2 getting updated as a list with multiple nodes after each iteration and for nx.shortest_path_length you need to pass single source and a single target node, not a list. I hope this helps.

Update

Here is sample code to solve your problem. graph.neighbors(node) will give a list of neighbors for the node.

 import networkx as nx
import pandas as pd
G = nx.path_graph(5)
Distance=[]
edge0=[]
neighbor0edge0=[]
neighbor1edge1=[]
edge1=[]
Output=pd.DataFrame()
for edge in G.edges():
    neighbor1=[n for n in G.neighbors(edge[0])] #neighborrs w.r.t v1
    neighbor2=[n for n in G.neighbors(edge[1])] #neighborrs w.r.t v2
    distance=[]
    for i in neighbor1:
        for j in neighbor2:
              distance.append(nx.shortest_path_length(G, source=i, target=j)) #Find distance between all the combination of neighbor1 and neighbor2
    edge0.append(edge[0])
    edge1.append(edge[1])
    Distance.append(distance)
    neighbor0edge0.append(neighbor1)
    neighbor1edge1.append(neighbor2)
Output['v1']=edge0
Output['neighborv1']=neighbor0edge0
Output['v2']=edge1
Output['neighborv2']=neighbor1edge1
Output['Distances']=Distance

Result:-

`v1 neighborv1  v2 neighborv2     Distances
 0        [1]   1     [0, 2]        [1, 1]
 1     [0, 2]   2     [1, 3]  [1, 3, 1, 1]
 2     [1, 3]   3     [2, 4]  [1, 3, 1, 1]
 3     [2, 4]   4        [3]        [1, 1]`