Interactive visualisation tutorial

In the introductory_tutorial we ran through building structural covariance network analyses using scona🍪, in the global_network_viz_tutorial we looked at how to report measures relating to the whole network, and in the anatomical_viz_tutorial we made some static visualisions that you could use to report your findings in a published paper.

In this tutorial we'll cover some interactive and 3D connectome visualisation commands.

You'll be able to share these with your final publication for your readers to explore.

Click on any of the links below to jump to that section

Get set up

Import the modules you need


In [ ]:
import scona as scn
import scona.datasets as datasets
import numpy as np
import networkx as nx
import pandas as pd
from IPython.display import display

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

%load_ext autoreload
%autoreload 2

Read in the data, build a network and calculate the network metrics

If you're not sure about this step, please check out the introductory_tutorial notebook for more explanation.


In [ ]:
# Read in sample data from the NSPN WhitakerVertes PNAS 2016 paper.
df, names, covars, centroids = datasets.NSPN_WhitakerVertes_PNAS2016.import_data()

# calculate residuals of the matrix df for the columns of names
df_res = scn.create_residuals_df(df, names, covars)

# create a correlation matrix over the columns of df_res
M = scn.create_corrmat(df_res, method='pearson')

# Initialise a weighted graph G from the correlation matrix M
G = scn.BrainNetwork(network=M, parcellation=names, centroids=centroids)

# Threshold G at cost 10 to create a binary graph with 10% as many edges as the complete graph G.
G10 = G.threshold(10)

# Calculate global and nodal measures for graph G10
G10.calculate_global_measures()
G10.calculate_nodal_measures()

Visualise nodes in 3D: view_nodes_3d

One of the first things you'll want to do is view the nodes of your network on the brain.

The commands here are lightly wrapping around the excellent nilearn.plotting.view_markers tool.

Look at the data

We'll use the thresholded graph G10 because it has some of the properties we'd like to visualise.

The nodes have specific attributes, or you can see all of the information saved for each node using the report_nodal_measures command.


In [ ]:
# Look at one specific node's attributes
G.nodes[12]

In [ ]:
# show the nodal attributes of first 5 brain regions
G10.report_nodal_measures().loc[:5]

One of the most important columns is the centroids column which has the x, y and z coordinates of the nodal location in MNI space. We'll use those values to put the nodes in the right place on the brain.

Import the code you need: view_nodes_3d


In [ ]:
# import the function to plot network measures
from scona.visualisations import view_nodes_3d

Plot nodes

As it says in the name: lets view the nodes on a 3d plot.

All you need is a graph (we're using G10 in this case) which has the centroids attribute.

Default settings

By default, all nodes will be displayed in black color with the same size value: 5.0.


In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10)
# Show the view interactively
ConnectomeView

Spin the brain around

Have a play around!

You should be able to view the brain from different angles by moving the mouse to rotate the brain. Alternatively you can select one of six prescribed orientations by using the drop down lis in the bottom left corner

Zoom in and out

You can zoom in on the brain either by selecting the 🔍 icon, or using two fingers on the trackpad, and then dragging the mouse.

Move the brain from left to right

You can pan the image from side to side by clicking on the 4 direction arrow icon and then moving the mouse.

Reset the view

Sometimes you can make the brain look particularly odd (very small, very large, or upside down, for example!) The home button (🏠) will rest the view to the default settings, which is a nice way to get back on track.

Adjust the transparency of the brain

You can make the brain on which the nodes are plotted more or less transparent by moving the slider along the horizonal bar in the bottom left corner.

Note that this will not change the colour of the nodes, just the brain in the background.

Save a picture

