MICA-MNI / BrainSpace

BrainSpace is an open-access toolbox that allows for the identification and analysis of gradients from neuroimaging and connectomics datasets | available in both Python and Matlab |
http://brainspace.readthedocs.io
BSD 3-Clause "New" or "Revised" License
183 stars 73 forks source link

Alternative layout in plot_hemispheres #37

Closed danjgale closed 3 years ago

danjgale commented 3 years ago

First off, thank you for such a great package. It has been helpful in my own studies!

I was wondering how I would go about plotting my surfaces in adifferent layout -- a 2x2 grid with the top row as lateral views and the bottom row as medial views. This is the default view in connectome workbench (see below) and it is used quite often: S1200_midthickness_view

I noticed that the layout in plot_hemispheres is hardcoded, so I've been playing with the underlying code in plot_hemispheres with somewhat success, although I can't quite get the data to plot properly.

I am using a standalone example by taking these lines from plot_hemipsheres and making adjustments like so:

import numpy as np
from brainspace import plotting, datasets

lh, rh = datasets.load_conte69()
conn_matrix = datasets.load_group_fc('schaefer', scale=400)
labeling = datasets.load_parcellation('schaefer', scale=400, join=True)

# these are the inputs to `plot_hemispheres`
array_name = labeling
surf_lh = lh
surf_rh = rh

surfs = {'lh': surf_lh, 'rh': surf_rh}
layout = ['lh', 'lh', 'rh', 'rh']
view = ['lateral', 'medial', 'lateral', 'medial']

if isinstance(array_name, np.ndarray):
    if array_name.ndim == 2:
        array_name = [a for a in array_name]
    elif array_name.ndim == 1:
        array_name = [array_name]

if isinstance(array_name, list):
    layout = [layout] * len(array_name)
    array_name2 = []
    n_pts_lh = surf_lh.n_points
    for an in array_name:
        if isinstance(an, np.ndarray):
            name = surf_lh.append_array(an[:n_pts_lh], at='p')
            surf_rh.append_array(an[n_pts_lh:], name=name, at='p')
            array_name2.append(name)
        else:
            array_name2.append(an)
    array_name = np.asarray(array_name2)[:, None]

# reshape to 2x2
array_name = np.full((2, 2), fill_value=array_name[0][0], dtype='S50') 
layout = np.array(layout).reshape(2, 2).T.tolist()
view = view = [['lateral', 'medial'], ['medial', 'lateral']]

plotting.plot_surf({'lh': surf_lh, 'rh': surf_rh}, layout=layout, array_name=array_name, 
                   view=view, embed_nb=True, nan_color=(.6, .6, .6, 1), 
                   size=(400, 300), zoom=1.5)

Produces:

Screenshot from 2021-03-23 09-11-48

As you can see, none of the actual data is plotted, but the layout is correct. Thoughts?

Also, if you were interested in adding this layout as an option, I would be happy to work on it and submit a PR, as I think a lot of people would find it useful!

OualidBenkarim commented 3 years ago

You are changing arrayname type from str to bytes when using dtype='S50':

# In the surfs you stored arrayname as str
print(type(surf_lh.PointData.keys()[-1]))  # output: str 

# And here is bytes
print(type(array_name.flat[0]))  # output: numpy.bytes_

Just remove the dtype:

array_name = np.full((2, 2), fill_value=array_name[0][0])

or use unicode:

array_name = np.full((2, 2), fill_value=array_name[0][0], dtype='U50')

Yes, you are welcome to submit the PR. You could also consider how to plot the color bar, I didn't try with this layout

danjgale commented 3 years ago

Ah, totally missed that array_name uses bytes instead of str. I've fixed this and it works. I also added some code to handle the color bar. I figured the easies solution would be to have share = 'both' in plot_surf, such that the color bar can span the grid. Plotting the same Schaefer 400 parcelation:

grid

Thank you! I'll submit a PR and we can discuss further there.

ReinderVosDeWael commented 3 years ago

Closing this as it was resolved in #39