igraph Graph from numpy or pandas adjacency matrix - python

I have an adjacency matrix stored as a pandas.DataFrame:
node_names = ['A', 'B', 'C']
a = pd.DataFrame([[1,2,3],[3,1,1],[4,0,2]],
index=node_names, columns=node_names)
a_numpy = a.as_matrix()
I'd like to create an igraph.Graph from either the pandas or the numpy adjacency matrices. In an ideal world the nodes would be named as expected.
Is this possible? The tutorial seems to be silent on the issue.

In igraph you can use igraph.Graph.Adjacency to create a graph from an adjacency matrix without having to use zip. There are some things to be aware of when a weighted adjacency matrix is used and stored in a np.array or pd.DataFrame.
igraph.Graph.Adjacency can't take an np.array as argument, but that is easily solved using tolist.
Integers in adjacency-matrix are interpreted as number of edges between nodes rather than weights, solved by using adjacency as boolean.
An example of how to do it:
import igraph
import pandas as pd
node_names = ['A', 'B', 'C']
a = pd.DataFrame([[1,2,3],[3,1,1],[4,0,2]], index=node_names, columns=node_names)
# Get the values as np.array, it's more convenenient.
A = a.values
# Create graph, A.astype(bool).tolist() or (A / A).tolist() can also be used.
g = igraph.Graph.Adjacency((A > 0).tolist())
# Add edge weights and node labels.
g.es['weight'] = A[A.nonzero()]
g.vs['label'] = node_names # or a.index/a.columns
You can reconstruct your adjacency dataframe using get_adjacency by:
df_from_g = pd.DataFrame(g.get_adjacency(attribute='weight').data,
columns=g.vs['label'], index=g.vs['label'])
(df_from_g == a).all().all() # --> True

Strictly speaking, an adjacency matrix is boolean, with 1 indicating the presence of a connection and 0 indicating the absence. Since many of the values in your a_numpy matrix are > 1, I will assume that they correspond to edge weights in your graph.
import igraph
# get the row, col indices of the non-zero elements in your adjacency matrix
conn_indices = np.where(a_numpy)
# get the weights corresponding to these indices
weights = a_numpy[conn_indices]
# a sequence of (i, j) tuples, each corresponding to an edge from i -> j
edges = zip(*conn_indices)
# initialize the graph from the edge sequence
G = igraph.Graph(edges=edges, directed=True)
# assign node names and weights to be attributes of the vertices and edges
# respectively
G.vs['label'] = node_names
G.es['weight'] = weights
# I will also assign the weights to the 'width' attribute of the edges. this
# means that igraph.plot will set the line thicknesses according to the edge
# weights
G.es['width'] = weights
# plot the graph, just for fun
igraph.plot(G, layout="rt", labels=True, margin=80)

This is possible with igraph.Graph.Weighted_Adjacency as
g = igraph.Graph.Weighted_Adjacency(a.to_numpy().tolist())
pandas.DataFrame.as_matrix has been deprecated,
so pandas.DataFrame.to_numpy should be used instead.
Additionally the numpy.ndarray given by a.to_numpy() must be converted to a list with tolist() before being passed to Weighted_Adjacency.
The node names can be stored as another attribute with
g.vs['name'] = node_names

Related

How to get Minimum Spanning Tree Matrix in python

Initially, i have 2d array. By using this array i have created a graph with weight on its edges. Now i am trying to use this Graph to make Minimum Spanning Tree matrix but i cant make it as desire. I am using the following code to make graph.
G = nx.from_numpy_matrix(ED_Matrix, create_using=nx.DiGraph)
layout = nx.spring_layout(G)
sizes = len(ED_Matrix)
nx.draw(G, layout, with_labels=True, node_size=sizes)
labels = nx.get_edge_attributes(G, "weight")
output = nx.draw_networkx_edge_labels(G, pos=layout, edge_labels=labels)
plt.show()
And its gives the output like this
Now i am using MST code, to get the its MST matrix but its gives error like this.
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import minimum_spanning_tree
Tcsr = minimum_spanning_tree(G)
Tcsr.toarray().astype(int)
Taking into account example from docs of scipy, it should be constructed from adjacency matrix of G, not from G.
You might want to replace G with nx.adjacency_matrix(G) or csr_matrix(nx.adjacency_matrix(G)) or ED_Matrix itself in calculation (assignment) of Tcsr:
Tcsr = minimum_spanning_tree(nx.adjacency_matrix(G)) #or
Tcsr = minimum_spanning_tree(csr_matrix(nx.adjacency_matrix(G))) #or
Tcsr = minimum_spanning_tree(ED_Matrix)
Tcsr is a sparse matrix which is later converted to numpy array.

