igraph plot for multiple layers partition does not return expected output

Assume there are 3 time slices (i.e., G_1, G_2, G_3) that converted to layers via time_slices_to_layers. These layers and inter slices were then subject to find_partition to get their communities. However, plotting these partition does not result the expected output.

However, plotting the partition result give a incorrect intraslice link connection.

May I know how to fix this issue.

I suspect this is due to wrongly assign the vertex_label

ig.plot(partition,vertex_label = ['A','B','C','D','E','F','G'])

or it can be related to other configuration ?

The complete code to reproduce the above graph

import leidenalg as la
import igraph as ig
import numpy as np


A1 = np.array ( [[0., 0., 0., 0., 0,0,0],[5., 0., 0., 0., 0,0,0],[1., 0., 0., 0., 0,0,0],
                 [0., 1., 2., 0., 0,0,0],[0., 0., 0., 1., 0,0,0], [0., 0., 0., 1., 0,0,0],
                 [0., 0., 0., 0., 1,1,0]] )


A2 = np.array ( [[0., 0., 0., 0., 0,0,0],[5., 0., 0., 0., 0,0,0], [1., 0., 0., 0., 0,0,0],
                 [0., 1., 2., 0., 0,0,0], [0., 0., 0., 1., 0,0,0],[0., 0., 1., 1., 0,0,0],
                 [0., 0., 0., 0., 1,1,0]] )

A3 = np.array ( [[0., 0., 0., 0., 0,0,0], [0., 0., 0., 0., 0,0,0],[0., 0., 0., 0., 0,0,0],
                 [0., 1., 2., 0., 0,0,0],[0., 0., 0., 1., 0,0,0],[0., 0., 0., 1., 0,0,0],
                 [0., 0., 0., 0., 1,1,0]] )


G_1 = ig.Graph.Weighted_Adjacency ( A1.tolist () )
G_2 = ig.Graph.Weighted_Adjacency ( A2.tolist () )
G_3 = ig.Graph.Weighted_Adjacency ( A3.tolist () )
G_1.vs ['id'] = ['A','B','C','D','E','F','G']
G_2.vs ['id'] = ['A','B','C','D','E','F','G']
G_3.vs ['id'] = ['A','B','C','D','E','F','G']

G_layers, G_interslice, G = la.time_slices_to_layers ( [G_1, G_2, G_3], interslice_weight=1,
                                                       slice_attr='slice', vertex_id_attr='id',
                                                       edge_type_attr='type', weight_attr='weight' )

partition = la.find_partition(G, la.CPMVertexPartition,resolution_parameter = 0.05);
ig.plot(partition,vertex_label = ['A','B','C','D','E','F','G'])

P.S., Similar post has been published at SO: python - igraph plot for multiple layers partition does not return expected output - Stack Overflow

Could you indicate what output you would expect exactly?

Please note that this may be incorrect. You try to detect clusters in the overall graph containing both all layers and the interslice coupling links. You typically want to use a resolution parameter of 0 for the interslice coupling links however. Please see the documentation and the included example for more details. Note that there’s also the simpler find_partition_temporal, which does most of this stuff automatically correctly.

I’m not sure exactly what the expected output would be, but in principle this might not give correct results. The partition is defined on a graph with 3 * 7 = 21 nodes, and the vertex_label that you provide just has 7 entries. For some reason, the entries seem to be recycled across all nodes, but I’m not 100% sure whether the labels refer to the “correct” label always. Instead, you can use

ig.plot(partition, vertex_label = G.vs['id'])

which is guaranteed to have the correct labels.

Hi @vtraag, thanks for responding.

Sorry for the lack of information provided, but previously I had to trim some content (i.e., image) due to the new member requirement.

Actually, I desire to reproduce the figure in the slice to layers example (describe in leidenalg-multiplex section), as shown below

Here.the node interaction for each of the 3 time slices are maintained and were map using the numpy array (A1,A2,A3 -in the code provided above)

Given that I would like to reproduce the above figure, I assume we need to time_slices_to_layers rather than directly used find_partition_temporal as you suggested. Hence, I change the code into


gamma=0
optimiser = la.Optimiser()
layers, interslice, G = la.time_slices_to_layers ( [G_1, G_2, G_3], interslice_weight=1)

partitions = [la.CPMVertexPartition(H, node_sizes='node_size',weights='weight',
                                    resolution_parameter=gamma) for H in layers];

interslice_partition = la.CPMVertexPartition(interslice, resolution_parameter=gamma,
                                             node_sizes='node_size', weights='weight');

diff = optimiser.optimise_partition_multiplex(partitions + [interslice_partition]);

