Plot t-value on head-scalp for fNIRS data

  • MNE-Python version: 0.21
  • operating system: Ubuntu

I have fNIRS data of 106 channels in two experimental conditions, and computed t-statistic (106 t-values) in each channel basis. I want to plot these 106 t-values on scalp of head, but I am clueless how I can plot these t-value on scalp. I would appreciate any suggestions or help.
Thanks

Very cool dataset you have Prasenjit. Did you compute the t-values with MNE-NIRS? If so, can you post in here the code?

Is it group level results from MNE-NIRS? if so, you can use the group level topo function mne_nirs.visualisation.plot_glm_group_topo — MNE-NIRS 0.0.6 dev documentation and specify the value parameter to be the t-statistic.

Alternatively, you may be interested in mne_nirs.visualisation.plot_nirs_source_detector — MNE-NIRS 0.0.6 dev documentation

diagram

Thanks Robert!
I would explain a little bit about the steps that I followed. We have two conditions in our data described in the above figure. I have done the preprocessing of fNIRS signal using MNE-NIRS. In each channel, we took the average of last 10 sec data for Event_1 (v1,avg). Similarly, we computed the average of first 20 sec for Event_2 (v2,avg). Then, we computed the difference between these two averages (d1=v2,avg-v1,avg). In our whole data-set, we have 20 such occurrences (i.e., d1, d2, …, d20). Now, on each channel basis, we have computed t-statistic on these difference values (Null hypothesis: H0=0, Alternate Hypothesis: Ha!=0) whether the difference is zero or not. So, we have 106 t-values for 106 channels. This calculation is done outside of MNE by taking the data (raw.get_data()).

I plot these values on 2D head model using MNE (mne.viz.plot_topomap()). I want to plot these t-values on scalp of head. But, I could not figure out how to do it as most of the scalp figures in MNE tutorials deal with ‘stc’ file.

Any help would be really appreciated. Thanks.

Your processing sounds great. Very interesting work. I’m glad the functions are working well for you so far.

If the topoplots are working well then that means your data is in the right format.

There is currently no convenient way to create the plot you desire that I am aware of. I am almost have that functionality working in this pull request, but can’t merge it as I’m fighting to get the continuous integration to work… But maybe the code here can help you get started. WIP: Plot GLM results on cortical surface by rob-luke · Pull Request #287 · mne-tools/mne-nirs · GitHub

Can you share a version of your script here? Im curious about you pipeline.

If you get the 3d plot working please report back here so we can learn together. And it will be a nice resource for future users.

import os
import numpy as np
import matplotlib.pyplot as plt
import mne
from scipy.stats import ttest_1samp


def get_data(fnirs_data, d_type, t_tup):
        # return the hbo data belong to the time interval specified by the tuple
        
        return fnirs_data.pick_types(fnirs=d_type).crop(tmin=t_tup[0], tmax=t_tup[1], 
        include_tmax=True).get_data()

def get_diff_talking_base_data(raw, t, t_b=10, t_a=20):
	# This function takes the raw data, time instant, duration before event (t_a), duration after event 
        # (t_a) as input. It will collect the data t_b sec before and t_a sec after the specified time instant.
	# Then take the average of these data before and after time instance specified by t. 
        # Then compute the difference of this two averages.
	
        d_type='hbo' # type of fNIRS data (hbo/hbr)
        base_data=get_data(raw.copy(), d_type, (t-t_b, t)).mean(axis=1)
        event2_data=get_data(raw.copy(), d_type, (t, t+t_a)).mean(axis=1)
        diff=event2_data-base_data        
        return diff[:, np.newaxis]


baseFolder='path for data'
run_2='xxxx' # folder name


data_path=os.path.join(baseFolder, run_2)

# Reading fNIRS data
fnirs_data=mne.io.read_raw_nirx(data_path, preload=True, verbose=None)

### converting raw intensity to optical intensity
fnirs_data_od=mne.preprocessing.nirs.optical_density(fnirs_data)