Reverse Cuthill Mckee in Python

How can i use the permutation array returned by Scipy RCM, to reorder the original sparse matrix and reduce the bandwidth?
B = mmread('G22.mtx')
graph = csr_matrix(B)
aux2 = reverse_cuthill_mckee(graph,symmetric_mode=True)
Where 'graph' is a undirected graph(symmetric matrix).
I found the answer, if anyone needs it in the future:
B = mmread('G22.mtx')
graph = csr_matrix(B)
aux2 = reverse_cuthill_mckee(graph,symmetric_mode=True)
for i in range(len(aux2)):
graph[:,i] = graph[aux2,i]
for i in range(len(aux2)):
graph[i,:] = graph[i,aux2]

Python NetworkX: How to access edges with a specific data value

For a bond percolation model I want to build a square lattice with NetworkX using grid_2d_graph(l,l). This gives me a square lattice of size lxl with every edge open.
The idea is that I want to pick an edge of the graph randomly, then check if the edge has already been assigned (1 to leave the edge as it is, 0 to add it to the list of edges to remove from the graph) and if it hasn't been assigned yet (edge has 'state' = -1), I want to randomly choose with a specific probability p, if the edge is open (leave it as it is), or if it is closed (put it on the list of the edges to remove).
Therefor, I saved all edges with data attribute 'state' = -1 as a list and then tried to randomly access an entry of this list to then change the attribute 'state' to some value. But it seems that this operation is not allowed. When I try to edit the states, I receive the following error:
File "bond-percolation.py", line 39, in <module>
ed[10][2] = 1
TypeError: 'tuple' object does not support item assignment
So my question is, how can I randomly pick an edge and change the value of 'state' efficiently?
Here is my code:
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import random
#Width of the quadratic lattice
l = 30
#Create grid
G = nx.grid_2d_graph(l,l)
#Total number of edges in the lattice
n = 2 * l * (l-1)
m = 0
#Set probability if an edge is open
p = 0.17
#Create empty list to add closed edges later
ed = []
ld = []
for e in G.edges(data = 'state', default = -1):
ed.append(e)
#Creating the lattice
while (m != n):
i = np.random.randint(n-1)
a = random.random()
if (ed[i][2] == -1):
if (a > p):
ld.append(ed[i])
else:
ed[i][2] = 1
m = m + 1
#We need this so that the lattice is drawn vertically to the horizon
pos = dict( (l,l) for l in G.nodes() )
#Draw the lattice
nx.draw_networkx(G, pos = pos, with_labels = False, node_size = 0)
#Plot it on the screen
plt.axis('off')
plt.show()
I believe you can simply search for it using the edge selector.
There's no built-in selector (afaik) but you can create a helper function that loops through the edges and returns your list.
def filter_edges(value):
edge_list = []
for u,v,s in G.edges(data='state'):
if s == value:
edge_list.append((u,v))
return edge_list
Re-reading your error, I don't think your error is related to randomly picking edges. Instead, you are incorrectly trying to assign the state values.
ed[10][2] returns the whole edge (presumably a dict). It'd be helpful to include the output when you just run ed[10][2].
You can't assign an int to that. You likely want to do ed[10][2]['state'] = 1

Node size dependent on the node degree on NetworkX