ig.plot(diff,vertex_label = G.vs['id'])

However, ig.plot(diff,vertex_label = G.vs['id']) or ig.plot(diff) return a white blank figure.

The full code are provided below.

p.s. I cannot hide my excitement as the author himself reply this OP :heart_eyes: :heart_eyes: :star_struck:


import leidenalg as la
import igraph as ig
import numpy as np


A1 = np.array ( [[0., 0., 0., 0., 0,0,0],[5., 0., 0., 0., 0,0,0],[1., 0., 0., 0., 0,0,0],
                 [0., 1., 2., 0., 0,0,0],[0., 0., 0., 1., 0,0,0], [0., 0., 0., 1., 0,0,0],
                 [0., 0., 0., 0., 1,1,0]] )


A2 = np.array ( [[0., 0., 0., 0., 0,0,0],[5., 0., 0., 0., 0,0,0], [1., 0., 0., 0., 0,0,0],
                 [0., 1., 2., 0., 0,0,0], [0., 0., 0., 1., 0,0,0],[0., 0., 1., 1., 0,0,0],
                 [0., 0., 0., 0., 1,1,0]] )

A3 = np.array ( [[0., 0., 0., 0., 0,0,0], [0., 0., 0., 0., 0,0,0],[0., 0., 0., 0., 0,0,0],
                 [0., 1., 2., 0., 0,0,0],[0., 0., 0., 1., 0,0,0],[0., 0., 0., 1., 0,0,0],
                 [0., 0., 0., 0., 1,1,0]] )


G_1 = ig.Graph.Weighted_Adjacency ( A1.tolist () )
G_2 = ig.Graph.Weighted_Adjacency ( A2.tolist () )
G_3 = ig.Graph.Weighted_Adjacency ( A3.tolist () )
G_1.vs ['id'] = ['A','B','C','D','E','F','G']
G_2.vs ['id'] = ['A','B','C','D','E','F','G']
G_3.vs ['id'] = ['A','B','C','D','E','F','G']

gamma=0
optimiser = la.Optimiser()
layers, interslice, G = la.time_slices_to_layers ( [G_1, G_2, G_3], interslice_weight=1)

partitions = [la.CPMVertexPartition(H, node_sizes='node_size',weights='weight',
                                    resolution_parameter=gamma) for H in layers];

interslice_partition = la.CPMVertexPartition(interslice, resolution_parameter=gamma,
                                             node_sizes='node_size', weights='weight');

diff = optimiser.optimise_partition_multiplex(partitions + [interslice_partition]);

ig.plot(diff,vertex_label = G.vs['id'])

OK, right, I understand. Note that this is just an illustrative example, but you can surely toy around with it to see how the package works.

Here diff is the difference in the quality function that results from the optimisation, which is not a graph itself. Hence, it does not show anything.

There are a couple of more comments that might make it easier to work with. Creating the graphs can be substantially simplified using Formula:

# Create first slice graph
G1 = ig.Graph.Formula('A-B-D-C-A, D-E-F-G-D')

# Make a copy, remove edge between C and F
G2 = G1.copy(); G2.add_edge('C', 'F')

# Make a copy, remove vertex A
G3 = G1.copy(); G3.delete_vertices('A')

We can then indeed use the conversion using time_slices_to_layers and create the partitions

# Convert from slices to layers
G_layers, G_interslice, G = la.time_slices_to_layers([G1, G2, G3], interslice_weight=1e-1,
                                                     slice_attr='slice', vertex_id_attr='name',
                                                     edge_type_attr='type', weight_attr='weight' )

# Create partitions
gamma = 0.3
partitions = [la.CPMVertexPartition(H, node_sizes='node_size',weights='weight',
                                    resolution_parameter=gamma) for H in G_layers]
interslice_partition = la.CPMVertexPartition(G_interslice, resolution_parameter=0,
                                             node_sizes='node_size', weights='weight')

Note that an interslice_weight of 1 might be a bit high here, typically you can set that relatively low.

As you did, you can now detect the communities using

# Detect communities
optimiser = la.Optimiser()
optimiser.set_rng_seed(11)
diff = optimiser.optimise_partition_multiplex(partitions + [interslice_partition])

Here I set the RNG seed explicitly, so that you are able to reproduce the results. Different seeds may yield different results, this seed was chosen to reproduce the illustrative example.

If you now want to plot the results, you can use

# Plot network
partition_all = ig.VertexClustering(G, partitions[0].membership)
ig.plot(partition_all,
        vertex_label = [f'{v["name"]}-{v["slice"]}' for v in G.vs])