You can save the brain (in the orientation you've selected) as a png file by clicking the camera icon (📷) in the top right corner. (That menu might not always be visible. It should appear when you hover your mouse in that corner though!)

The image will be saved wherever your browser saves images by default, so if in doubt check your Downloads folder.

Edit the image with plotly

It's a little confusing that the save icon (💾) doesn't save an image (that's the 📷 icon as described above) but rather opens the plot in Plotly:Chart Studio.

Plotly is a little hard to get started with, but once you're up and running it can give you online control over setting up the image.

You can modify and manually plot the nodes location and colors using the data in the top right of the page. And you can pan and set different rotations when you're visualising the brain.

Explore and find what you need! Have fun 😺

Plot nodes with different sizes and change the default color

You might want to adjust the size of the nodes according to their degree: making the most strongly connected nodes larger than those with fewer connections.

The degree is saved in the nodal measures pandas data frame and we can pass that column directly to the view_nodes_3d function under the node_size keyword arguement.

Note that we've changed the node_color to "coral" because black dots are very boring 😉


In [ ]:
# Get the nodal measures as a pandas dataframe
nodal_df = G10.report_nodal_measures()

# Create a view
ConnectomeView = view_nodes_3d(G10, node_color="coral", node_size=nodal_df['degree'])

# Show the view interactively
ConnectomeView

WOAH! That's not what we were expecting!

The circles that mark the nodes are all overlapping. This is not a particularly useful figure.

The reason is that the range of degree values is too large. The smallest dots look great, but the largest dots are taking up too much space.

You can play around with different scalings by dividing the values by a constant and/or adding a constant. You may remember this activity from high school mathematics. If you don't, here's a lovely refresher video from Khan Academy 📐👩‍🔬

For example, for this example dataset, adding 30.0 to every degree value and dividing by 7.0 looks pretty good.


In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10, node_color="coral", node_size=(nodal_df['degree']+30)/7.0)
# Show the view interactively
ConnectomeView

If you want to show a different measure, participation coefficient for example, you'll need to customise the values again.

Play around with the two different options in the cell below by uncommenting one of the lines with information for node_size to contrast the different sizes and their effect on the brain image.

  1. No adjustment (nodes will look very small)
  2. Add a constant (10, nodes will all look very similar in size)
  3. Multiple the values by a constant (20, the nodes in frontal cortex with low participation coefficient look much smaller than those with higher participation coefficient)

None of the node size options are wrong but they do tell quite different stories! Remember to be responsible in the choices you make when you communicate your findings. With great power ... 💪


In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10,
                               node_color="cornflowerblue",
                               #node_size=(nodal_df['participation_coefficient'])
                               #node_size=(nodal_df['participation_coefficient']+10.0)
                               node_size=(nodal_df['participation_coefficient']*20.0)
                              )
# Show the view interactively
ConnectomeView

Show a subset of the nodes

If you set the node size to 0 then it will not be shown on the brain.

A good use case for this is if you want to show the location of just one specific node. Let's say: lh_rostralmiddlefrontal_part1.


In [ ]:
# Create a list that has 0s everwhere except for that one node
sizes = [ 15 
          if node_name == 'lh_rostralmiddlefrontal_part1' 
          else 0 
          for node_name in nodal_df['name'].values ]

print (sizes)

In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10, node_color="Red", node_size=sizes)

# Show the view interactively
ConnectomeView

Plot the rich club only

Another use case is to plot just the rich club nodes.

To do this you need to define a cut off point from the rich club curve (described in more detail in the global_network_viz tutorial). In the published paper that this example data is from we designated the most connected 15% of nodes to be in the rich club.


In [ ]:
# Define the degree threshold that separates the rich club from
# the other nodes
rich_thresh = np.percentile(nodal_df['degree'], 85)
print ('Rich club threshold: {}'.format(rich_thresh))

# Create a list that replaces the degree value with 0 for nodes that are
# not in the rich club
sizes = [ degree 
          if degree > rich_thresh 
          else 0 
          for degree in nodal_df['degree'].values ]

# Print the sizes to show the 0s and values
print (sizes)

# Adjust these values as above (otherwise you'll get some great big circles again!)
sizes = [ (size + 30.0)/7.0 
          if size > 1.0
          else 0
          for size in sizes ]

# Print just the first few entries
print (sizes[:5])

In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10, node_color="teal", node_size=sizes)

# Show the view interactively
ConnectomeView

Plot nodes in different colors

You might not want all your nodes in the same colour.

For example you might want to put each hemisphere in a different colour.

MNI coordinates are set up so that the x coordinate of a node determines whether a node belongs to the left or right hemisphere.

Negative values of x mean that node is in left hemisphere, and positive values are all located in the right hemisphere.


In [ ]:
# Set 2 colors for nodes in the left and right hemispheres
color_dict = {'left' : "#FFD700",
              'right' : "#4169E1"}

# Create a list of colors for each node
node_colors = [ color_dict['left'] if x < 0 else color_dict['right'] for x in nodal_df['x'] ]

# Print every 10th node (to save us from a huge print out!)
print (node_colors[::10])

Do you see that in our case the nodes are ordered so that all the ones in the left hemisphere come first and then those in the right hemisphere?

That's a coincidence of the way this data was stored. It doesn't have to be that way at all. The important information is the x coordinate in the nodal values data frame (nodal_df).


In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10, node_color=node_colors)

# Show the view interactively
ConnectomeView

Fun right? Go Bears! 🐻

Plot nodal measures in different colours using a colourmap

We can grab the nodal values from the nodal_df dataframe, and use a colourmap instead of specifying individual colours for each value.

This example uses a keyword argument we haven't seen yet: measure. Measure looks for columns in the nodal values data frame and uses the information it finds in there to set the colour.

