Search code examples
pythonnetworkxadjacency-matrix

Networkx - create a multilayer network from two adjacent matrices


I have two adjacent matrices that represent two brain structures (cerebellum and cortex):

Dataset:

import networkx as nx
from astropy.io import fits

# Cerebellum
with fits.open('matrix_CEREBELLUM_large.fits') as data:
    matrix_cerebellum = pd.DataFrame(data[0].data.byteswap().newbyteorder())
    # 1858 rows × 1858 columns
# Cortex    
with fits.open('matrix_CORTEX_large.fits') as data:
    matrix_cortex = pd.DataFrame(data[0].data.byteswap().newbyteorder())
    #1464 rows × 1464 columns

Note: datasets can be downloaded here: brain datasets

Adjacent matrices

Adjacent matrices here are not weighted, and have the usual binary representation, with 1 value for connected nodes and 0 otherwise, like so:

0 1 0 1 0 0 0 0 0 0 0 ...
0 0 0 1 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 1 ...

I'm using the library Networkx to look for community detection in the networks. I could try to do that for each network, individually.

Simulation

Let's say I need to simulate the real world networks, where a fraction of cortex nodes ( say, 0.01%) projects edges into cerebellum.

I'm wondering how I could implement this simulation considering my community detection goal.

Approaches

I initially though about creating a bipartite network, but decided instead to use a multilayer network (2 layers, actually) approach.

In this approach, cortex would be network layer 1, cerebellum would be network layer 2, each one with intra-connections already represented in each adjacent matrix.

Now I would add the cortex projections as inter-connections between the two layers.

Question

How do I create these projections and represent the new matrix, knowing that I need to:

  1. start from my adjacent matrices
  2. keep their intra-connectivity mappings
  3. add a new mapping for the intermediate layer

Solution

  • Here's a way to do what you want:

    1. First after loading your adjacency matrices to pandas you can convert them to two different graphs with nx.convert_matrix.from_pandas_adjacency
    2. You can then join the two graph into a single graph by using nx.disjoint_union. The nodes of both graphs are basically concatenated onto a single graph (see more here).
    3. Once you have the full graph, you can randomly draw nodes from the cortex part of the full graph with a 0.01 probability.
    4. Similarly you can draw the same number of nodes on the cerebellum part of the graph to act as recipients of the connection.
    5. Finally you can create the edges between the chosen nodes on both sides.
    6. And you can get your adjacency matrix from the final graph by using adj_matrix_full=nx.linalg.graphmatrix.adjacency_matrix(full_g,weight=None)

    See full code below for more details:

    import networkx as nx
    from astropy.io import fits
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    
    fig=plt.figure(figsize=(20,20))
    # Cerebellum
    with fits.open('matrix_CEREBELLUM_large.fits') as data:
      matrix_cerebellum = pd.DataFrame(data[0].data.byteswap().newbyteorder())
        # 1858 rows × 1858 columns
    # Cortex    
    with fits.open('matrix_CORTEX_large.fits') as data:
      matrix_cortex = pd.DataFrame(data[0].data.byteswap().newbyteorder())
        #1464 rows × 1464 columns
    
    cerebellum_g=nx.convert_matrix.from_pandas_adjacency(matrix_cerebellum) #convert cerebellum adj matrix to nx graph
    N_nodes_cer=cerebellum_g.number_of_nodes()
    cortex_g=nx.convert_matrix.from_pandas_adjacency(matrix_cortex) #convert matrix adj matrix to nx graph
    N_nodes_cort=cortex_g.number_of_nodes()
    full_g=nx.algorithms.operators.binary.disjoint_union(cortex_g,cerebellum_g) #concatenate the two graphs
    
    #choose randomly 0.01 cortex nodes to project to cerebellum
    p=0.01
    N_projs=int(cortex_g.number_of_nodes()*p)
    cortex_proj_nodes=np.random.choice(cortex_g.number_of_nodes(), size=N_projs,replace=False)
    cerebellum_recipient=np.random.choice(cerebellum_g.number_of_nodes(), size=N_projs,replace=False)
    
    #Add edges
    for i in range(N_projs):
      full_g.add_edge(list(full_g.nodes)[cortex_proj_nodes[i]],list(full_g.nodes)[N_nodes_cort+cerebellum_recipient[i]])
    
    #Color the nodes based on brain region
    color_map = []
    for node in full_g:
        if node < N_nodes_cort:
            color_map.append('tab:blue')
        else: 
            color_map.append('tab:orange')
    
    adj_matrix_full=nx.linalg.graphmatrix.adjacency_matrix(full_g,weight=None) #Compute adj matrix for full graph
    
    pos = nx.circular_layout(full_g)
    #Setting up a legend
    plt.plot([],[],color='tab:blue',label='Cortex')
    plt.plot([],[],color='tab:orange',label='Cerebellum')
    #plotting graph
    nx.draw(full_g,pos=pos,node_color=color_map)
    plt.legend()
    

    And the output gives: enter image description here