The first line constructs a general igraph VertexClustering that contains the membership for the overall graph (i.e. including both the slices and the interslice coupling links). The result is then

Thanks for the detail explanation @vtraag

Maybe unintended, but I notice there are some typo.

  • Missing (

Should be

G_layers, G_interslice, G = la.time_slices_to_layers([G1, G2, G3], interslice_weight=1e-1,
                                                     slice_attr='slice', vertex_id_attr='name')
  • Oulier > edge_type_attr='type', weight_attr='weight')

Should be


interslice_partition = la.CPMVertexPartition(G_interslice, resolution_parameter=0,
                                             node_sizes='node_size', weights='weight')

Amending these two lines may successfully produce the graph as shown above

Thanks for introducing Formula. This indeed an elegant way of constructing a graph.

But I believe, the connection to reproduce the original diagram should be


G1 = ig.Graph.Formula('A-B-D-C-A, D-E-G-F-D')

For future reader who might be interested with input framed from numpy array, below is the complete code based on @vtraag suggestion.
Please note, in the code below we used id instead of name


import leidenalg as la
import igraph as ig
import numpy as np

A1 = np.array ( [[0., 0., 0., 0., 0, 0, 0], [5., 0., 0., 0., 0, 0, 0], [1., 0., 0., 0., 0, 0, 0],
                 [0., 1., 2., 0., 0, 0, 0], [0., 0., 0., 1., 0, 0, 0], [0., 0., 0., 1., 0, 0, 0],
                 [0., 0., 0., 0., 1, 1, 0]] )

A2 = np.array ( [[0., 0., 0., 0., 0, 0, 0], [5., 0., 0., 0., 0, 0, 0], [1., 0., 0., 0., 0, 0, 0],
                 [0., 1., 2., 0., 0, 0, 0], [0., 0., 0., 1., 0, 0, 0], [0., 0., 1., 1., 0, 0, 0],
                 [0., 0., 0., 0., 1, 1, 0]] )

A3 = np.array ( [[0., 0., 0., 0., 0, 0, 0], [0., 0., 0., 0., 0, 0, 0], [0., 0., 0., 0., 0, 0, 0],
                 [0., 1., 2., 0., 0, 0, 0], [0., 0., 0., 1., 0, 0, 0], [0., 0., 0., 1., 0, 0, 0],
                 [0., 0., 0., 0., 1, 1, 0]] )

G_1 = ig.Graph.Weighted_Adjacency ( A1.tolist () )
G_2 = ig.Graph.Weighted_Adjacency ( A2.tolist () )
G_3 = ig.Graph.Weighted_Adjacency ( A3.tolist () )
G_1.vs ['id'] = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
G_2.vs ['id'] = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
G_3.vs ['id'] = ['A', 'B', 'C', 'D', 'E', 'F', 'G']

# Convert from slices to layers
# Note here, the slice', vertex_id_attr is assigned to `id` since we define it as id at the previous step
G_layers, G_interslice, G = la.time_slices_to_layers([G_1, G_2, G_3], interslice_weight=1e-1,
                                                     slice_attr='slice', vertex_id_attr='id',
           edge_type_attr='type', weight_attr='weight')
# Create partitions
gamma = 0.3
partitions = [la.CPMVertexPartition(H, node_sizes='node_size',weights='weight',
                                    resolution_parameter=gamma) for H in G_layers]
interslice_partition = la.CPMVertexPartition(G_interslice, resolution_parameter=0,
                                             node_sizes='node_size', weights='weight')


# Detect communities
optimiser = la.Optimiser()
optimiser.set_rng_seed(11)
diff = optimiser.optimise_partition_multiplex(partitions + [interslice_partition])


# Plot network
# Note here, the`v` is assigned to `id` since we define it as id at the previous step
partition_all = ig.VertexClustering(G, partitions[0].membership)
ig.plot(partition_all,
        vertex_label = [f'{v["id"]}-{v["slice"]}' for v in G.vs])

Which produced

For visual learner, I had manually label the edge. The label color black, blue and red represent the slice G_1, G_2, and G_3, respectively.

Thanks for spotting this! It was a copy-paste error from my code to the forum. I’ve corrected it now.

1 Like

Hi @vtraag , maybe you did not get the notification. But, I did sent a private discourse message you for follow-up question from this post. Appreciate if you can shed some light as I am unable to move forward without the verification from you.

Yes, you are right, the circle is D, E, G, F, D in that picture, I wrote that too quickly.

Do note that in your numpy style example, the node A is always included, also in G_3. That is not the case in the example.

I’ll respond to the private discourse message. I do think that these could be discussed in general, so that others can also read along and contribute to the discussion, or learn from it.

1 Like