I imported my Facebook data onto my computer in the form of a .json file. The data is in the format:
{"nodes":[{"name":"Alan"},{"name":"Bob"}],"links":[{"source":0,"target:1"}]}
Then, I use this function:
def parse_graph(filename):
"""
Returns networkx graph object of facebook
social network in json format
"""
G = nx.Graph()
json_data=open(filename)
data = json.load(json_data)
# The nodes represent the names of the respective people
# See networkx documentation for information on add_* functions
G.add_nodes_from([n['name'] for n in data['nodes']])
G.add_edges_from([(data['nodes'][e['source']]['name'],data['nodes'][e['target']]['name']) for e in data['links']])
json_data.close()
return G
to enable this .json file to be used a graph on NetworkX. If I find the degree of the nodes, the only method I know how to use is:
degree = nx.degree(p)
Where p is the graph of all my friends. Now, I want to plot the graph such that the size of the node is the same as the degree of that node. How do I do this?
Using:
nx.draw(G,node_size=degree)
didn't work and I can't think of another method.
Update for those using networkx 2.x
The API has changed from v1.x to v2.x. networkx.degree no longer returns a dict but a DegreeView Object as per the documentation.
There is a guide for migrating from 1.x to 2.x here.
In this case it basically boils down to using dict(g.degree) instead of d = nx.degree(g).
The updated code looks like this:
import networkx as nx
import matplotlib.pyplot as plt
g = nx.Graph()
g.add_edges_from([(1,2), (2,3), (2,4), (3,4)])
d = dict(g.degree)
nx.draw(g, nodelist=d.keys(), node_size=[v * 100 for v in d.values()])
plt.show()
nx.degree(p) returns a dict while the node_size keywod argument needs a scalar or an array of sizes. You can use the dict nx.degree returns like this:
import networkx as nx
import matplotlib.pyplot as plt
g = nx.Graph()
g.add_edges_from([(1,2), (2,3), (2,4), (3,4)])
d = nx.degree(g)
nx.draw(g, nodelist=d.keys(), node_size=[v * 100 for v in d.values()])
plt.show()
#miles82 provided a great answer. However, if you've already added the nodes to your graph using something like G.add_nodes_from(nodes), then I found that d = nx.degree(G) may not return the degrees in the same order as your nodes.
Building off the previous answer, you can modify the solution slightly to ensure the degrees are in the correct order:
d = nx.degree(G)
d = [(d[node]+1) * 20 for node in G.nodes()]
Note the d[node]+1, which will be sure that nodes of degree zero are added to the chart.
other method if you still get 'DiDegreeView' object has no attribute 'keys'
1)you can first get the degree of each node as a list of tuples
2)build a node list from the first value of tuple and degree list from the second value of tuple.
3)finally draw the network with the node list you've created and degree list you've created
here's the code:
list_degree=list(G.degree()) #this will return a list of tuples each tuple is(node,deg)
nodes , degree = map(list, zip(*list_degree)) #build a node list and corresponding degree list
plt.figure(figsize=(20,10))
nx.draw(G, nodelist=nodes, node_size=[(v * 5)+1 for v in degree])
plt.show() #ploting the graph

How can I get the (x,y) values of the line that is plotted by a contour plot?

Is there an easy way to get the (x,y) values of a contour line that was plotted like this:
import matplotlib.pyplot as plt
x = [1,2,3,4]
y = [1,2,3,4]
m = [[15,14,13,12],[14,12,10,8],[13,10,7,4],[12,8,4,0]]
cs = plt.contour(x,y,m, [9.5])
plt.show()
Look at the collections property of the returned ContourSet. In particular the get_paths() method of the first collection returns paired points making up each line segment.
cs.collections[0].get_paths()
To get a NumPy array of the coordinates, use the Path.vertices attribute.
p1 = cs.collections[0].get_paths()[0] # grab the 1st path
coor_p1 = p1.vertices
Going through the collections and extracting the paths and vertices is not the most straight forward or fastest thing to do. The returned Contour object actually has attributes for the segments via cs.allsegs, which returns a nested list of shape [level][element][vertex_coord]:
num_levels = len(cs.allsegs)
num_element = len(cs.allsegs[0]) # in level 0
num_vertices = len(cs.allsegs[0][0]) # of element 0, in level 0
num_coord = len(cs.allsegs[0][0][0]) # of vertex 0, in element 0, in level 0
Hence the vertices of an all paths can be extracted as:
cs.allsegs[i][j] # for element j, in level i
See reference:
https://matplotlib.org/3.1.1/api/contour_api.html

Categories

Resources