Note that you can't use this syntax (yet?) to change the node size because it is very difficult to automatically set the correct scaling factors (as you saw above!)

Show modules in different colours using a qualitative colourmap

We want the modules to be as different from each other as possible, so in the example below we've chosen the tab10 colourmap from this awesome list of options!


In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10, measure='module', cmap_name='tab10')

# Show the view interactively
ConnectomeView

Visualise continuous data with a range of colours

Set the continuous parameter to True in order to plot nodes according to the continuous data of nodal measures (like "closeness", "betweenness" etc).

In the example below we've shown how closeness varies across the brain.


In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10, 
                               measure="closeness", 
                               cmap_name="autumn",
                               continuous=True)

# Show the view interactively
ConnectomeView

These values all look kinda super similar, and it's probably driven by some nodal values that are particularly high or particularly low.

If you want to adjust the min and max values of the colorbar you can do that with the vmin and vmax parameters.

This usually lets you see a little more variation in the remaining 80% of the data, but be careful that you don't lie about the data as you adjust these values!


In [ ]:
# Set vmin and vmax to be the 10th and 90th percentiles of the data range
vmin = np.percentile(nodal_df['closeness'], 10)
vmax = np.percentile(nodal_df['closeness'], 90)

In [ ]:
# Create a view
ConnectomeView = view_nodes_3d(G10, 
                               measure="closeness", 
                               cmap_name="autumn",
                               continuous=True, 
                               vmin=vmin,
                               vmax=vmax)

# Show the view interactively
ConnectomeView

A note on warnings

You may see the following warning appear in red when you were creating some of the 3D visualisations above.

UserWarning: It seems you have created more than 10 nilearn views.
As each view uses dozens of megabytes of RAM, you might want to delete some of them.
warnings.warn('It seems you have created more than 10')

This is telling you that you're using lots of your browsers memory by opening up so many of these ConnectomeViews.

You can reduce the memory by clicking the "Clear all outputs" option under the "Edit" button in Jupyter Lab. (See the Jupyter Lab documentation for more information about the interface.)

We recommend clearing the outputs every now and again to keep everything clean and tidy 🗑️

In a future version of nilearn we'll be able to turn off this warning through their interface, but for now we're just going to turn it off manually.


In [ ]:
import warnings
warnings.filterwarnings("ignore", category=UserWarning)

Visualise Connectome in 3D: view_connectome_3d

Wow, we've done loads of visualisations with the nodes, but what about the edges of the graph!?

With the scona.view_nodes_3d() tool we were mainly focused to show only nodes and especially node visualisation according to their nodal measures. Whereas view_connectome_3d tool is essentially used to show the connections between nodes.

This function builds off the lovely nilearn.plotting.view_connectome tool.

Look at the data


In [ ]:
# Print the number of edges
print ('Number of edges in original graph: {}'.format(G.number_of_edges()))
print ('Number of edges in thresholded (10%) graph: {}'.format(G10.number_of_edges()))

In [ ]:
# Print the weight of a couple of example edges
print ('Edge between {} and {}'.format(G.node[144]['name'],G.node[300]['name']))
print ('  Weight in original graph: {:2.3f}'.format(G.edges[(144, 300)]['weight']))
print ('  Weight in thresholded (10%) graph: {:2.3f}'.format(G10.edges[(144, 300)]['weight']))

print ('Edge between {} and {}'.format(G.node[256]['name'],G.node[260]['name']))
print ('  Weight in original graph: {:2.3f}'.format(G.edges[(256, 260)]['weight']))
print ('  Weight in thresholded (10%) graph: {:2.3f}'.format(G10.edges[(256, 260)]['weight']))

Use original graph rather than the thresholded graph

As you can see from looking at the data, all the edges in thresholded network are exactly 1.0.

We create a thresholded network and then binarize it (setting weights to 1 or 0) so that we can calculate the global and nodal measures for the graph.

However, if we plotted all those connections (even "only" the 10% we kept for G10) the network would look like a messy spagetti plot! And as the weights are all exactly the same we have no way to select, for example, the top 2% of edges (which generally makes a good looking network).

Therefore, for the view_connectome_3d function its important to pass the original Graph (G) rather than the thresholded graph (G10).

There is a useful inbuilt argument edge_threshold that makes it easier to adjust which edges to show.

Import the code you need: view_connectome_3d


In [ ]:
from scona.visualisations import view_connectome_3d

View Connectome

The view connectome function lets you view the edges - the connections - of the network.

