Skip to content

Commit

Permalink
Merge branch 'master' of github.com:nilearn/nilearn into test-circleci
Browse files Browse the repository at this point in the history
* 'master' of github.com:nilearn/nilearn:
  Rel 060b0 (nilearn#2208)
  Nilearn 0.6.0b0 release (nilearn#2206)
  Fixed the redundant & missing test case in merged PR nilearn#2035 (nilearn#2205)
  Modify fetch_development_fmri to fetch adults or children (nilearn#2035)
  Verbose doc building to ease tracking of progress & diagnose stalls (nilearn#2203)
  New conda env is created once conda path has been activated
  • Loading branch information
kchawla-pi committed Nov 20, 2019
2 parents d80143c + 0941897 commit 806b6b0
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 43 deletions.
1 change: 1 addition & 0 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Michael Waskom <mwaskom@stanford.edu>
Moritz Boos <moritz.boos@gmail.com> <moritz.boos@uni-oldenburg.de>
Óscar Nájera <najera.oscar@gmail.com>
Philippe Gervais <philippe.gervais@inria.fr>
Roberto Guidotti <robbenson18@gmail.com>
Ronald Phlypo <Ronald.Phlypo@inria.fr>
Salma Bougacha <salmabougacha@hotmail.com>
Vincent Michel <vm.michel@gmail.com>
Expand Down
2 changes: 1 addition & 1 deletion doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#

# You can set these variables from the command line.
SPHINXOPTS =
SPHINXOPTS = -v
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
Expand Down
19 changes: 17 additions & 2 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
0.6.0b
======
0.6.0b0
=======

**Released November 2019**

.. warning::

Expand Down Expand Up @@ -29,12 +31,25 @@ Changes
- :func:`nilearn.datasets.fetch_neurovault` now does not filter out images that
have their metadata field `is_valid` cleared by default.

- Users can now specify fetching data for adults, children, or both from
:func:`nilearn.datasets.fetch_development_fmri` .


Fixes
-----

- :func:`nilearn.plotting.plot_connectome` now correctly displays marker size on 'l'
and 'r' orientations, if an array or a list is passed to the function.

Contributors
------------

The following people contributed to this release (in alphabetical order)::

Jake Vogel
Jerome Dockes
Kshitij Chawla (kchawla-pi)
Roberto Guidotti

0.6.0a0
=======
Expand Down
146 changes: 107 additions & 39 deletions nilearn/datasets/func.py
Original file line number Diff line number Diff line change
Expand Up @@ -1910,7 +1910,8 @@ def _fetch_development_fmri_participants(data_dir, url, verbose):
return participants


def _fetch_development_fmri_functional(participants, data_dir, url, verbose):
def _fetch_development_fmri_functional(participants, data_dir, url, resume,
verbose):
"""Helper function to fetch_development_fmri.
This function helps in downloading functional MRI data in Nifti
Expand All @@ -1932,6 +1933,9 @@ def _fetch_development_fmri_functional(participants, data_dir, url, verbose):
Override download URL. Used for test only (or if you setup a mirror of
the data). Default: None
resume: bool, optional (default True)
Whether to resume download of a partly-downloaded file.
verbose: int
Defines the level of verbosity of the output.
Expand Down Expand Up @@ -1981,13 +1985,15 @@ def _fetch_development_fmri_functional(participants, data_dir, url, verbose):
func_url = url.format(this_osf_id['key_b'][0])
func_file = [(func.format(participant_id, participant_id), func_url,
{'move': func.format(participant_id)})]
path_to_func = _fetch_files(data_dir, func_file, verbose=verbose)[0]
path_to_func = _fetch_files(data_dir, func_file, resume=resume,
verbose=verbose)[0]
funcs.append(path_to_func)
return funcs, regressors


def fetch_development_fmri(n_subjects=None, reduce_confounds=True,
data_dir=None, resume=True, verbose=1):
data_dir=None, resume=True, verbose=1,
age_group='both'):
"""Fetch movie watching based brain development dataset (fMRI)
The data is downsampled to 4mm resolution for convenience. The origin of
Expand Down Expand Up @@ -2019,6 +2025,12 @@ def fetch_development_fmri(n_subjects=None, reduce_confounds=True,
verbose: int, optional (default 1)
Defines the level of verbosity of the output.
age_group: str, optional (default 'both')
Which age group to fetch
- 'adults' = fetch adults only (n=33, ages 18-39)
- 'child' = fetch children only (n=122, ages 3-12)
- 'both' = fetch full sample (n=155)
Returns
-------
data: Bunch
Expand All @@ -2044,6 +2056,10 @@ def fetch_development_fmri(n_subjects=None, reduce_confounds=True,
Preprocessing details: https://osf.io/wjtyq/
Note that if n_subjects > 2, and age_group is 'both',
fetcher will return a ratio of children and adults representative
of the total sample.
References
----------
Please cite this paper if you are using this dataset.
Expand All @@ -2070,52 +2086,104 @@ def fetch_development_fmri(n_subjects=None, reduce_confounds=True,
url=None,
verbose=verbose)

max_subjects = len(participants)
if n_subjects is None:
n_subjects = max_subjects

if (isinstance(n_subjects, numbers.Number) and
((n_subjects > max_subjects) or (n_subjects < 1))):
warnings.warn("Wrong value for n_subjects={0}. The maximum "
"value will be used instead n_subjects={1}"
.format(n_subjects, max_subjects))
n_subjects = max_subjects
adult_count, child_count = _filter_func_regressors_by_participants(
participants, age_group) # noqa: E126
max_subjects = adult_count + child_count

# Download functional and regressors based on participants
child_count = participants['Child_Adult'].tolist().count('child')
adult_count = participants['Child_Adult'].tolist().count('adult')
n_subjects = _set_invalid_n_subjects_to_max(n_subjects,
max_subjects,
age_group)

# To keep the proportion of children versus adults
n_child = np.round(float(n_subjects) / max_subjects * child_count).astype(int)
n_adult = np.round(float(n_subjects) / max_subjects * adult_count).astype(int)
percent_total = float(n_subjects) / max_subjects
n_child = np.round(percent_total * child_count).astype(int)
n_adult = np.round(percent_total * adult_count).astype(int)

# First, restrict the csv files to the adequate number of subjects
child_ids = participants[participants['Child_Adult'] ==
'child']['participant_id'][:n_child]
adult_ids = participants[participants['Child_Adult'] ==
'adult']['participant_id'][:n_adult]
ids = np.hstack([child_ids, adult_ids])
participants = participants[np.in1d(participants['participant_id'],
ids)]
# We want to return adults by default (i.e., `age_group=both`) or
# if explicitly requested.
if (age_group != 'child') and (n_subjects == 1):
n_adult, n_child = 1, 0

if (age_group == 'both') and (n_subjects == 2):
n_adult, n_child = 1, 1

participants = _filter_csv_by_n_subjects(participants, n_adult, n_child)

funcs, regressors = _fetch_development_fmri_functional(participants,
data_dir=data_dir,
url=None,
resume=resume,
verbose=verbose)

if reduce_confounds:
reduced_regressors = []
for in_file in regressors:
out_file = in_file.replace('desc-confounds',
'desc-reducedConfounds')
if not os.path.isfile(out_file):
confounds = np.recfromcsv(in_file, delimiter='\t')
selected_confounds = confounds[keep_confounds]
header = '\t'.join(selected_confounds.dtype.names)
np.savetxt(out_file, np.array(selected_confounds.tolist()),
header=header, delimiter='\t', comments='')
reduced_regressors.append(out_file)
regressors = reduced_regressors

regressors = _reduce_confounds(regressors, keep_confounds)
return Bunch(func=funcs, confounds=regressors, phenotypic=participants,
description=fdescr)


def _filter_func_regressors_by_participants(participants, age_group):
""" Filter functional and regressors based on participants
"""
valid_age_groups = ('both', 'child', 'adult')
if age_group not in valid_age_groups:
raise ValueError("Wrong value for age_group={0}. "
"Valid arguments are: {1}".format(age_group,
valid_age_groups)
)

child_adult = participants['Child_Adult'].tolist()

if age_group != 'adult':
child_count = child_adult.count('child')
else:
child_count = 0

if age_group != 'child':
adult_count = child_adult.count('adult')
else:
adult_count = 0
return adult_count, child_count


def _filter_csv_by_n_subjects(participants, n_adult, n_child):
"""Restrict the csv files to the adequate number of subjects
"""
child_ids = participants[participants['Child_Adult'] ==
'child']['participant_id'][:n_child]
adult_ids = participants[participants['Child_Adult'] ==
'adult']['participant_id'][:n_adult]
ids = np.hstack([adult_ids, child_ids])
participants = participants[np.in1d(participants['participant_id'], ids)]
participants = participants[np.argsort(participants, order='Child_Adult')]
return participants


def _set_invalid_n_subjects_to_max(n_subjects, max_subjects, age_group):
""" If n_subjects is invalid, sets it to max.
"""
if n_subjects is None:
n_subjects = max_subjects

if (isinstance(n_subjects, numbers.Number) and
((n_subjects > max_subjects) or (n_subjects < 1))):
warnings.warn("Wrong value for n_subjects={0}. The maximum "
"value (for age_group={1}) will be used instead: "
"n_subjects={2}"
.format(n_subjects, age_group, max_subjects))
n_subjects = max_subjects
return n_subjects


def _reduce_confounds(regressors, keep_confounds):
reduced_regressors = []
for in_file in regressors:
out_file = in_file.replace('desc-confounds',
'desc-reducedConfounds')
if not os.path.isfile(out_file):
confounds = np.recfromcsv(in_file, delimiter='\t')
selected_confounds = confounds[keep_confounds]
header = '\t'.join(selected_confounds.dtype.names)
np.savetxt(out_file, np.array(selected_confounds.tolist()),
header=header, delimiter='\t', comments='')
reduced_regressors.append(out_file)
return reduced_regressors
48 changes: 48 additions & 0 deletions nilearn/datasets/tests/test_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@

import os
import uuid

import numpy as np
import json
import nibabel
import gzip

import pytest
from sklearn.utils import check_random_state

from nose import with_setup
Expand Down Expand Up @@ -609,6 +612,7 @@ def test_fetch_development_fmri_functional():
funcs, confounds = func._fetch_development_fmri_functional(csv,
data_dir=tst.tmpdir,
url=local_url,
resume=True,
verbose=1)
assert_equal(len(funcs), 8)
assert_equal(len(confounds), 8)
Expand All @@ -633,3 +637,47 @@ def test_fetch_development_fmri():
verbose=1)
confounds = np.recfromcsv(data.confounds[0], delimiter='\t')
assert_equal(len(confounds[0]), 28)

# check first subject is an adult
data = func.fetch_development_fmri(n_subjects=1, reduce_confounds=False,
verbose=1)
age_group = data.phenotypic['Child_Adult'][0]
assert_equal(age_group, 'adult')

# check first subject is an child if requested with age_group
data = func.fetch_development_fmri(n_subjects=1, reduce_confounds=False,
verbose=1, age_group='child')
age_group = data.phenotypic['Child_Adult'][0]
assert_equal(age_group, 'child')

# check one of each age group returned if n_subject == 2
# and age_group == 'both
data = func.fetch_development_fmri(n_subjects=2, reduce_confounds=False,
verbose=1, age_group='both')
age_group = data.phenotypic['Child_Adult']
assert(all(age_group == ['adult', 'child']))

# check age_group
data = func.fetch_development_fmri(n_subjects=2, reduce_confounds=False,
verbose=1, age_group='child')
assert(all([x == 'child' for x in data.phenotypic['Child_Adult']]))


@with_setup(tst.setup_tmpdata, tst.teardown_tmpdata)
def test_fetch_development_fmri_invalid_n_subjects():
max_subjects = 155
n_subjects = func._set_invalid_n_subjects_to_max(n_subjects=None,
max_subjects=max_subjects,
age_group='adult')
assert n_subjects == max_subjects
with pytest.warns(UserWarning, match='Wrong value for n_subjects='):
func._set_invalid_n_subjects_to_max(n_subjects=-1,
max_subjects=max_subjects,
age_group='adult')


@with_setup(tst.setup_tmpdata, tst.teardown_tmpdata)
def test_fetch_development_fmri_exception():
with pytest.raises(ValueError, match='Wrong value for age_group'):
func._filter_func_regressors_by_participants(participants='junk',
age_group='junk for test')
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ doc-files = doc
# For PEP8 error codes see
# http://pep8.readthedocs.org/en/latest/intro.html#error-codes
# E402: module level import not at top of file
ignore=E402
# W504: line break after binary operator
ignore=E402, W504

[tool:pytest]
doctest_optionflags = NORMALIZE_WHITESPACE ELLIPSIS
Expand Down

0 comments on commit 806b6b0

Please sign in to comment.