scona is a tool to perform network analysis over correlation networks of brain regions. This tutorial will go through the basic functionality of scona, taking us from our inputs (a matrix of structural regional measures over subjects) to a report of local network measures for each brain region, and network level comparisons to a cohort of random graphs of the same degree.
In [2]:
import numpy as np
import networkx as nx
import scona as scn
import scona.datasets as datasets
A scona analysis starts with four inputs.
In [3]:
# Read in sample data from the NSPN WhitakerVertes PNAS 2016 paper.
df, names, covars, centroids = datasets.NSPN_WhitakerVertes_PNAS2016.import_data()
In [4]:
df.head()
Out[4]:
In [5]:
df_res = scn.create_residuals_df(df, names, covars)
In [6]:
df_res
Out[6]:
Now we create a correlation matrix over the columns of df_res
In [7]:
M = scn.create_corrmat(df_res, method='pearson')
A short sidenote on the BrainNetwork class: This is a very lightweight subclass of the Networkx.Graph
class. This means that any methods you can use on a Networkx.Graph
object can also be used on a BrainNetwork
object, although the reverse is not true. We have added various methods which allow us to keep track of measures that have already been calculated, which, especially later on when one is dealing with 10^3 random graphs, saves a lot of time.
All scona measures are implemented in such a way that they can be used on a regular Networkx.Graph
object. For example, instead of G.threshold(10)
you can use scn.threshold_graph(G, 10)
.
Also you can create a BrainNetwork
from a Networkx.Graph
G
, using scn.BrainNetwork(network=G)
Initialise a weighted graph G
from the correlation matrix M
. The parcellation
and centroids
arguments are used to label nodes with names and coordinates respectively.
In [8]:
G = scn.BrainNetwork(network=M, parcellation=names, centroids=centroids)
We threshold G at cost 10 to create a binary graph with 10% as many edges as the complete graph G. Ordinarily when thresholding one takes the 10% of edges with the highest weight. In our case, because we want the resulting graph to be connected, we calculate a minimum spanning tree first. If you want to omit this step, you can pass the argument mst=False
to threshold
.
The threshold method does not edit objects inplace
In [9]:
H = G.threshold(10)
calculate_nodal_measures
will compute and record the following nodal measures
export_nodal_measure
returns nodal attributes in a DataFrame. Let's try it now.
In [11]:
H.report_nodal_measures().head()
Out[11]:
Use calculate_nodal_measures
to fill in a bunch of nodal measures
In [12]:
H.calculate_nodal_measures()
In [14]:
H.report_nodal_measures().head()
Out[14]:
We can also add measures as one might normally add nodal attributes to a networkx graph
In [15]:
nx.set_node_attributes(H, name="hat", values={x: x**2 for x in H.nodes})
These show up in our DataFrame too
In [17]:
H.report_nodal_measures(columns=['name', 'degree', 'hat']).head()
Out[17]:
In [18]:
H.calculate_global_measures()
Out[18]:
In [20]:
H.rich_club();
In [21]:
brain_bundle = scn.GraphBundle([H], ['NSPN_cost=10'])
This creates a dictionary-like object with BrainNetwork H
keyed by 'NSPN_cost=10'
In [22]:
brain_bundle
Out[22]:
Now add a series of random_graphs created by edge swap randomisation of H (keyed by 'NSPN_cost=10'
)
In [23]:
# Note that 10 is not usually a sufficient number of random graphs to do meaningful analysis,
# it is used here for time considerations
brain_bundle.create_random_graphs('NSPN_cost=10', 10)
In [24]:
brain_bundle
Out[24]:
In [25]:
brain_bundle.report_global_measures()
Out[25]:
In [26]:
brain_bundle.report_rich_club()
Out[26]: