diff --git a/neuromaps/datasets/_osf.py b/neuromaps/datasets/_osf.py index 15a2fc63..aab37b23 100644 --- a/neuromaps/datasets/_osf.py +++ b/neuromaps/datasets/_osf.py @@ -2,13 +2,17 @@ """Functions for working with data/osf.json file.""" import os -from pkg_resources import resource_filename import json - +import importlib.resources from nilearn.datasets.utils import _md5_sum_file - from neuromaps.datasets.utils import _get_session +if getattr(importlib.resources, 'files', None) is not None: + _importlib_avail = True +else: + from pkg_resources import resource_filename + _importlib_avail = False + # uniquely identify each item ('hemi' can be None) FNAME_KEYS = ['source', 'desc', 'space', 'den', 'res', 'hemi'] # auto-generated (checksum can be None if file does not exist) @@ -23,9 +27,12 @@ INFO_KEYS = ['source', 'refs', 'comments', 'demographics'] # distribution JSON -OSFJSON = resource_filename( - 'neuromaps', os.path.join('datasets', 'data', 'osf.json') -) +if _importlib_avail: + OSFJSON = importlib.resources.files("neuromaps") / "datasets/data/osf.json" +else: + OSFJSON = resource_filename( + 'neuromaps', os.path.join('datasets', 'data', 'osf.json') + ) def parse_filename(fname, return_ext=True, verbose=False): diff --git a/neuromaps/datasets/tests/test__osf.py b/neuromaps/datasets/tests/test__osf.py index 0a410c44..4485f622 100644 --- a/neuromaps/datasets/tests/test__osf.py +++ b/neuromaps/datasets/tests/test__osf.py @@ -1,12 +1,16 @@ # -*- coding: utf-8 -*- """For testing neuromaps.datasets._osf functionality.""" -from pkg_resources import resource_filename - import pytest - +import importlib.resources from neuromaps.datasets import _osf +if getattr(importlib.resources, 'files', None) is not None: + _importlib_avail = True +else: + from pkg_resources import resource_filename + _importlib_avail = False + @pytest.mark.xfail def test_parse_filename(): @@ -22,7 +26,10 @@ def test_parse_fname_list(): def test_parse_json(): """Test parsing a JSON file.""" - osf = resource_filename('neuromaps', 'datasets/data/osf.json') + if _importlib_avail: + osf = importlib.resources.files("neuromaps") / "datasets/data/osf.json" + else: + osf = resource_filename('neuromaps', 'datasets/data/osf.json') out = _osf.parse_json(osf) assert isinstance(out, list) and all(isinstance(i, dict) for i in out) diff --git a/neuromaps/datasets/utils.py b/neuromaps/datasets/utils.py index 39debb60..f44b911e 100644 --- a/neuromaps/datasets/utils.py +++ b/neuromaps/datasets/utils.py @@ -3,10 +3,15 @@ import json import os -from pkg_resources import resource_filename - +import importlib.resources import requests +if getattr(importlib.resources, 'files', None) is not None: + _importlib_avail = True +else: + from pkg_resources import resource_filename + _importlib_avail = False + RESTRICTED = ["grh4d"] @@ -70,8 +75,14 @@ def get_dataset_info(name, return_restricted=True): dataset : dict or list-of-dict Information on requested data """ - fn = resource_filename('neuromaps', - os.path.join('datasets', 'data', 'osf.json')) + if _importlib_avail: + fn = importlib.resources.files("neuromaps") / "datasets/data/osf.json" + else: + fn = resource_filename( + 'neuromaps', + os.path.join('datasets', 'data', 'osf.json') + ) + with open(fn) as src: osf_resources = _osfify_urls(json.load(src), return_restricted) diff --git a/neuromaps/nulls/tests/test_nulls.py b/neuromaps/nulls/tests/test_nulls.py index fa634883..d701b019 100644 --- a/neuromaps/nulls/tests/test_nulls.py +++ b/neuromaps/nulls/tests/test_nulls.py @@ -2,36 +2,382 @@ """For testing neuromaps.nulls.nulls functionality.""" import pytest +from neuromaps.nulls.nulls import ( + alexander_bloch, vasa, hungarian, baum, + cornblath, burt2018, burt2020, moran +) +from neuromaps.datasets import fetch_annotation +from neuromaps.parcellate import Parcellater +from neuromaps.images import annot_to_gifti, dlabel_to_gifti +from netneurotools.datasets import fetch_schaefer2018, fetch_cammoun2012 +sample_surface_maps = [ + ('abagen', 'genepc1', 'fsaverage', '10k'), + ('hcps1200', 'myelinmap', 'fsLR', '32k'), +] +sample_volume_maps = [ + ('neurosynth', 'cogpc1', 'MNI152', '2mm'), + ('dukart2018', 'flumazenil', 'MNI152', '3mm'), +] -@pytest.mark.xfail -def test_alexander_bloch(): +sample_surface_parcellations = [ + ("schaefer100x7", fetch_schaefer2018, '100Parcels7Networks'), + ("schaefer200x7", fetch_schaefer2018, '200Parcels7Networks') +] + +sample_volume_parcellations = [ + ("lausanne033", fetch_cammoun2012, 'scale033'), + ("lausanne060", fetch_cammoun2012, 'scale060') +] + +@pytest.fixture( + scope="module", + params=sample_surface_maps, + ids=["_".join(_) for _ in sample_surface_maps] +) +def sample_surface(request): + """Fixture for surface annotation.""" + source, desc, space, den = request.param + annot = fetch_annotation( + source=source, desc=desc, space=space, den=den + ) + return request.param, annot + +@pytest.fixture( + scope="module", + params=sample_volume_maps, + ids=["_".join(_) for _ in sample_volume_maps] +) +def sample_volume(request): + """Fixture for volume annotation.""" + source, desc, space, res = request.param + annot = fetch_annotation( + source=source, desc=desc, space=space, res=res + ) + return request.param, annot + +@pytest.fixture( + scope="module", + params=sample_surface_parcellations, + ids=[_[0] for _ in sample_surface_parcellations] +) +def sample_surface_parcellated(sample_surface, request): + """Fixture for parcellated surface annotation.""" + surf_tuple, annot = sample_surface + source, desc, space, den = surf_tuple + + if request.param[0].startswith("schaefer"): + parc_name, parc_fetcher, parc_label = request.param + if space == "fsaverage": + if den == "164k": + atlas = annot_to_gifti(parc_fetcher(version="fsaverage")[parc_label]) + elif den == "41k": + atlas = annot_to_gifti(parc_fetcher(version="fsaverage6")[parc_label]) + elif den == "10k": + atlas = annot_to_gifti(parc_fetcher(version="fsaverage5")[parc_label]) + else: + raise NotImplementedError( + f"Invalid surface density: {den} for fsaverage space" + ) + elif space == "fsLR": + atlas = dlabel_to_gifti(parc_fetcher(version="fslr32k")[parc_label]) + else: + raise NotImplementedError(f"Invalid surface space: {space}") + parc = Parcellater(atlas, space) + else: + raise NotImplementedError(f"Invalid parcellation: {request.param[0]}") + + annot_parc = parc.fit_transform(annot, space) + + return surf_tuple, parc_name, atlas, annot_parc + + +@pytest.fixture( + scope="module", + params=sample_volume_parcellations, + ids=[_[0] for _ in sample_volume_parcellations] +) +def sample_volume_parcellated(sample_volume, request): + """Fixture for parcellated volume annotation.""" + vol_tuple, annot = sample_volume + source, desc, space, res = vol_tuple + + if request.param[0].startswith("lausanne"): + parc_name, parc_fetcher, parc_label = request.param + atlas = parc_fetcher(version="MNI152NLin2009aSym")[parc_label] + parc = Parcellater(atlas, space) + else: + raise NotImplementedError(f"Invalid parcellation: {request.param[0]}") + + annot_parc = parc.fit_transform(annot, space) + + return vol_tuple, parc_name, atlas, annot_parc + +class TestFixturesSmoke: + """Test fixtures for null models.""" + + def test_fixture_surface_smoke(self, sample_surface): + """Test fetching surface annotation.""" + print(sample_surface[0]) + assert True + + + def test_fixture_volume_smoke(self, sample_volume): + """Test fetching volume annotation.""" + print(sample_volume[0]) + assert True + + + def test_fixture_surface_parcellated_smoke(self, sample_surface_parcellated): + """Test fetching parcellated surface annotation.""" + surf_tuple, parc_name, atlas, annot_parc = sample_surface_parcellated + print(surf_tuple, parc_name, atlas, annot_parc.shape[0]) + assert True + + + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # nilearn/nilearn/pull/3722 + ) + def test_fixture_volume_parcellated_smoke(self, sample_volume_parcellated): + """Test fetching parcellated volume annotation.""" + vol_tuple, parc_name, atlas, annot_parc = sample_volume_parcellated + print(vol_tuple, parc_name, atlas, annot_parc.shape[0]) + assert True + +class TestAlexanderBloch: """Test alexander-bloch null model.""" - assert False + def test_alexander_bloch_surface(self, sample_surface): + """Test alexander-bloch null model for surface.""" + surf_tuple, annot = sample_surface + _, _, space, den = surf_tuple + alexander_bloch(annot, atlas=space, density=den, n_perm=3) -@pytest.mark.xfail -def test_vasa(): + + @pytest.mark.xfail + def test_alexander_bloch_volume(self, sample_volume): + """Test alexander-bloch null model for volume.""" + vol_tuple, annot = sample_volume + _, _, space, res = vol_tuple + alexander_bloch(annot, atlas=space, density=res, n_perm=3) + + + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # nilearn/nilearn/pull/3722 + ) + def test_alexander_bloch_surface_parcellated(self, sample_surface_parcellated): + """Test alexander-bloch null model for parcellated surface.""" + surf_tuple, parc_name, atlas, annot_parc = sample_surface_parcellated + _, _, space, den = surf_tuple + print(surf_tuple, parc_name, atlas, annot_parc.shape) + + alexander_bloch( + annot_parc, atlas=space, density=den, parcellation=atlas, n_perm=3 + ) + + + @pytest.mark.xfail + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # neuromaps.images.load_data() + ) + def test_alexander_bloch_volume_parcellated(self, sample_volume_parcellated): + """Test alexander-bloch null model for parcellated volume.""" + vol_tuple, parc_name, atlas, annot_parc = sample_volume_parcellated + _, _, space, res = vol_tuple + print(vol_tuple, parc_name, atlas, annot_parc.shape) + + alexander_bloch( + annot_parc, atlas=space, density=res, parcellation=atlas, n_perm=3 + ) + +class TestVasa: """Test vasa null model.""" - assert False + @pytest.mark.xfail + def test_vasa_surface(self, sample_surface): + """Test vasa null model for surface.""" + surf_tuple, annot = sample_surface + _, _, space, den = surf_tuple + vasa(annot, atlas=space, density=den, n_perm=3) -@pytest.mark.xfail -def test_hungarian(): + + @pytest.mark.xfail + def test_vasa_volume(self, sample_volume): + """Test vasa null model for volume.""" + vol_tuple, annot = sample_volume + _, _, space, res = vol_tuple + vasa(annot, atlas=space, density=res, n_perm=3) + + + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # nilearn/nilearn/pull/3722 + ) + def test_vasa_surface_parcellated(self, sample_surface_parcellated): + """Test vasa null model for parcellated surface.""" + surf_tuple, parc_name, atlas, annot_parc = sample_surface_parcellated + _, _, space, den = surf_tuple + print(surf_tuple, parc_name, atlas, annot_parc.shape) + + vasa( + annot_parc, atlas=space, density=den, parcellation=atlas, n_perm=3 + ) + + + @pytest.mark.xfail + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # neuromaps.images.load_data() + ) + def test_vasa_volume_parcellated(self, sample_volume_parcellated): + """Test vasa null model for parcellated volume.""" + vol_tuple, parc_name, atlas, annot_parc = sample_volume_parcellated + _, _, space, res = vol_tuple + print(vol_tuple, parc_name, atlas, annot_parc.shape) + + vasa( + annot_parc, atlas=space, density=res, parcellation=atlas, n_perm=3 + ) + +class TestHungarian: """Test hungarian null model.""" - assert False + @pytest.mark.xfail + def test_hungarian_surface(self, sample_surface): + """Test hungarian null model for surface.""" + surf_tuple, annot = sample_surface + _, _, space, den = surf_tuple + hungarian(annot, atlas=space, density=den, n_perm=3) -@pytest.mark.xfail -def test_baum(): + + @pytest.mark.xfail + def test_hungarian_volume(self, sample_volume): + """Test hungarian null model for volume.""" + vol_tuple, annot = sample_volume + _, _, space, res = vol_tuple + hungarian(annot, atlas=space, density=res, n_perm=3) + + + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # nilearn/nilearn/pull/3722 + ) + def test_hungarian_surface_parcellated(self, sample_surface_parcellated): + """Test hungarian null model for parcellated surface.""" + surf_tuple, parc_name, atlas, annot_parc = sample_surface_parcellated + _, _, space, den = surf_tuple + print(surf_tuple, parc_name, atlas, annot_parc.shape) + + hungarian( + annot_parc, atlas=space, density=den, parcellation=atlas, n_perm=3 + ) + + + @pytest.mark.xfail + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # neuromaps.images.load_data() + ) + def test_hungarian_volume_parcellated(self, sample_volume_parcellated): + """Test hungarian null model for parcellated volume.""" + vol_tuple, parc_name, atlas, annot_parc = sample_volume_parcellated + _, _, space, res = vol_tuple + print(vol_tuple, parc_name, atlas, annot_parc.shape) + + hungarian( + annot_parc, atlas=space, density=res, parcellation=atlas, n_perm=3 + ) + +class TestBaum: """Test baum null model.""" - assert False + @pytest.mark.xfail + def test_baum_surface(self, sample_surface): + """Test baum null model for surface.""" + surf_tuple, annot = sample_surface + _, _, space, den = surf_tuple + baum(annot, atlas=space, density=den, n_perm=3) -@pytest.mark.xfail -def test_cornblath(): + + @pytest.mark.xfail + def test_baum_volume(self, sample_volume): + """Test baum null model for volume.""" + vol_tuple, annot = sample_volume + _, _, space, res = vol_tuple + baum(annot, atlas=space, density=res, n_perm=3) + + + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # nilearn/nilearn/pull/3722 + ) + def test_baum_surface_parcellated(self, sample_surface_parcellated): + """Test baum null model for parcellated surface.""" + surf_tuple, parc_name, atlas, annot_parc = sample_surface_parcellated + _, _, space, den = surf_tuple + print(surf_tuple, parc_name, atlas, annot_parc.shape) + + baum( + annot_parc, atlas=space, density=den, parcellation=atlas, n_perm=3 + ) + + + @pytest.mark.xfail + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # neuromaps.images.load_data() + ) + def test_baum_volume_parcellated(self, sample_volume_parcellated): + """Test baum null model for parcellated volume.""" + vol_tuple, parc_name, atlas, annot_parc = sample_volume_parcellated + _, _, space, res = vol_tuple + print(vol_tuple, parc_name, atlas, annot_parc.shape) + + baum( + annot_parc, atlas=space, density=res, parcellation=atlas, n_perm=3 + ) + +class TestCornblath: """Test cornblath null model.""" - assert False + + @pytest.mark.skip + @pytest.mark.xfail + def test_cornblath_surface(self, sample_surface): + """Test cornblath null model for surface.""" + surf_tuple, annot = sample_surface + _, _, space, den = surf_tuple + cornblath(annot, atlas=space, density=den, n_perm=3) + + @pytest.mark.skip + @pytest.mark.xfail + def test_cornblath_volume(self, sample_volume): + """Test cornblath null model for volume.""" + vol_tuple, annot = sample_volume + _, _, space, res = vol_tuple + cornblath(annot, atlas=space, density=res, n_perm=3) + + + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # nilearn/nilearn/pull/3722 + ) + def test_cornblath_surface_parcellated(self, sample_surface_parcellated): + """Test cornblath null model for parcellated surface.""" + surf_tuple, parc_name, atlas, annot_parc = sample_surface_parcellated + _, _, space, den = surf_tuple + print(surf_tuple, parc_name, atlas, annot_parc.shape) + + cornblath( + annot_parc, atlas=space, density=den, parcellation=atlas, n_perm=3 + ) + + + @pytest.mark.xfail + @pytest.mark.filterwarnings( + "ignore::DeprecationWarning" # neuromaps.images.load_data() + ) + def test_cornblath_volume_parcellated(self, sample_volume_parcellated): + """Test cornblath null model for parcellated volume.""" + vol_tuple, parc_name, atlas, annot_parc = sample_volume_parcellated + _, _, space, res = vol_tuple + print(vol_tuple, parc_name, atlas, annot_parc.shape) + + cornblath( + annot_parc, atlas=space, density=res, parcellation=atlas, n_perm=3 + ) @pytest.mark.xfail @@ -45,20 +391,35 @@ def test__make_surrogates(): """Test making surrogates.""" assert False - -@pytest.mark.xfail -def test_burt2018(): +class TestBurt2018: """Test burt2018 null model.""" - assert False + def test_burt2018_surface(self, sample_surface): + """Test burt2018 null model for surface.""" + surf_tuple, annot = sample_surface + _, _, space, den = surf_tuple + burt2018(annot, atlas=space, density=den, n_perm=3) -@pytest.mark.xfail -def test_burt2020(): + + def test_burt2018_volume(self, sample_volume): + """Test burt2018 null model for volume.""" + vol_tuple, annot = sample_volume + _, _, space, res = vol_tuple + burt2018(annot, atlas=space, density=res, n_perm=3) + +class TestBurt2020: """Test burt2020 null model.""" - assert False + @pytest.mark.xfail + def test_burt2020(self): + """Test burt2020 null model.""" + assert False -@pytest.mark.xfail -def test_moran(): + +class TestMoran: """Test moran null model.""" - assert False + + @pytest.mark.xfail + def test_moran(self): + """Test moran null model.""" + assert False diff --git a/neuromaps/parcellate.py b/neuromaps/parcellate.py index 2dd2f34a..bc37cb37 100644 --- a/neuromaps/parcellate.py +++ b/neuromaps/parcellate.py @@ -2,7 +2,10 @@ """Functionality for parcellating data.""" import nibabel as nib -from nilearn.maskers import NiftiLabelsMasker +try: + from nilearn.maskers import NiftiLabelsMasker +except ImportError: + from nilearn.input_data import NiftiLabelsMasker from nilearn.image import new_img_like from nilearn.masking import compute_background_mask import numpy as np diff --git a/neuromaps/tests/conftest.py b/neuromaps/tests/conftest.py index 0963681f..91fb5989 100644 --- a/neuromaps/tests/conftest.py +++ b/neuromaps/tests/conftest.py @@ -5,6 +5,11 @@ import pytest +def pytest_configure(config): + """Add markers for tests.""" + config.addinivalue_line( + "markers", "workbench: mark test to run with Connectome Workbench" + ) def pytest_runtest_setup(item): """Skip tests that require workbench if it's not installed."""