### Converting from optical density to haemoglobin
fnirs_data_haemo=mne.preprocessing.nirs.beer_lambert_law(fnirs_data_od)


## Removing heart rate from signal
l_freq=0.01 # lower cut off frequency
h_freq=0.1 # higher cut off frequency
fnirs_filt= fnirs_data_haemo.filter(l_freq=l_freq, h_freq=h_freq, method='fir', phase='zero-double')

## Get the difference data from the particular intervals
time_inst=[399.68, 466.54, 772.73, 1018.89, 1102.38] ## Specify the time instants where you want to compare
t_b=10 # time duration before an event you want to average (for event_1)
t_a=20 # Time duration after an event you want to average (for event_2)
diff_data=[]
diff_data=[get_diff_talking_base_data(fnirs_filt.copy(), t, t_b, t_a) for t in time_inst] # Get the difference between average data from two events
diff_data=np.hstack(diff_data) # list to 2-D array


## Compute t-static of the difference values
info_data=fnirs_filt.pick_types(fnirs='hbo').info
t_diff, p_diff=ttest_1samp(diff_data, 0, axis=1)
fig, ax=plt.subplots(1,1)
mne.viz.plot_topomap(t_diff, info_data, colorbar=True, sensors=True, vmin=-5, vmax=5, outlines='head', contours=0)
ax.set_title('T-statitstic between Talking time and Before Talking intervals', fontweight='bold')
### End of 2D plotting t-values

data_path='/data2/pdhara/mne_data/MNE-sample-data'
subjects_dir = data_path + '/subjects'

## This function is working for plotting source and detector pairs (106 channels) in 3D head model
fig = mne.viz.create_3d_figure(size=(800, 600), bgcolor='white')
fig = mne.viz.plot_alignment(fnirs_filt.info, show_axes=True,
							 subject='fsaverage', coord_frame='mri',
							 trans='fsaverage', surfaces=['brain'],
							 fnirs=['channels', 'pairs',
									'sources', 'detectors'],
							 subjects_dir=subjects_dir, fig=fig)
mne.viz.set_3d_view(figure=fig, azimuth=20, elevation=60, distance=0.4,
					focalpoint=(0., -0.01, 0.02))

### This function is not working ***
mne_nirs.visualisation.plot_nirs_source_detector(t_diff, info_data, radius=0.001, subject='fsaverage', subjects_dir=subjects_dir, surfaces='head', cmap=True)

I have attached the code above. It took longer a bit as I need to eliminate unnecessary stuffs. Anyways,
I tried to plot source and detector pairs in head, and it is working fine (second last). But, I tried to plot t-values as a data matrix ([106x1]), but not working.

When you say not working, please provide the error. How else do you expect us to help you?

Just looking at the code could it be because t_diff is computed on all channels, but the info field has been subset to just hbo?

Sorry Robert. I tried to run the following line:

data_path=’/path_to_mne_data/mne_data/MNE-sample-data’ # mne dataset in different directory than the default one.
subjects_dir = data_path + ‘/subjects’
mne_nirs.visualisation.plot_nirs_source_detector(t_diff, fnirs_filt.pick_types(fnirs=‘hbo’).info, radius=0.001, subject=‘fsaverage’, subjects_dir=subjects_dir, surfaces=‘head’, cmap=True)

I have copied the mne_data folder into another path, and I think the files related to head model are missing. The error is following:
mne_nirs.visualisation.plot_nirs_source_detector(t_diff, fnirs_filt.info, radius=0.001, subject=‘fsaverage’, subjects_dir=subjects_dir, surfaces=‘head’, cmap=True)
Traceback (most recent call last):

File “”, line 1, in
mne_nirs.visualisation.plot_nirs_source_detector(t_diff, fnirs_filt.info, radius=0.001, subject=‘fsaverage’, subjects_dir=subjects_dir, surfaces=‘head’, cmap=True)