You need an unthresholded graph (we're using G) which has the centroids attribute.

Default settings

By default, all nodes have 3.0 size and the top 2% of edges are displayed.

The color of each edge is chosen based on the weight value of an edge, which corresponds to the strangth of the correlation between the two regions across your cohort.

The default colormap for visualising edges is Spectral_r, and by default we show a non-symmetric colormap which goes from 0 to the maximum value in the correlation matrix.


In [ ]:
# Create a view
ConnectomeView = view_connectome_3d(G)

# Show the view interactively
ConnectomeView

Alter the number of edges you show

You can change the number of edges you show with three different options.

Quoting from the nilearn documentation:

edge_threshold : str, number or None, optional (default=None)

  • If None, no thresholding.
  • If it is a number only connections of amplitude greater than threshold will be shown.
  • If it is a string it must finish with a percent sign,
  • e.g. "25.3%", and only connections of amplitude above the given percentile will be shown.

Set an absolute threshold value

Show all edges with a correlation greater than 0.5.


In [ ]:
# Create a view
ConnectomeView = view_connectome_3d(G, edge_threshold=0.5)

# Show the view interactively
ConnectomeView

Choose a percentile as the threshold

The default value plots the top 2% of edges by setting the default edge_threshold value to "98%".

Note that this is a string that ends with the % character.

We can adjust it to show more of these edges, for example to show the top 5% of edges.

(Heads up, even 5% is too many, the plot doesn't look very good 😆).


In [ ]:
# Create a view
ConnectomeView = view_connectome_3d(G, edge_threshold="95%")

# Show the view interactively
ConnectomeView

Choose a different colormap

Don't like the default Spectral_r colormap? You can change it! Any of the matplotlib colormaps work (although qualitative ones might not make much sense unless you change the weights to some categorical values.

How about a lovely purple edges from the PRGn_r edge colormap?


In [ ]:
# Create a view
ConnectomeView = view_connectome_3d(G, edge_cmap='PRGn_r')

# Show the view interactively
ConnectomeView

Change the range of the colorbar

By default the colorbar goes from 0 to the maximum value (vmax) in your correlation matrix: the strongest weight in the network.

At the moment nilearn doesn't give us access to adjust this to a specific minimum value (eg your threshold value) although we're confident that functionality will be added soon.

Side note: THANK YOU Nilearn for the amazing tools! We love you! 💖💖💖

We do have the ability to run the colorbar from -vmax to vmax. If you want to do that (maybe to match a different figure with a symmetric colormap) you can set symmetric_cmap to True.


In [ ]:
# Create a view
ConnectomeView = view_connectome_3d(G, edge_cmap='PRGn_r', symmetric_cmap=True)

# Show the view interactively
ConnectomeView

Change the width of the lines

If you do want to plot lots of edges, it makes sense to make the lines a little thinner so they are easier to see.

You can do this by setting the linewidth to a smaller value than the default which is 6.

Let's try plotting the top 5% of edges again, with a linewidth of 0.5. And a green colormap for fun.


In [ ]:
# Create a view
ConnectomeView = view_connectome_3d(G,
                                    edge_threshold="95%",
                                    edge_cmap="Greens",
                                    linewidth=0.5)

# Show the view interactively
ConnectomeView

Meh, still a few too many edges to really interpret, but at least you can see the individual lines now!

Change the size of the nodes

We played around with changing the nodes a lot in the first half of this tutorial using the view_nodes_3d function.

Unfortunately we aren't able to link up many of those options to the view_connectome_3d function...yet. (This might need some more nilearn functionality.)

But, we can change the size of the nodes (from the default value of 6). They'll always be black.

If we double the size (to 12) and add a winter colormap the brain doesn't really convey more information, but it looks pretty rad!


In [ ]:
# Create a view
ConnectomeView = view_connectome_3d(G,
                                    edge_cmap="winter",
                                    node_size=12)

# Show the view interactively
ConnectomeView

Note: transfer interactive brain visualisation into your local machine:

Even if you have a command-line interface and/or perform brain network analysis on the remote machine/cluster, you are still able to get produced interactive visualisations into your local machine and view those plots.

Save the output of any function described in this tutorial as a ConnectomeView data type.

ConnectomeView data type can be saved as an html page or rendered (transparently) by the Jupyter notebook. Useful methods are :

  • ‘resize’ to resize the plot displayed in a Jupyter notebook
  • ‘save_as_html’ to save the plot to a file
  • ‘open_in_browser’ to save the plot and open it in a web browser.

For example, you can do the following:

# save the output of the visualisation function into the variable ConnectomeView
ConnectomeView = view_connectome_3d(G)

# save the visualisation in the current directory as html file
ConnectomeView.save_as_html(file_name="my-lovely-viz.html")

Now you are able to move the .html file from remote machine/cluster to your local machine.
Then open the html file you moved in a browser!

Thank you

We hope you really enjoyed playing around with these interactive brains!