NiChord
is a Python package for visualizing functional connectivity data. This package was inspired by NeuroMArVL, an online visualization tool. Although the code was designed for neuroscience research, it can be used with any configuration of edge and label data.
Can be installed via pip:
pip install nichord
Can alternatively be installed via conda:
conda install -c conda-forge nichord
Here, we cover the code provided in example\example.py
.
edges
are specified as a list of tuples, (i, j)
, where i
and j
are indices representing the two nodes making up the edge. For this example, the following list represents seven edges among eight nodes.
edges = [(0, 1), (0, 2), (1, 5), (3, 5), (4, 6), (2, 7), (6, 7)]
Each node index should also correspond to a coordinate in MNI space. The coordinates are defined as a list of lists or list of tuples, wherein the outer list's length (8, here) corresponds to the number of nodes.
coords = [[-24, -99, -12], [51, -3, -15], [-15, -70, 30], [21, 39, 39],
[21, -66, 48], [54, 33, 12], [-33, 3, 3], [57, -45, 12]]
These coordinates can be used to construct a dictionary (idx_to_label
), mapping each node index to a network label. This package provides functions to help assign labels to nodes given their anatomical location, based on the Yeo et al. (2011) atlas:
from nichord.coord_labeler import get_idx_to_label
idx_to_label = get_idx_to_label(coords, atlas='yeo')
idx_to_label
can alternatively be defined manually:
idx_to_label = {0: 'Visual', 1: 'DMN', 2: 'Visual', 3: 'DMN',
4: 'DAN', 5: 'FPCN', 6: 'VAN', 7: 'VAN'}
You may assign each edge a weight. Weights are defined as a list of length equal to the number of edges (e.g., 7 weights for this example). If edge_weights = None
, then grey edges are plotted, unless an aggregation feature is used (see plot_count=True
below).
edge_weights = [-0.3, -0.5, 0.7, 0.5, -0.2, 0.3, 0.8]
These variables and an optional filepath can then be passed to create a chord diagram:
from nichord.chord import plot_chord
# If the filepath is left None, the chord diagram can be opened in a matplotlib with plt.show()
fp_chord = 'ex0_chord.png'
plot_chord(idx_to_label, edges, edge_weights=edge_weights, fp_chord=fp_chord,
linewidths=15, alphas=0.9, do_ROI_circles=True, label_fontsize=70,
# July 2023 update allows changing label fontsize
do_ROI_circles_specific=True, ROI_circle_radius=0.02)
If no filepath is passed, the diagram will be opened in a matplotlib window.
The code can also be used to plot glass brains, which leverages the same variables. Note that the colors of the glass brain nodes correspond to the network colors in the chord diagram.
from nichord.glassbrain import plot_glassbrain
fp_glass = 'ex0_glassbrain.png'
plot_glassbrain(idx_to_label, edges, edge_weights, fp_glass,
coords, linewidths=15, node_size=17)
You can combine the figures above into a single figure.
from nichord.combine import combine_imgs
fp_combined = 'ex0_combined.png'
combine_imgs(fp_glass, fp_chord, fp_combined)
Notably, these functions have many other optional variables. For example, plot_chord
, plot_glassbrain
, and plot_and_combine
all take edge_threshold
as an argument, which operates the same as in nilearn. Also, you can pass specific colors for each network label using a dictionary.
Further information on these optional variables can be seen by examining example\example.py
, which contains code used to generate the two examples from the first section of this README. You can also learn about these by reading the hinting/documentation within each function.
You can also use combine.plot_and_combine
to do plot_chord
, plot_glassbrain
, and combine_image
with a single function. plot_and_combine
will create (if needed) and use directories chord
and glass
wherever you specify the combined image to be made with dir_out
.
from nichord.combine import plot_and_combine
network_colors = {'Uncertain': 'black', 'Visual': 'purple',
'SM': 'darkturquoise', 'DAN': 'green', 'VAN': 'fuchsia',
'Limbic': 'burlywood', 'FPCN': 'orange', 'DMN': 'red'}
network_order = ['FPCN', 'DMN', 'DAN', 'Visual', 'SM', 'Limbic',
'Uncertain', 'VAN']
dir_out = 'example'
fn = 'ex1.png'
plot_and_combine(dir_out, fn, idx_to_label, edges,
edge_weights=edge_weights, coords=power_coords,
network_order=network_order, network_colors=network_colors)
To plot_and_combine
, you can pass chord_kwargs
and/or glass_kwargs
to adjust the appearance of the chord diagram or glass brain, which will in turn be sent to plot_chord
and/or plot_glassbrain
. The example below shows this and also how you can add a title and give the chord diagram a black background:
dir_out = 'example'
fn = r'ex1_black_BG.png'
chord_kwargs = {'black_BG': True}
plot_and_combine(dir_out, fn, idx_to_label, edges,
edge_weights=edge_weights, coords=power_coords,
network_order=network_order, network_colors=network_colors,
chord_kwargs=chord_kwargs, title='Example 1b (black)')
Here is another example. This one shows how setting linewidth = 0
in the glass brain kwargs causes only the glass brain ROIs to be plotted. This may be useful in combination with setting a node_size
as a list, which causes nodes on the glass brain to be plotted in sizes specified.
fn = r'ex1_count.png'
chord_kwargs = {'plot_count': True}
n_nodes = len(set([i for i, j in edges] + [j for i, j in edges]))
glass_kwargs = {'linewidths': 0.,
'node_size': list(range(1, n_nodes+1))}
plot_and_combine(dir_out, fn, idx_to_label, edges,
edge_weights=edge_weights, coords=power_coords,
network_order=network_order, network_colors=network_colors,
chord_kwargs=chord_kwargs, glass_kwargs=glass_kwargs,
title='Example 1c (count)')
This next example shows further features. With do_ROI_cicles=True
, you can plot little circles on the chord diagrams where the arcs start with. With only1glass=True
, you can the sagittal glass brain only. These arguments are not specific to plot_and_combine
. They can also be passed to plot_chord
and plot_glassbrain
, respectively.
fn = r'ex2.png'
edges = [(32, 12), (32, 48), (33, 48), (101, 105), (105, 219), (201, 33),
(32, 105)]
edge_weights = [-0.3, -0.5, 0.7, 0.5, -0.2, 0.3, 0.8]
chord_kwargs = {'alphas': 0.9, 'linewidths': 15, 'do_ROI_circles': True,
'do_ROI_circles_specific': True, 'ROI_circle_radius': 0.02,
'arc_setting': False}
glass_kwargs = {'linewidths': 15, 'node_size': 17}
plot_and_combine(dir_out, fn, idx_to_label, edges,
edge_weights=edge_weights, coords=power_coords,
network_order=network_order, network_colors=network_colors,
chord_kwargs=chord_kwargs, glass_kwargs=glass_kwargs)
For convenience, the function convert.convert_matrix(matrix)
is provided, which takes a matrix as input and returns two lists corresponding to edges
and edge_weights
.
from nichord.convert import convert_matrix
matrix = [[0, 0.5, 0.2], [0.5, 0, -0.2], [0.2, -0.2, 0]]
edges, edge_weights = convert_matrix(matrix)
There is seemingly a bug in matplotlib.backend_agg.RenderAgg, which makes rotated text not look ideal when plotting character by character. I submitted a bug report to matplotlib, along with a potential
solution. It has not been accepted yet, so for now I am "monkey patching" the
malfunctioning code in nichord.patch_RenderAgg.py
. The patch is automatically
applied when calling chord.plot_chord_diagram(...)
with the default argument
do_monkeypatch=True
.
The glass brain diagrams rely on the plotting tools from nilearn
, whereas the chord diagrams is made from scratch by drawing shapes in matplotlib
.
NiChord
was created by Paul C. Bogdan with help from Jonathan Shobrook as part of our research in the Dolcos Lab at the Beckman Institute for Advanced Science and Technology and the University of Illinois at Urbana-Champaign.
If you are using NiChord
in your work, it would be nice if you could please cite the paper below (or at least link to the present repository):
Bogdan, P.C., Iordan, A.D., Shobrook, J., & Dolcos, F. (2023). ConnSearch: A Framework for Functional Connectivity Analysis Designed for Interpretability and Effectiveness at Limited Sample Sizes. NeuroImage, 120274.