diff --git a/.github/workflows/macos_conda.yml b/.github/workflows/macos_conda.yml index f44c7f5cb..4df9dd1aa 100644 --- a/.github/workflows/macos_conda.yml +++ b/.github/workflows/macos_conda.yml @@ -31,11 +31,12 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - - uses: conda-incubator/setup-miniconda@v2 + - uses: mamba-org/setup-micromamba@v1 with: - activate-environment: 'mne-nirs' - python-version: ${{ env.PYTHON_VERSION }} environment-file: ${{ env.CONDA_ENV }} + environment-name: 'mne-nirs' + create-args: >- + python=${{ env.PYTHON_VERSION }} name: 'Setup conda' - shell: bash -el {0} run: | diff --git a/examples/general/plot_10_hrf_simulation.py b/examples/general/plot_10_hrf_simulation.py index 4dc023028..5ce343cf7 100644 --- a/examples/general/plot_10_hrf_simulation.py +++ b/examples/general/plot_10_hrf_simulation.py @@ -60,7 +60,7 @@ design_matrix = make_first_level_design_matrix(raw, stim_dur=5.0, drift_order=1, drift_model='polynomial') -fig, ax1 = plt.subplots(figsize=(10, 6), nrows=1, ncols=1) +fig, ax1 = plt.subplots(figsize=(10, 6), constrained_layout=True) fig = plot_design_matrix(design_matrix, ax=ax1) @@ -98,7 +98,7 @@ def print_results(glm_est, truth): # and plot the noisy data and the GLM fitted model. # We print the response estimate and see that is close, but not exactly correct, # we observe the mean square error is similar to the added noise. -# Note that the clean data plot is so similar to the GLM estimate that it is hard to see unless zoomed in. +# Note that the clean data plot is so similar to the GLM estimate that it is hard to see unless zoomed in. # First take a copy of noise free data for comparison raw_noise_free = raw.copy() @@ -106,12 +106,12 @@ def print_results(glm_est, truth): raw._data += np.random.normal(0, np.sqrt(1e-11), raw._data.shape) glm_est = run_glm(raw, design_matrix) -plt.plot(raw.times, raw_noise_free.get_data().T * 1e6) -plt.plot(raw.times, raw.get_data().T * 1e6, alpha=0.3) -plt.plot(raw.times, glm_est.theta()[0][0] * design_matrix["A"].values * 1e6) -plt.xlabel("Time (s)") -plt.ylabel("Haemoglobin (uM)") -plt.legend(["Clean Data", "Noisy Data", "GLM Estimate"]) +fig, ax = plt.subplots(constrained_layout=True) +ax.plot(raw.times, raw_noise_free.get_data().T * 1e6) +ax.plot(raw.times, raw.get_data().T * 1e6, alpha=0.3) +ax.plot(raw.times, glm_est.theta()[0][0] * design_matrix["A"].values * 1e6) +ax.set(xlabel="Time (s)", ylabel="Haemoglobin (μM)") +ax.legend(["Clean Data", "Noisy Data", "GLM Estimate"]) print_results(glm_est, amp) @@ -138,12 +138,12 @@ def print_results(glm_est, truth): drift_model='polynomial') glm_est = run_glm(raw, design_matrix) -plt.plot(raw.times, raw_noise_free.get_data().T * 1e6) -plt.plot(raw.times, raw.get_data().T * 1e6, alpha=0.3) -plt.plot(raw.times, glm_est.theta()[0][0] * design_matrix["A"].values * 1e6) -plt.xlabel("Time (s)") -plt.ylabel("Haemoglobin (uM)") -plt.legend(["Clean Data", "Noisy Data", "GLM Estimate"]) +fig, ax = plt.subplots(constrained_layout=True) +ax.plot(raw.times, raw_noise_free.get_data().T * 1e6) +ax.plot(raw.times, raw.get_data().T * 1e6, alpha=0.3) +ax.plot(raw.times, glm_est.theta()[0][0] * design_matrix["A"].values * 1e6) +ax.set(xlabel="Time (s)", ylabel="Haemoglobin (μM)") +ax.legend(["Clean Data", "Noisy Data", "GLM Estimate"]) print_results(glm_est, amp) @@ -171,11 +171,11 @@ def print_results(glm_est, truth): drift_model='polynomial') glm_est = run_glm(raw, design_matrix) -plt.plot(raw.times, raw.get_data().T * 1e6, alpha=0.3) -plt.plot(raw.times, glm_est.theta()[0][0] * design_matrix["A"].values * 1e6) -plt.xlabel("Time (s)") -plt.ylabel("Haemoglobin (uM)") -plt.legend(["Noisy Data", "GLM Estimate"]) +fig, ax = plt.subplots(constrained_layout=True) +ax.plot(raw.times, raw.get_data().T * 1e6, alpha=0.3) +ax.plot(raw.times, glm_est.theta()[0][0] * design_matrix["A"].values * 1e6) +ax.set(xlabel="Time (s)", ylabel="Haemoglobin (μM)") +ax.legend(["Noisy Data", "GLM Estimate"]) print_results(glm_est, amp) @@ -193,12 +193,12 @@ def print_results(glm_est, truth): glm_est = run_glm(raw, design_matrix, noise_model='ar5') -fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(15, 6)) -plt.plot([-0.58853134, -0.29575669, -0.52246482, 0.38735476, 0.02428681], - axes=axes) # actual values from model above -plt.plot(glm_est.model()[0].rho * -1.0, axes=axes) # estimates -plt.legend(["Simulation AR coefficients", "Estimated AR coefficients"]) -plt.xlabel("Coefficient") +fig, ax = plt.subplots(figsize=(15, 6), constrained_layout=True) +# actual values from model above +ax.plot([-0.58853134, -0.29575669, -0.52246482, 0.38735476, 0.02428681]) +ax.plot(glm_est.model()[0].rho * -1.0) # estimates +ax.legend(["Simulation AR coefficients", "Estimated AR coefficients"]) +ax.set_xlabel("Coefficient") # %% diff --git a/examples/general/plot_11_hrf_measured.py b/examples/general/plot_11_hrf_measured.py index 8f1b125c7..0beda136e 100644 --- a/examples/general/plot_11_hrf_measured.py +++ b/examples/general/plot_11_hrf_measured.py @@ -143,10 +143,10 @@ # line is raised for the duration of the stimulus/condition. s = mne_nirs.experimental_design.create_boxcar(raw_haemo) -fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(15, 6)) -plt.plot(raw_haemo.times, s, axes=axes) -plt.legend(["Control", "Left", "Right"], loc="upper right") -plt.xlabel("Time (s)"); +fig, ax = plt.subplots(figsize=(15, 6), constrained_layout=True) +ax.plot(raw_haemo.times, s) +ax.legend(["Control", "Left", "Right"], loc="upper right") +ax.set_xlabel("Time (s)"); # %% @@ -203,7 +203,7 @@ # The next columns illustrate the drift and constant components. # The last columns illustrate the short channel signals. -fig, ax1 = plt.subplots(figsize=(10, 6), nrows=1, ncols=1) +fig, ax1 = plt.subplots(figsize=(10, 6), constrained_layout=True) fig = plot_design_matrix(design_matrix, ax=ax1) @@ -227,13 +227,12 @@ # name. Note however, that this is just for visualisation and does not affect # the results below. +fig, ax = plt.subplots(constrained_layout=True) s = mne_nirs.experimental_design.create_boxcar(raw_intensity, stim_dur=5.0) -plt.plot(raw_intensity.times, s[:, 1]) -plt.plot(design_matrix['Tapping_Left']) -plt.xlim(180, 300) -plt.legend(["Stimulus", "Expected Response"]) -plt.xlabel("Time (s)") -plt.ylabel("Amplitude") +ax.plot(raw_intensity.times, s[:, 1]) +ax.plot(design_matrix['Tapping_Left']) +ax.legend(["Stimulus", "Expected Response"]) +ax.set(xlim=(180, 300), xlabel="Time (s)", ylabel="Amplitude") # %% diff --git a/mne_nirs/conftest.py b/mne_nirs/conftest.py index 8d047fe23..3a1149319 100644 --- a/mne_nirs/conftest.py +++ b/mne_nirs/conftest.py @@ -48,8 +48,15 @@ def pytest_configure(config): ignore:The register_cmap function.*: ignore:The get_cmap function.*: ignore:The figure layout has changed.*:UserWarning - # old MNE - ignore:The `pyvista.plotting.plotting` module.*: + # H5py + ignore:`product` is deprecated as of NumPy.*:DeprecationWarning + # seaborn + ignore:is_categorical_dtype is deprecated.*:FutureWarning + ignore:use_inf_as_na option is deprecated.*:FutureWarning + # nilearn + ignore:The provided callable scipy.optimize.curve_fit +@pytest.mark.filterwarnings("ignore:divide by zero encountered in divide.*:") +@pytest.mark.filterwarnings("ignore:invalid value encountered in.*:") def test_mayer(): fnirs_data_folder = mne.datasets.fnirs_motor.data_path() fnirs_raw_dir = os.path.join(fnirs_data_folder, 'Participant-1') diff --git a/mne_nirs/statistics/_glm_level_first.py b/mne_nirs/statistics/_glm_level_first.py index 775a776e0..09bf21796 100644 --- a/mne_nirs/statistics/_glm_level_first.py +++ b/mne_nirs/statistics/_glm_level_first.py @@ -16,14 +16,14 @@ import nilearn.glm from nilearn.glm.first_level import run_glm as nilearn_glm -try: # remove once MNE 1.0 is required - from mne.io.meas_info import ContainsMixin +try: # remove once MNE 1.6 is required + from mne._fiff.meas_info import ContainsMixin except ImportError: - from mne.channels.channels import ContainsMixin + from mne.io.meas_info import ContainsMixin from mne.utils import fill_doc, warn, verbose, check_fname, _validate_type -from mne.io.pick import _picks_to_idx, pick_info +from mne.io.pick import _picks_to_idx from mne.io.constants import FIFF -from mne import Info +from mne import Info, pick_info from ..visualisation._plot_GLM_topo import _plot_glm_topo,\ _plot_glm_contrast_topo diff --git a/mne_nirs/statistics/_statsmodels.py b/mne_nirs/statistics/_statsmodels.py index 19854f59f..75107d716 100644 --- a/mne_nirs/statistics/_statsmodels.py +++ b/mne_nirs/statistics/_statsmodels.py @@ -2,6 +2,8 @@ # # License: BSD (3-clause) +from io import StringIO + import pandas as pd import numpy as np @@ -14,7 +16,7 @@ def summary_to_dataframe(summary): ''' results = summary.tables[1] if type(results) is not pd.core.frame.DataFrame: - results = summary.tables[1].as_html() + results = StringIO(summary.tables[1].as_html()) results = pd.read_html(results, header=0, index_col=0)[0] return results diff --git a/mne_nirs/statistics/tests/test_glm_type.py b/mne_nirs/statistics/tests/test_glm_type.py index f3938b47b..1e3e26c57 100644 --- a/mne_nirs/statistics/tests/test_glm_type.py +++ b/mne_nirs/statistics/tests/test_glm_type.py @@ -298,6 +298,9 @@ def test_create_results_glm_contrast(): df = res.to_dataframe() assert df.shape == (n_channels, 10) + src = np.asarray(df["Source"]) + assert src.dtype.kind == "i" + def test_results_glm_io(): diff --git a/mne_nirs/tests/test_examples.py b/mne_nirs/tests/test_examples.py index 0b2de6ebb..e7fe15ded 100644 --- a/mne_nirs/tests/test_examples.py +++ b/mne_nirs/tests/test_examples.py @@ -7,7 +7,6 @@ import os import pytest import sys -import warnings from mne.utils import check_version @@ -26,14 +25,6 @@ def examples_path(): return path -def run_script_and_check(test_file_path): - with open(test_file_path) as fid, warnings.catch_warnings(): - # Ignore deprecation warning caused by - # app.setAttribute(Qt.AA_UseHighDpiPixmaps) in mne-python - warnings.filterwarnings("ignore", category=DeprecationWarning) - return exec(fid.read(), locals(), locals()) - - requires_mne_1p2 = pytest.mark.skipif( not check_version('mne', '1.2'), reason='Needs MNE-Python 1.2') # https://github.com/mne-tools/mne-bids/pull/406 @@ -53,6 +44,8 @@ def run_script_and_check(test_file_path): @pytest.mark.filterwarnings('ignore:No bad channels to interpolate.*:') +@pytest.mark.filterwarnings('ignore:divide by zero encountered.*:') +@pytest.mark.filterwarnings('ignore:invalid value encountered.*:') @pytest.mark.skipif( sys.platform.startswith('win'), reason='Unstable on Windows') @pytest.mark.examples @@ -76,4 +69,6 @@ def run_script_and_check(test_file_path): "plot_99_bad.py"])) def test_examples(fname, requires_pyvista): test_file_path = examples_path() + fname - run_script_and_check(test_file_path) + with open(test_file_path) as fid: + code = fid.read() + exec(code, locals(), locals()) diff --git a/mne_nirs/utils/_io.py b/mne_nirs/utils/_io.py index fe2a010ba..01b2b8757 100644 --- a/mne_nirs/utils/_io.py +++ b/mne_nirs/utils/_io.py @@ -110,7 +110,7 @@ def _tidy_RegressionResults(data, glm_est, design_matrix): mse_estimates[idx, :] = glm_est[name].MSE[0] for cond_idx, cond in enumerate(design_matrix.columns): t_estimates[idx, cond_idx] = glm_est[name].t( - column=cond_idx) + column=cond_idx).item() p_estimates[idx, cond_idx] = 2 * stats.t.cdf( -1.0 * np.abs(t_estimates[idx, cond_idx]), df=df_estimates[idx, cond_idx]) @@ -171,12 +171,18 @@ def _tidy_long_to_wide(d, expand_output=True): if expand_output: try: - d["Source"] = [re.search(r'S(\d+)_D(\d+) (\w+)', ch).group(1) - for ch in d["ch_name"]] - d["Detector"] = [re.search(r'S(\d+)_D(\d+) (\w+)', ch).group(2) - for ch in d["ch_name"]] - d["Chroma"] = [re.search(r'S(\d+)_D(\d+) (\w+)', ch).group(3) - for ch in d["ch_name"]] + d["Source"] = [ + int(re.search(r'S(\d+)_D(\d+) (\w+)', ch).group(1)) + for ch in d["ch_name"] + ] + d["Detector"] = [ + int(re.search(r'S(\d+)_D(\d+) (\w+)', ch).group(2)) + for ch in d["ch_name"] + ] + d["Chroma"] = [ + re.search(r'S(\d+)_D(\d+) (\w+)', ch).group(3) + for ch in d["ch_name"] + ] except AttributeError: warn("Non standard source detector names used") d["Significant"] = d["p_value"] < 0.05 diff --git a/mne_nirs/visualisation/_plot_3d_montage.py b/mne_nirs/visualisation/_plot_3d_montage.py index b8f8dc442..5489ac6fb 100644 --- a/mne_nirs/visualisation/_plot_3d_montage.py +++ b/mne_nirs/visualisation/_plot_3d_montage.py @@ -7,10 +7,9 @@ import numpy as np -from mne import pick_info, pick_types +from mne import pick_info, pick_types, Info from mne.channels import make_standard_montage from mne.channels.montage import transform_to_head -from mne.io import Info from mne.transforms import _get_trans, apply_trans from mne.utils import _validate_type, _check_option, verbose, logger from mne.viz import Brain diff --git a/mne_nirs/visualisation/_plot_GLM_topo.py b/mne_nirs/visualisation/_plot_GLM_topo.py index 219d544f4..41a94b586 100644 --- a/mne_nirs/visualisation/_plot_GLM_topo.py +++ b/mne_nirs/visualisation/_plot_GLM_topo.py @@ -10,10 +10,10 @@ import matplotlib as mpl from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable -from mne import Info +from mne import Info, pick_info from mne.utils import warn from mne.channels.layout import _merge_ch_data -from mne.io.pick import _picks_to_idx, _get_channel_types, pick_info +from mne.io.pick import _picks_to_idx from mne.viz import plot_topomap @@ -38,7 +38,7 @@ def _plot_glm_topo(inst, glm_estimates, design_matrix, *, for idx, name in enumerate(glm_estimates.keys()): estimates[idx, :] = glm_estimates[name].theta.T - types = np.unique(_get_channel_types(info)) + types = np.unique(info.get_channel_types()) if requested_conditions is None: requested_conditions = design_matrix.columns @@ -100,7 +100,7 @@ def _plot_glm_contrast_topo(inst, contrast, figsize=(12, 7), sphere=None): info = deepcopy(inst if isinstance(inst, Info) else inst.info) # Extract types. One subplot is created per type (hbo/hbr) - types = np.unique(_get_channel_types(info)) + types = np.unique(info.get_channel_types()) # Extract values to plot and rescale to uM estimates = contrast.effect[0] diff --git a/tools/circleci_dependencies.sh b/tools/circleci_dependencies.sh index 31d9c5728..5ef2b436c 100755 --- a/tools/circleci_dependencies.sh +++ b/tools/circleci_dependencies.sh @@ -2,5 +2,5 @@ python -m pip install --upgrade pip setuptools wheel -python -m pip install --upgrade --progress-bar off -r requirements.txt -r requirements_testing.txt -r requirements_doc.txt git+https://github.com/mne-tools/mne-python.git@main +python -m pip install --upgrade --progress-bar off -r requirements.txt -r requirements_testing.txt -r requirements_doc.txt git+https://github.com/mne-tools/mne-python.git@main "mne-qt-browser[opengl] @ git+https://github.com/mne-tools/mne-qt-browser.git@main" python -m pip install -e . diff --git a/tools/github_actions_dependencies.sh b/tools/github_actions_dependencies.sh index c42edc826..a05a67168 100755 --- a/tools/github_actions_dependencies.sh +++ b/tools/github_actions_dependencies.sh @@ -7,24 +7,15 @@ if [ ! -z "$CONDA_ENV" ]; then else # Changes here should also go in the interactive_test CircleCI job python -m pip install $STD_ARGS pip setuptools wheel - echo "Numpy" - pip uninstall -yq numpy - echo "Date utils" - # https://pip.pypa.io/en/latest/user_guide/#possible-ways-to-reduce-backtracking-occurring - pip install $STD_ARGS --pre --only-binary ":all:" python-dateutil pytz joblib threadpoolctl six - echo "pyside6" - pip install $STD_ARGS --pre --only-binary ":all:" pyside6 echo "NumPy/SciPy/pandas etc." - # TODO: Currently missing dipy for 3.10 https://github.com/dipy/dipy/issues/2489 - pip install $STD_ARGS --pre --only-binary ":all:" --no-deps --default-timeout=60 -i "https://pypi.anaconda.org/scipy-wheels-nightly/simple" numpy scipy pandas scikit-learn statsmodels dipy - echo "H5py, pillow, matplotlib" - pip install $STD_ARGS --pre --only-binary ":all:" --no-deps -f "https://7933911d6844c6c53a7d-47bd50c35cd79bd838daf386af554a83.ssl.cf2.rackcdn.com" h5py pillow matplotlib - echo "Numba, nilearn" - pip install $STD_ARGS --pre --only-binary ":all:" numba llvmlite nilearn + pip install $STD_ARGS --pre --only-binary ":all:" --default-timeout=60 --extra-index-url "https://pypi.anaconda.org/scientific-python-nightly-wheels/simple" numpy scipy scikit-learn pandas matplotlib pillow statsmodels + pip install $STD_ARGS --pre --only-binary ":all:" --no-deps --default-timeout=60 -i "https://pypi.anaconda.org/scipy-wheels-nightly/simple" dipy + echo "H5py" + pip install $STD_ARGS --pre --only-binary ":all:" --no-deps -f "https://7933911d6844c6c53a7d-47bd50c35cd79bd838daf386af554a83.ssl.cf2.rackcdn.com" h5py + echo "nilearn" + pip install $STD_ARGS --pre --only-binary ":all:" nilearn echo "VTK" - # Have to use our own version until VTK releases a 3.10 build - wget -q https://osf.io/ajder/download -O vtk-9.1.20220406.dev0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pip install $STD_ARGS --pre --only-binary ":all:" vtk-9.1.20220406.dev0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + pip install $STD_ARGS --pre --only-binary ":all:" vtk python -c "import vtk" echo "PyVista" pip install --progress-bar off git+https://github.com/pyvista/pyvista