# -*- coding: utf-8 -*-
"""Functions for fetching datasets (from the internet, if necessary)."""
from collections import namedtuple
import os
from pathlib import Path
try:
# nilearn 0.10.3
from nilearn.datasets._utils import fetch_files as _fetch_files
except ImportError:
from nilearn.datasets.utils import _fetch_files
from sklearn.utils import Bunch
from neuromaps.datasets.utils import get_data_dir, get_dataset_info
SURFACE = namedtuple('Surface', ('L', 'R'))
ALIAS = dict(
fslr='fsLR', fsavg='fsaverage', mni152='MNI152', mni='MNI152',
FSLR='fsLR', CIVET='civet'
)
DENSITIES = dict(
civet=['41k', '164k'],
fsaverage=['3k', '10k', '41k', '164k'],
fsLR=['4k', '8k', '32k', '164k'],
MNI152=['1mm', '2mm', '3mm'],
)
_atlas_docs = dict(
url="""\
url : str, optional
URL from which to download data. Default: None\
""",
data_dir="""\
data_dir : str, optional
Path to use as data directory. If not specified, will check for
environmental variable 'neuromaps_DATA'; if that is not set, will
use `~/neuromaps-data` instead. Default: None\
""",
verbose="""\
verbose : int, optional
Modifies verbosity of download, where higher numbers mean more updates.
Default: 1\
""",
genericatlas="""\
atlas : dict
Dictionary where keys are atlas types and values are atlas files\
""",
surfatlas="""\
atlas : dict
Dictionary where keys are atlas types and values are tuples of atlas
files (L/R hemisphere)\
"""
)
def _sanitize_atlas(atlas):
"""Check for aliases of `atlas` and confirms valid input."""
atlas = ALIAS.get(atlas, atlas)
if atlas not in DENSITIES:
raise ValueError(f'Invalid atlas: {atlas}.')
return atlas
def _bunch_outputs(keys, values, surface=True):
"""Group `values` together (L/R) if `surface` and zips with `keys`."""
if surface:
values = [SURFACE(*values[i:i + 2]) for i in range(0, len(values), 2)]
return Bunch(**dict(zip(keys, values)))
def _fetch_atlas(atlas, density, keys, url=None, data_dir=None, verbose=1):
"""Get requested `atlas`."""
atlas = _sanitize_atlas(atlas)
densities = DENSITIES[atlas]
if density not in densities:
raise ValueError(f'Invalid density: {density}. Must be one of '
f'{densities}')
data_dir = get_data_dir(data_dir=data_dir)
info = get_dataset_info(atlas)[density]
if url is None:
url = info['url']
opts = {
'uncompress': True,
'md5sum': info['md5'],
'move': f'{atlas}{density}.tar.gz'
}
if atlas == 'MNI152':
filenames = [
f'tpl-MNI152NLin2009cAsym_res-{density}{suff}.nii.gz'
for suff in ('_T1w', '_T2w', '_PD', '_desc-brain_mask',
'_label-csf_probseg', '_label-gm_probseg',
'_label-wm_probseg')
]
if density in ('1mm', '2mm'):
filenames += [
f'tpl-MNI152NLin6Asym_res-{density}{suff}.nii.gz'
for suff in ('_T1w', '_desc-brain_mask')
]
else:
filenames = [
'tpl-{}_den-{}_hemi-{}_{}.surf.gii'
.format(atlas, density, hemi, surf)
for surf in keys
for hemi in ('L', 'R')
] + [
'tpl-{}_den-{}_hemi-{}_desc-{}.gii'
.format(atlas, density, hemi, desc)
for desc in ('nomedialwall_dparc.label',
'sulc_midthickness.shape',
'vaavg_midthickness.shape')
for hemi in ('L', 'R')
]
keys += ['medial', 'sulc', 'vaavg']
filenames = [os.path.join('atlases', atlas, fn) for fn in filenames]
data = [
Path(fn) for fn in
_fetch_files(data_dir, files=[(f, url, opts) for f in filenames],
verbose=verbose)
]
return _bunch_outputs(keys, data, atlas != 'MNI152')
[docs]def fetch_civet(density='41k', url=None, data_dir=None, verbose=1): # noqa: D103
keys = ['white', 'midthickness', 'inflated', 'veryinflated', 'sphere']
return _fetch_atlas(
'civet', density, keys, url=url, data_dir=data_dir, verbose=verbose
)
fetch_civet.__doc__ = """
Fetch CIVET surface atlas.
Parameters
----------
density : {{'{densities}'}}, optional
Density of CIVET atlas to fetch. Default: '41k'
{url}
{data_dir}
{verbose}
Returns
-------
{surfatlas}
""".format(**_atlas_docs, densities="', '".join(DENSITIES['civet']))
[docs]def fetch_fsaverage(density='41k', url=None, data_dir=None, verbose=1): # noqa: D103
keys = ['white', 'pial', 'inflated', 'sphere']
return _fetch_atlas(
'fsaverage', density, keys, url=url, data_dir=data_dir, verbose=verbose
)
fetch_fsaverage.__doc__ = """
Fetch fsaverage surface atlas.
Parameters
----------
density : {{'{densities}'}}, optional
Density of fsaverage atlas to fetch. Default: '41k'
{url}
{data_dir}
{verbose}
Returns
-------
{surfatlas}
""".format(**_atlas_docs, densities="', '".join(DENSITIES['fsaverage']))
[docs]def fetch_fslr(density='32k', url=None, data_dir=None, verbose=1): # noqa: D103
keys = ['midthickness', 'inflated', 'veryinflated', 'sphere']
if density in ('4k', '8k'):
keys.remove('veryinflated')
return _fetch_atlas(
'fsLR', density, keys, url=url, data_dir=data_dir, verbose=verbose
)
fetch_fslr.__doc__ = """
Fetch fsLR surface atlas.
Parameters
----------
density : {{'{densities}'}}, optional
Density of fsLR atlas to fetch. Default: '32k'
{url}
{data_dir}
{verbose}
Returns
-------
{surfatlas}
""".format(**_atlas_docs, densities="', '".join(DENSITIES['fsLR']))
[docs]def fetch_mni152(density='1mm', url=None, data_dir=None, verbose=1): # noqa: D103
keys = ['2009cAsym_T1w', '2009cAsym_T2w', '2009cAsym_PD',
'2009cAsym_brainmask', '2009cAsym_CSF', '2009cAsym_GM',
'2009cAsym_WM']
if density in ('1mm', '2mm'):
keys += ['6Asym_T1w', '6Asym_brainmask']
return _fetch_atlas(
'MNI152', density, keys, url=url, data_dir=data_dir, verbose=verbose
)
fetch_mni152.__doc__ = """
Fetch MNI152 atlas.
Parameters
----------
density : {{'{densities}'}}, optional
Resolution of MNI152 atlas to fetch. Default: '1mm'
{url}
{data_dir}
{verbose}
Returns
-------
{genericatlas}
""".format(**_atlas_docs, densities="', '".join(DENSITIES['MNI152']))
[docs]def fetch_regfusion(atlas, url=None, data_dir=None, verbose=1): # noqa: D103
atlas = _sanitize_atlas(atlas)
densities = DENSITIES[atlas].copy()
invalid = dict(civet=('164k',), fsLR=('4k', '8k'))
for remove in invalid.get(atlas, []):
densities.remove(remove)
data_dir = get_data_dir(data_dir=data_dir)
info = get_dataset_info('regfusion')
if url is None:
url = info['url']
opts = {
'uncompress': True,
'md5sum': info['md5'],
'move': 'regfusion.tar.gz'
}
filenames = [
'tpl-MNI152_space-{}_den-{}_hemi-{}_regfusion.txt'
.format(atlas, density, hemi)
for density in densities
for hemi in ['L', 'R']
]
filenames = [os.path.join('atlases', 'regfusion', fn) for fn in filenames]
data = [
Path(fn) for fn in
_fetch_files(data_dir, files=[(f, url, opts) for f in filenames],
verbose=verbose)
]
return _bunch_outputs(densities, data)
fetch_regfusion.__doc__ = """
Fetch regfusion inputs for mapping MNI152 to specified surface `atlas`.
Parameters
----------
atlas : {{'civet', 'fsaverage', 'fsLR'}}
Atlas to fetch
{url}
{data_dir}
{verbose}
Returns
-------
regfusion : dict
Dictionary where keys are surface densities and values are regfusion inputs
""".format(**_atlas_docs)
[docs]def fetch_atlas(atlas, density, url=None, data_dir=None, verbose=1): # noqa: D103
atlas = _sanitize_atlas(atlas)
fetcher = globals()[f'fetch_{atlas.lower()}']
return fetcher(density, url=url, data_dir=data_dir, verbose=verbose)
fetch_atlas.__doc__ = """
Fetch specified `atlas` and `density`.
Parameters
----------
atlas : {{'{atlases}'}}
Atlas to fetch
density : str
Density (or resolution) of `atlas`. Must be valid for provided `atlas`
{url}
{data_dir}
{verbose}
Returns
-------
{genericatlas}
""".format(**_atlas_docs, atlases="', '".join(DENSITIES.keys()))
[docs]def fetch_all_atlases(data_dir=None, verbose=1): # noqa: D103
atlases = {'regfusion': {}}
for key, resolutions in DENSITIES.items():
atlases[key] = {}
for res in resolutions:
atlases[key][res] = \
fetch_atlas(key, res, data_dir=data_dir, verbose=verbose)
if key != 'MNI152':
atlases['regfusion'][key] = \
fetch_regfusion(key, data_dir=data_dir, verbose=verbose)
return atlases
fetch_all_atlases.__doc__ = """
Fetch (and cache) all available atlases.
Parameters
----------
{data_dir}
{verbose}
Returns
-------
atlases : dict
Nested dictionaries containing all available atlases
"""
[docs]def get_atlas_dir(atlas, data_dir=None): # noqa: D103
try:
atlas = _sanitize_atlas(atlas)
except ValueError as err:
if atlas != 'regfusion':
raise err
return Path(get_data_dir(data_dir=data_dir)) / 'atlases' / atlas
get_atlas_dir.__doc__ = """
Return filepath to specified `atlas`.
Parameters
----------
atlas : str
Atlas for which filepath should be returned
{data_dir}
Returns
-------
atlas_dir : os.PathLike
Full filepath to `atlas` directory
Raises
------
ValueError
If provided `atlas` is not valid
""".format(**_atlas_docs)