Skip to content

paulcbogdan/NiChord

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PyPI - Python Version PyPI Conda

NiChord

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.

Installation

Can be installed via pip:

pip install nichord

Can alternatively be installed via conda:

conda install -c conda-forge nichord

Examples

Here, we cover the code provided in example\example.py.

Input variables

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]

Plotting

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.

Plotting everything at once

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)

Convenience functions

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)

Note

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.

Authors

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 please cite the paper below or the present reporsitory:

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.

About

Package for visualizing brain networks. Creates chord diagrams drawn using matplotlib

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages