24

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.

LondonRob
  • 53,478
  • 30
  • 110
  • 152
  • Strictly speaking, [adjacency matrices](http://en.wikipedia.org/wiki/Adjacency_matrix) are boolean. What do the values in `a_numpy` actually mean? Are they connection weights? – ali_m Apr 15 '15 at 17:21
  • @ali_m I meant them to mean edge weights. – LondonRob Apr 15 '15 at 17:45

3 Answers3

34

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
RickardSjogren
  • 3,541
  • 2
  • 14
  • 25
  • 1
    Perfect! +1 for the fact that `Adjacency` interprets numbers as a number of links, not the link weight. – LondonRob Apr 16 '15 at 15:03
  • 1
    How would you convert this graph to a directed version if the rows were the 'from' nodes and columns were the 'to' nodes. – sachinruk May 19 '16 at 04:29
  • `a.to_numpy()` [is preferred to](https://stackoverflow.com/a/54508052/6509615) `a.values` – rlchqrd Jan 13 '21 at 17:19
14

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)

enter image description here

ali_m
  • 62,795
  • 16
  • 193
  • 270
1

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
rlchqrd
  • 329
  • 1
  • 11