File “/**/anaconda3/envs/mne21nirs/lib/python3.8/site-packages/mne_nirs/visualisation/_plot_nirs_source_detector.py”, line 127, in plot_nirs_source_detector
fig = plot_alignment(

File “”, line 24, in plot_alignment

File “/**/anaconda3/envs/mne21nirs/lib/python3.8/site-packages/mne/viz/_3d.py”, line 742, in plot_alignment
raise IOError('No head surface found for subject ’

OSError: No head surface found for subject fsaverage after trying:
/path_to_mne_data/mne_data/MNE-sample-data/subjects/fsaverage/bem/outer_skin.surf
/path_to_mne_data/mne_data/MNE-sample-data/subjects/fsaverage/bem/flash/outer_skin.surf
/path_to_mne_data/mne_data/MNE-sample-data/subjects/fsaverage/bem/fsaverage-head.fif

/path_to_mne_data/mne_data/MNE-sample-data/subjects/fsaverage/bem/outer_skin.surf
/path_to_mne_data/mne_data/MNE-sample-data/subjects/fsaverage/bem/flash/outer_skin.surf
/path_to_mne_data/mne_data/MNE-sample-data/subjects/fsaverage/bem/fsaverage-head.fif

I searched for these three files in mne_data folder (downloaded earlier), but I could not find it. I downloaded it again in my machine to ensure that the mne_data folder is intact. But these files are not available in the mentioned path. Would you help me where do I get these files? Thanks.

is your data really stored at the location /path_to_mne_data/mne_data/? What is the output of mne.get_config('MNE_DATASETS_SAMPLE_PATH')?

We have limited storage in the server, therefore, I have copied the mne_data folder into another path. I checked the path (/mne_data/MNE-sample-data/subjects/fsaverage/bem/) mentioned in above three lines, and could not find these three files. I have attached the screenshot of that folder.

Also, I checked in another machine where I downloaded mne_data folder (using codes in mne tutorials). There, too, I checked the same path, could not find these three files. I attached another screenshot.

If I run the mne.get_config(‘MNE_DATASETS_SAMPLE_PATH’) line in my local machine. I get the path for mne_data (’/home/user_name/mne_data’)

I would be thankful if you could tell me where do I get these files. Thanks .

Your script above contains the following lines:

data_path='/data2/pdhara/mne_data/MNE-sample-data'
subjects_dir = data_path + '/subjects'

Can you try with this instead?

subjects_dir = mne.datasets.fsaverage.data_path()

I run this line: subjects_dir = mne.datasets.fsaverage.data_path()

But I got the following error.
subjects_dir = mne.datasets.fsaverage.data_path()
AttributeError: module ‘mne.datasets’ has no attribute ‘fsaverage’

I have checked in another path (/data2/pdhara/mne_data/MNE-sample-data/subjects/sample/bem) where I find the files of similar names (attached in below picture).

I do not want to change these path in mne built-in function as these additional path (i.e., subjects_dir+ /fsaverage/bem/) are added by built-in functions, and don not want to create mess in built-in functions.

oops, subjects_dir = mne.datasets.fsaverage.data_path() is wrong. To get the fsaverage files you do mne.datasets.fetch_fsaverage(). The subjects_dir that you pass to that function needs to be the same one you pass to plot_nirs_source_detector() otherwise the plotting function will be looking in the wrong place for the files.

I run this line: mne.datasets.fetch_fsaverage(). I got another directory, named MNE-fsaverage-data, in mne_data folder. I attached a screenshot.

Then I run the following lines

subjects_dir = mne.datasets.sample.data_path() + ‘/subjects’

mne_nirs.visualisation.plot_nirs_source_detector(t_diff, fnirs_filt.pick_types(fnirs=‘hbo’).info, radius=0.001, subject=‘fsaverage’, subjects_dir=subjects_dir, surfaces=‘head’, cmap=True)

I found the same error.

Traceback (most recent call last):
File “fnirs_processing.py”, line 91, in
mne_nirs.visualisation.plot_nirs_source_detector(t_diff, fnirs_filt.pick_types(fnirs=‘hbo’).info, radius=0.001, subject=‘fsaverage’, subjects_dir=subjects_dir, surfaces=‘head’, cmap=True)
File “/home/user_name/anaconda3/envs/mne21/lib/python3.8/site-packages/mne_nirs/visualisation/_plot_nirs_source_detector.py”, line 127, in plot_nirs_source_detector
fig = plot_alignment(
File “”, line 21, in plot_alignment
File “/home/user_name/anaconda3/envs/mne21/lib/python3.8/site-packages/mne/viz/_3d.py”, line 742, in plot_alignment
raise IOError('No head surface found for subject ’
OSError: No head surface found for subject fsaverage after trying:
/home/user_name/mne_data/MNE-sample-data/subjects/fsaverage/bem/outer_skin.surf
/home/user_name/mne_data/MNE-sample-data/subjects/fsaverage/bem/flash/outer_skin.surf
/home/user_name/mne_data/MNE-sample-data/subjects/fsaverage/bem/fsaverage-head.fif

I think the additional data downloaded into the folder mne_data/MNE-fsaverage-data/, and do not changes anything in the directory /home/user_name/mne_data/MNE-sample-data/subjects/fsaverage/bem/. But this function (mne_nirs.visualisation.plot_nirs_source_detector) is looking into the later path.

As noted above:

where “that function” is fetch_fsaverage(). if you did fetch_fsaverage() with no arguments, then it will have used the default subjects_dir, so you are passing a different subjects_dir to plot_nirs_source_detector().

Thank you Dan. The problem of missing files related to head surface got solved. Thanks again. Those files are used by mne_nirs.visualisation.plot_nirs_source_detector function. Though the problem of missing files got solved, but I encounter another error related to length of ‘colors’ variable, given below.

Run the following line:
mne_nirs.visualisation.plot_nirs_source_detector(t_diff, info_data, radius=0.001, subject=‘fsaverage’, subjects_dir=subjects_dir, surfaces=‘head’, cmap=True)

I got the following error:

mne_nirs.visualisation.plot_nirs_source_detector(t_diff, info_data, radius=0.001, subject=‘fsaverage’, subjects_dir=subjects_dir, surfaces=‘head’, cmap=True)
Using outer_skin.surf for head surface.
Traceback (most recent call last):

File “”, line 1, in
mne_nirs.visualisation.plot_nirs_source_detector(t_diff, info_data, radius=0.001, subject=‘fsaverage’, subjects_dir=subjects_dir, surfaces=‘head’, cmap=True)

File “/home/user_name/anaconda3/envs/mne21nirs/lib/python3.8/site-packages/mne_nirs/visualisation/_plot_nirs_source_detector.py”, line 146, in plot_nirs_source_detector
renderer.tube(origin=[np.array([locs[3], locs[4], locs[5]])],

File “/home/user_name/anaconda3/envs/mne21nirs/lib/python3.8/site-packages/mne/viz/backends/_pyvista.py”, line 460, in tube
cmap = _get_colormap_from_array(colormap, normalized_colormap)

File “/home/user_name/anaconda3/envs/mne21nirs/lib/python3.8/site-packages/mne/viz/backends/_utils.py”, line 32, in _get_colormap_from_array
cmap = ListedColormap(np.array(colormap) / 255.0)

File “/home/user_name/anaconda3/envs/mne21nirs/lib/python3.8/site-packages/matplotlib/colors.py”, line 893, in init
N = len(colors)

TypeError: object of type ‘numpy.float64’ has no len()

This could be an actual bug or mistake in the documentation. I’ll check and report back here.

According to the documentation cmap should be a string. Can you remove this argument so it uses the default and try again.

https://mne.tools/mne-nirs/master/generated/mne_nirs.visualisation.plot_nirs_source_detector.html#mne_nirs.visualisation.plot_nirs_source_detector

Please let me know if this works or not. I can add more examples and expand the documentation if we resolve this.