#!/usr/bin/env python3
"""
Module to plot graphs.
All `viz` functions require the extra modules `matplotlib` and `nibabel`,
installable using the flag:
```shell
$ pip3 install nigsp[viz]
```
Attributes
----------
FIGSIZE : tuple
Figure size
LGR
Logger
SET_DPI : int
DPI of the figure
"""
import logging
import numpy as np
from .operations.timeseries import resize_ts
LGR = logging.getLogger(__name__)
SET_DPI = 100
FIGSIZE = (18, 10)
FIGSIZE_SQUARE = (6, 5)
FIGSIZE_LONG = (10, 5)
[docs]
def plot_connectivity(mtx, filename=None, closeplot=False):
"""
Create a connectivity matrix plot.
If mtx has 3 dimensions, average first along the last axis.
Parameters
----------
mtx : numpy.ndarray
A (square) array with connectivity information inside.
filename : None, str, or os.PathLike, optional
The path to save the plot on disk.
closeplot : bool, optional
Whether to close plots after saving or not. Mainly used for debug
or use with live python/ipython instances.
Returns
-------
0
If there are no errors.
Raises
------
ImportError
If matplotlib and/or nilearn are not installed.
ValueError
If mtx has more than 3 dimensions.
Notes
-----
Requires `matplotlib` and `nilearn`
"""
try:
import matplotlib.pyplot as plt
from nilearn.plotting import plot_matrix
except ImportError:
raise ImportError(
"nilearn and matplotlib are required to plot node images. "
"Please see install instructions."
)
mtx = mtx.squeeze()
if mtx.ndim > 3:
raise ValueError(
"Cannot plot connectivity matrices for matrix of dimensions > 3."
)
elif mtx.ndim == 3:
LGR.warning("Since matrix is 3D, averaging across last dimension.")
mtx = mtx.mean(axis=-1)
if mtx.shape[0] != mtx.shape[1]:
LGR.warning("Given matrix is not a square matrix!")
LGR.info("Creating connectivity plot.")
plt.figure(figsize=FIGSIZE_SQUARE)
plot_matrix(mtx)
if filename is not None:
plt.savefig(filename, dpi=SET_DPI)
closeplot = True
if closeplot:
plt.close()
return 0
[docs]
def plot_greyplot(timeseries, filename=None, title=None, resize=None, closeplot=False):
"""
Create a greyplot (a.k.a. carpet plot a.k.a. timeseries plot).
If timeseries has 3 dimensions, average first along the last axis.
Parameters
----------
timeseries : numpy.ndarray
An array representing a timeseries. Time has to be encoded in the
second dimension (axis=1).
filename : None, str, or os.PathLike, optional
The path to save the plot on disk.
title : None or str, optional
Add a title to the graph
resize : 'spc', 'norm', 'gnorm', 'demean', 'gdemean' tuple, list, or None, optional
Whether to resize the signal or not before plotting.
If 'spc', compute signal percentage change
If 'norm', normalise signals (z-score)
If 'gnorm', globally normalise signals (keep relationship between timeseries)
If 'demean', remove signal average
If 'gdemean', remove global average
If 'gsr', remove global signal (average across points)
If tuple or list, rescale signals between those two values
If None, don't do anything (default)
closeplot : bool, optional
Whether to close plots after saving or not. Mainly used for debug
or use with live python/ipython instances.
Returns
-------
0
If there are no errors.
Raises
------
ImportError
If matplotlib is not installed.
ValueError
If timeseries has more than 3 dimensions.
Notes
-----
Requires `matplotlib`
See `timeseries.rescale_ts`
"""
try:
import matplotlib.pyplot as plt
except ImportError:
raise ImportError(
"matplotlib is required to plot node images. "
"Please see install instructions."
)
timeseries = timeseries.squeeze()
if timeseries.ndim > 3:
raise ValueError("Cannot plot greyplots for timeseries of dimensions > 3.")
elif timeseries.ndim == 3:
LGR.warning("Since timeseries is 3D, averaging across last dimension.")
timeseries = timeseries.mean(axis=-1)
timeseries = resize_ts(timeseries, resize)
LGR.info("Creating greyplot.")
plt.figure(figsize=FIGSIZE_LONG)
if title is not None:
plt.title(title)
vmax = np.percentile(timeseries, 99)
vmin = np.percentile(timeseries, 1)
plt.imshow(timeseries, cmap="gray", vmin=vmin, vmax=vmax)
plt.colorbar()
plt.tight_layout()
if filename is not None:
plt.savefig(filename, dpi=SET_DPI)
closeplot = True
if closeplot:
plt.close()
else:
plt.show()
return 0
[docs]
def plot_nodes(ns, atlas, filename=None, thr=None, closeplot=False):
"""
Create a marker plot in the MNI space.
If ns has 2 dimensions, average first along last dimension.
Parameters
----------
ns : numpy.ndarray
A 1- or 2- D array that contains the value of the nodes.
atlas : str, os.PathLike, 3D Nifti1Image, or numpy.ndarray
The 3d nifti image of an atlas, a string or path to its position,
or a list of coordinates of the center of mass of parcels.
filename : None, str, or os.PathLike, optional
The path to save the plot on disk.
thr : float or None, optional
The threshold to use in plotting the nodes.
closeplot : bool, optional
Whether to close plots after saving or not. Mainly used for debug
or use with live python/ipython instances.
Returns
-------
0
If there are no errors.
Raises
------
ImportError
If matplotlib and/or nilearn are not installed.
ValueError
If ns has more than 2 dimensions.
If coordinates can't be extracted from atlas.
Notes
-----
Requires `matplotlib` and `nilearn`
"""
try:
import matplotlib.pyplot as plt
from nilearn.plotting import find_parcellation_cut_coords, plot_markers
except ImportError:
raise ImportError(
"nilearn and matplotlib are required to plot node images. "
"Please see install instructions."
)
# First check that ns is a valid source of data.
ns = ns.squeeze()
if ns.ndim > 2:
raise ValueError("Cannot plot node values for matrix of dimensions > 2.")
elif ns.ndim == 2:
LGR.warning("Given matrix has 2 dimensions, averaging across last dimension.")
ns = ns.mean(axis=-1)
# Then treat atlas
if type(atlas) is np.ndarray:
if atlas.ndim > 2 or atlas.shape[1] != 3:
raise NotImplementedError(
"Only atlases in nifti format or list of coordinates are supported."
)
else:
coord = atlas
else:
coord = find_parcellation_cut_coords(atlas)
if ns.shape[0] != coord.shape[0]:
raise ValueError("Node array and coordinates array have different length.")
LGR.info("Creating markerplot.")
plt.figure(figsize=FIGSIZE)
plot_markers(ns, coord, node_threshold=thr)
if filename is not None:
plt.savefig(filename, dpi=SET_DPI)
closeplot = True
if closeplot:
plt.close()
return 0
[docs]
def plot_edges(mtx, atlas, filename=None, thr=None, closeplot=False):
"""
Create a connectivity plot in the MNI space.
If mtx has 2 dimensions, average first along last dimension.
Parameters
----------
mtx : numpy.ndarray
A 2- or 3- D array that contains the value of the nodes.
atlas : str | os.PathLike | 3D Nifti1Image | numpy.ndarray
The 3d nifti image of an atlas, a string or path to its position,
or a list of coordinates of the center of mass of parcels.
filename : None, str, or os.PathLike, optional
The path to save the plot on disk.
thr : float, str or None, optional
The threshold to use in plotting the nodes.
If :class:``str``, needs to express a percentage.
closeplot : bool, optional
Whether to close plots after saving or not. Mainly used for debug
or use with live python/ipython instances.
Returns
-------
0
If there are no errors.
Raises
------
ImportError
If matplotlib and/or nilearn are not installed.
ValueError
If mtx has more than 3 dimensions.
If coordinates can't be extracted from atlas.
Notes
-----
Requires ``matplotlib`` and ``nilearn``.
"""
try:
import matplotlib.pyplot as plt
from nilearn.plotting import cm, find_parcellation_cut_coords, plot_connectome
except ImportError:
raise ImportError(
"nilearn and matplotlib are required to plot node images. "
"Please see install instructions."
)
# First check that mtx is a valid source of data.
mtx = mtx.squeeze()
if mtx.ndim > 3:
raise ValueError("Cannot plot node values for matrix of dimensions > 3.")
elif mtx.ndim == 3:
LGR.warning("Given matrix has 3 dimensions, averaging across last dimension.")
mtx = mtx.mean(axis=-1)
# Then treat atlas
if type(atlas) is np.ndarray:
if atlas.ndim > 2 or atlas.shape[1] != 3:
raise NotImplementedError(
"Only atlases in nifti format or list of coordinates are supported."
)
else:
coord = atlas
else:
coord = find_parcellation_cut_coords(atlas)
if mtx.shape[0] != coord.shape[0]:
raise ValueError("Matrix axis and coordinates array have different length.")
LGR.info("Creating connectome-like plot.")
plt.figure(figsize=FIGSIZE)
pc_args = {
"adjacency_matrix": mtx,
"node_coords": coord,
"node_color": "black",
"node_size": 5,
"edge_threshold": thr,
"colorbar": True,
}
if mtx.min() >= 0:
pc_args["edge_vmin"] = 0
pc_args["edge_vmax"] = mtx.max()
pc_args["edge_cmap"] = cm.red_transparent_full_alpha_range
plot_connectome(**pc_args)
if filename is not None:
plt.savefig(filename, dpi=SET_DPI)
closeplot = True
if closeplot:
plt.close()
return 0
"""
Copyright 2022, Stefano Moia.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""