diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 5f93c58..0000000 --- a/.coveragerc +++ /dev/null @@ -1,14 +0,0 @@ -[run] -branch = True -source = yasa -include = yasa/* -omit = - */__init__.py - */setup.py - */features.py - */examples/* - */notebooks/* - */tests/* - -[report] -show_missing = True diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml deleted file mode 100644 index 98b2a66..0000000 --- a/.github/workflows/black.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Lint - -on: [push, pull_request] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: psf/black@stable \ No newline at end of file diff --git a/.github/workflows/python_tests.yml b/.github/workflows/python_tests.yml index 84b4e4d..b7c8090 100644 --- a/.github/workflows/python_tests.yml +++ b/.github/workflows/python_tests.yml @@ -2,17 +2,17 @@ name: Python tests on: push: - branches: [master, develop] + branches: [master] pull_request: - branches: [master, develop] + branches: [master] jobs: build: strategy: fail-fast: false matrix: - platform: [ubuntu-latest, windows-latest] # macos-latest - python-version: ["3.9", "3.10", "3.11"] + platform: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.9", "3.10", "3.11", "3.12"] runs-on: ${{ matrix.platform }} @@ -20,27 +20,29 @@ jobs: FORCE_COLOR: true steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + - name: Install libomp (macOS) + if: ${{ matrix.platform == 'macos-latest' }} + run: brew install libomp + - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -r requirements-test.txt - pip install . + pip install ".[test]" - name: Test with pytest run: | - pytest --cov --cov-report=xml --cov-config=setup.cfg --verbose + pytest --cov --cov-report=xml --verbose - name: Upload coverage report if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == 3.9 }} - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v4 with: token: a58a0c62-fb11-4429-977b-65bec01ecb44 file: ./coverage.xml \ No newline at end of file diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 0000000..6251dfa --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,13 @@ +name: Ruff +on: [push, pull_request] +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: "Linting" + uses: astral-sh/ruff-action@v1 + - name: "Formatting" + uses: astral-sh/ruff-action@v1 + with: + args: "format --check" diff --git a/MANIFEST.in b/MANIFEST.in index a783d04..14a8d2f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,21 +1,6 @@ -# Add README, LICENSE and requirements : +# Add README and LICENSE : include README.rst include LICENSE -include requirements.txt # Add trained classifiers -# v0.4.0 -include yasa/classifiers/clf_eeg_lgb_0.4.0.joblib -include yasa/classifiers/clf_eeg+eog_lgb_0.4.0.joblib -include yasa/classifiers/clf_eeg+eog+emg_lgb_0.4.0.joblib -include yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.4.0.joblib - -# v0.5.0 -include yasa/classifiers/clf_eeg_lgb_0.5.0.joblib -include yasa/classifiers/clf_eeg+demo_lgb_0.5.0.joblib -include yasa/classifiers/clf_eeg+eog_lgb_0.5.0.joblib -include yasa/classifiers/clf_eeg+eog+demo_lgb_0.5.0.joblib -include yasa/classifiers/clf_eeg+emg_lgb_0.5.0.joblib -include yasa/classifiers/clf_eeg+emg+demo_lgb_0.5.0.joblib -include yasa/classifiers/clf_eeg+eog+emg_lgb_0.5.0.joblib -include yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.5.0.joblib \ No newline at end of file +recursive-include yasa/classifiers/ *.joblib diff --git a/README.rst b/README.rst index f93f088..c4fa2c2 100644 --- a/README.rst +++ b/README.rst @@ -14,6 +14,10 @@ .. image:: https://pepy.tech/badge/yasa :target: https://pepy.tech/badge/yasa +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Ruff + ---------------- .. figure:: /docs/pictures/yasa_logo.png @@ -48,6 +52,15 @@ Alternatively, YASA can be installed with conda: conda config --set channel_priority strict conda install yasa +To build and install from source, clone this repository or download the source archive and decompress the files + +.. code-block:: shell + + cd yasa + pip install ".[test]" # install the package + pip install --editable ".[test]" # or editable install + pytest # test the package + **What are the prerequisites for using YASA?** To use YASA, all you need is: diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 96e1205..0000000 --- a/codecov.yml +++ /dev/null @@ -1,2 +0,0 @@ -codecov: - token: a58a0c62-fb11-4429-977b-65bec01ecb44 diff --git a/docs/conf.py b/docs/conf.py index d622f17..ccd887d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,9 +3,11 @@ import os import sys import time -import yasa + import sphinx_bootstrap_theme +import yasa + # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. diff --git a/docs/contributing.rst b/docs/contributing.rst index 25da5a5..a131c96 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -12,11 +12,11 @@ Code guidelines Before starting new code, we highly recommend opening an issue on `GitHub `_ to discuss potential changes. -* Please use standard `pep8 `_ and `flake8 `_ Python style guidelines. To test that your code complies with those, you can run: +* Please use standard `pep8 `_ and `flake8 `_ Python style guidelines. YASA uses `ruff `_ for code formatting. Before submitting a PR, please make sure to run the following command in the root folder of YASA: .. code-block:: bash - $ flake8 + $ ruff format --line-length=100 * Use `NumPy style `_ for docstrings. Follow existing examples for simplest guidance. diff --git a/docs/index.rst b/docs/index.rst index 8499106..cd06a17 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,6 +14,10 @@ .. image:: https://pepy.tech/badge/yasa :target: https://pepy.tech/badge/yasa +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Ruff + ---------------- .. figure:: /pictures/yasa_logo.png @@ -48,6 +52,15 @@ Alternatively, YASA can be installed with conda: conda config --set channel_priority strict conda install yasa +To build and install from source, clone this repository or download the source archive and decompress the files + +.. code-block:: shell + + cd yasa + pip install ".[test]" # install the package + pip install -e ".[test]" # or editable install + pytest # test the package + **What are the prerequisites for using YASA?** To use YASA, all you need is: diff --git a/push_pypi.md b/push_pypi.md new file mode 100644 index 0000000..24f8346 --- /dev/null +++ b/push_pypi.md @@ -0,0 +1,6 @@ +# Build and upload a new version of YASA + +```bash +python -m build +twine upload dist/yasa- +``` diff --git a/pyproject.toml b/pyproject.toml index 67a6d04..f2c02bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,132 @@ -[tool.black] +[build-system] +requires = ["setuptools>=70.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "yasa" +description = "YASA: Analysis of polysomnography recordings." +readme = "README.rst" +license = {text = "BSD (3-clause)"} +authors = [ + {name = "Raphael Vallat", email = "raphaelvallat9@gmail.com"}, + {name = "Remington Mallett", email = "mallett.remy@gmail.com"}, +] +maintainers = [ + {name = "Raphael Vallat", email = "raphaelvallat9@gmail.com"}, + {name = "Remington Mallett", email = "mallett.remy@gmail.com"}, +] +classifiers = [ + "Intended Audience :: Science/Research", + "Operating System :: MacOS", + "Operating System :: POSIX", + "Operating System :: Unix", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dynamic = ["version"] +requires-python = ">=3.9" +dependencies = [ + "numpy>=1.18.1", + "scipy", + "pandas", + "matplotlib", + "seaborn", + "mne>=1.3", + "numba>=0.57.1", + "antropy", + "scikit-learn", + "tensorpac>=0.6.5", + "pyriemann>=0.2.7", + "sleepecg>=0.5.0", + "setuptools>=70", + "lspopt", + "ipywidgets", + "joblib", + "lightgbm", +] + +[project.optional-dependencies] +test = [ + "pytest>=6", + "pytest-cov", + # Ensure coverage is new enough for `source_pkgs`. + "coverage[toml]>=5.3", + "ruff" +] +docs = [ + "sphinx>7.0.0", + "pydata_sphinx_theme", + "numpydoc", + "sphinx-copybutton", + "sphinx-design", + "sphinx-notfound-page", +] + +[project.urls] +Homepage = "https://github.com/raphaelvallat/yasa/" +Downloads = "https://github.com/raphaelvallat/yasa/" + +[tool.setuptools] +py-modules = ["yasa"] +include-package-data = true + +[tool.setuptools.package-data] +yasa = [ + "classifiers/*.joblib", +] + +[tool.setuptools.packages.find] +namespaces = false +where = ["src"] + +[tool.setuptools.dynamic] +version = {attr = "yasa.__version__"} + +[tool.pytest.ini_options] +minversion = "6.0" +addopts = "--showlocals --durations=10 --maxfail=2 --cov" +doctest_optionflags= ["NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"] +filterwarnings = [ + "ignore::UserWarning", + "ignore::RuntimeWarning", + "ignore::FutureWarning", +] +markers = ["slow"] + +[tool.coverage.run] +branch = true +omit = [ + "*/tests/*", +] +source_pkgs = ["yasa"] + +[tool.coverage.paths] +source = ["src"] + +[tool.coverage.report] +show_missing = true +# sort = "Cover" + +[tool.ruff] line-length = 100 -target-version = ['py311'] -include = '\.pyi?$' \ No newline at end of file +target-version = "py311" +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +lint.select = ["E4", "E7", "E9", "F", "I", "NPY201"] +exclude = [ + "notebooks", # Skip jupyter notebook examples + "docs", +] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401", "F403"] # Ignore star and unused import violations for __init__.py files + +[tool.ruff.lint.pydocstyle] +convention = "numpy" + +[tool.ruff.format] +docstring-code-format = false +docstring-code-line-length = 90 diff --git a/requirements-test.txt b/requirements-test.txt deleted file mode 100644 index 9b95db4..0000000 --- a/requirements-test.txt +++ /dev/null @@ -1,4 +0,0 @@ -pytest>=7.2.0 -codecov -pytest-cov -pytest-sugar \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7d857e4..0000000 --- a/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ -numpy>=1.18.1 -scipy -pandas -mne>=1.3 -numba>=0.57.1 -matplotlib -ipywidgets -seaborn>=0.12.0 -lspopt -tensorpac>=0.6.5 -scikit-learn -pyriemann>=0.2.7 -sleepecg>=0.5.0 -joblib -antropy -lightgbm diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 5b99974..0000000 --- a/setup.cfg +++ /dev/null @@ -1,26 +0,0 @@ -[aliases] -test=pytest - -[tool:pytest] -addopts = --showlocals --durations=10 --cov -markers = - slow: mark a test as slow. -filterwarnings = - ignore:the matrix subclass:PendingDeprecationWarning -testpaths = - yasa/tests/ - -[flake8] -max-line-length = 100 -ignore = N806, N803, D107, D200, D205, D400, D401, D412, W504, E203 -exclude = - .git, - __pycache__, - docs, - tests, - __init__.py, - plotting.py, - examples, - notebooks, - setup.py, -statistics=True diff --git a/setup.py b/setup.py deleted file mode 100644 index 307a047..0000000 --- a/setup.py +++ /dev/null @@ -1,76 +0,0 @@ -#! /usr/bin/env python -# -# Copyright (C) 2018 Raphael Vallat - -DESCRIPTION = "YASA: Analysis of polysomnography recordings." -LONG_DESCRIPTION = """YASA (Yet Another Spindle Algorithm) : an open-source Python package to analyze polysomnographic sleep recordings. -""" - -DISTNAME = "yasa" -MAINTAINER = "Raphael Vallat" -MAINTAINER_EMAIL = "raphaelvallat9@gmail.com" -URL = "https://github.com/raphaelvallat/yasa/" -LICENSE = "BSD (3-clause)" -DOWNLOAD_URL = "https://github.com/raphaelvallat/yasa/" -VERSION = "0.6.5" -PACKAGE_DATA = {"yasa.data.icons": ["*.svg"]} - -INSTALL_REQUIRES = [ - "numpy>=1.18.1", - "scipy", - "pandas", - "matplotlib", - "seaborn", - "mne>=1.3", - "numba>=0.57.1", - "antropy", - "scikit-learn", - "tensorpac>=0.6.5", - "pyriemann>=0.2.7", - "sleepecg>=0.5.0", - "lspopt", - "ipywidgets", - "joblib", - "lightgbm", -] - -PACKAGES = [ - "yasa", -] - -CLASSIFIERS = [ - "Intended Audience :: Science/Research", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "License :: OSI Approved :: BSD License", - "Operating System :: POSIX", - "Operating System :: Unix", - "Operating System :: MacOS", -] - -try: - from setuptools import setup -except ImportError: - from distutils.core import setup - -if __name__ == "__main__": - setup( - name=DISTNAME, - author=MAINTAINER, - author_email=MAINTAINER_EMAIL, - maintainer=MAINTAINER, - maintainer_email=MAINTAINER_EMAIL, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - license=LICENSE, - url=URL, - version=VERSION, - download_url=DOWNLOAD_URL, - install_requires=INSTALL_REQUIRES, - include_package_data=True, - packages=PACKAGES, - package_data=PACKAGE_DATA, - classifiers=CLASSIFIERS, - ) diff --git a/yasa/__init__.py b/src/yasa/__init__.py similarity index 99% rename from yasa/__init__.py rename to src/yasa/__init__.py index a750a04..93f92f7 100644 --- a/yasa/__init__.py +++ b/src/yasa/__init__.py @@ -1,4 +1,5 @@ import logging + from .detection import * from .evaluation import * from .features import * diff --git a/yasa/classifiers/__init__.py b/src/yasa/classifiers/__init__.py similarity index 100% rename from yasa/classifiers/__init__.py rename to src/yasa/classifiers/__init__.py diff --git a/yasa/classifiers/clf_eeg+demo_lgb_0.5.0.joblib b/src/yasa/classifiers/clf_eeg+demo_lgb_0.5.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+demo_lgb_0.5.0.joblib rename to src/yasa/classifiers/clf_eeg+demo_lgb_0.5.0.joblib diff --git a/yasa/classifiers/clf_eeg+emg+demo_lgb_0.5.0.joblib b/src/yasa/classifiers/clf_eeg+emg+demo_lgb_0.5.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+emg+demo_lgb_0.5.0.joblib rename to src/yasa/classifiers/clf_eeg+emg+demo_lgb_0.5.0.joblib diff --git a/yasa/classifiers/clf_eeg+emg_lgb_0.5.0.joblib b/src/yasa/classifiers/clf_eeg+emg_lgb_0.5.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+emg_lgb_0.5.0.joblib rename to src/yasa/classifiers/clf_eeg+emg_lgb_0.5.0.joblib diff --git a/yasa/classifiers/clf_eeg+eog+demo_lgb_0.5.0.joblib b/src/yasa/classifiers/clf_eeg+eog+demo_lgb_0.5.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+eog+demo_lgb_0.5.0.joblib rename to src/yasa/classifiers/clf_eeg+eog+demo_lgb_0.5.0.joblib diff --git a/yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.4.0.joblib b/src/yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.4.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.4.0.joblib rename to src/yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.4.0.joblib diff --git a/yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.5.0.joblib b/src/yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.5.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.5.0.joblib rename to src/yasa/classifiers/clf_eeg+eog+emg+demo_lgb_0.5.0.joblib diff --git a/yasa/classifiers/clf_eeg+eog+emg_lgb_0.4.0.joblib b/src/yasa/classifiers/clf_eeg+eog+emg_lgb_0.4.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+eog+emg_lgb_0.4.0.joblib rename to src/yasa/classifiers/clf_eeg+eog+emg_lgb_0.4.0.joblib diff --git a/yasa/classifiers/clf_eeg+eog+emg_lgb_0.5.0.joblib b/src/yasa/classifiers/clf_eeg+eog+emg_lgb_0.5.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+eog+emg_lgb_0.5.0.joblib rename to src/yasa/classifiers/clf_eeg+eog+emg_lgb_0.5.0.joblib diff --git a/yasa/classifiers/clf_eeg+eog_lgb_0.4.0.joblib b/src/yasa/classifiers/clf_eeg+eog_lgb_0.4.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+eog_lgb_0.4.0.joblib rename to src/yasa/classifiers/clf_eeg+eog_lgb_0.4.0.joblib diff --git a/yasa/classifiers/clf_eeg+eog_lgb_0.5.0.joblib b/src/yasa/classifiers/clf_eeg+eog_lgb_0.5.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg+eog_lgb_0.5.0.joblib rename to src/yasa/classifiers/clf_eeg+eog_lgb_0.5.0.joblib diff --git a/yasa/classifiers/clf_eeg_lgb_0.4.0.joblib b/src/yasa/classifiers/clf_eeg_lgb_0.4.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg_lgb_0.4.0.joblib rename to src/yasa/classifiers/clf_eeg_lgb_0.4.0.joblib diff --git a/yasa/classifiers/clf_eeg_lgb_0.5.0.joblib b/src/yasa/classifiers/clf_eeg_lgb_0.5.0.joblib similarity index 100% rename from yasa/classifiers/clf_eeg_lgb_0.5.0.joblib rename to src/yasa/classifiers/clf_eeg_lgb_0.5.0.joblib diff --git a/yasa/detection.py b/src/yasa/detection.py similarity index 99% rename from yasa/detection.py rename to src/yasa/detection.py index 9d9bcac..18e6204 100644 --- a/yasa/detection.py +++ b/src/yasa/detection.py @@ -7,29 +7,29 @@ - License: BSD 3-Clause License """ -import mne import logging +from collections import OrderedDict + +import mne import numpy as np import pandas as pd -from scipy import signal from mne.filter import filter_data -from collections import OrderedDict -from scipy.interpolate import interp1d +from scipy import signal from scipy.fftpack import next_fast_len +from scipy.interpolate import interp1d from sklearn.ensemble import IsolationForest -from .spectral import stft_power +from .io import is_pyriemann_installed, is_tensorpac_installed, set_log_level from .numba import _detrend, _rms -from .io import set_log_level, is_tensorpac_installed, is_pyriemann_installed from .others import ( - moving_transform, - trimbothstd, - get_centered_indices, - sliding_window, _merge_close, _zerocrossings, + get_centered_indices, + moving_transform, + sliding_window, + trimbothstd, ) - +from .spectral import stft_power logger = logging.getLogger("yasa") @@ -86,7 +86,7 @@ def _check_data_hypno(data, sf=None, ch_names=None, hypno=None, include=None, ch include = np.atleast_1d(np.asarray(include)) assert include.size >= 1, "`include` must have at least one element." assert hypno.dtype.kind == include.dtype.kind, "hypno and include must have same dtype" - assert np.in1d(hypno, include).any(), ( + assert np.isin(hypno, include).any(), ( "None of the stages specified " "in `include` are present in " "hypno." ) @@ -110,7 +110,7 @@ def _check_data_hypno(data, sf=None, ch_names=None, hypno=None, include=None, ch # 5) Create sleep stage vector mask if hypno is not None: - mask = np.in1d(hypno, include) + mask = np.isin(hypno, include) else: mask = np.ones(n_samples, dtype=bool) @@ -458,8 +458,8 @@ def plot_average( **kwargs, ): """Plot the average event (not for REM, spindles & SW only)""" - import seaborn as sns import matplotlib.pyplot as plt + import seaborn as sns df_sync = self.get_sync_events( center=center, time_before=time_before, time_after=time_after, filt=filt, mask=mask @@ -485,8 +485,8 @@ def plot_average( def plot_detection(self): """Plot an overlay of the detected events on the signal.""" - import matplotlib.pyplot as plt import ipywidgets as ipy + import matplotlib.pyplot as plt # Define mask sf = self._sf @@ -1828,11 +1828,9 @@ def sw_detect( if coupling: # Get phase and amplitude for each centered epoch time_before = time_after = coupling_params["time"] - assert float( - sf * time_before - ).is_integer(), ( - "Invalid time parameter for coupling. Must be a whole number of samples." - ) + assert ( + float(sf * time_before).is_integer() + ), "Invalid time parameter for coupling. Must be a whole number of samples." bef = int(sf * time_before) aft = int(sf * time_after) # Center of each epoch is defined as the negative peak of the SW @@ -2782,8 +2780,8 @@ def plot_average( **kwargs : dict Optional argument that are passed to :py:func:`seaborn.lineplot`. """ - import seaborn as sns import matplotlib.pyplot as plt + import seaborn as sns df_sync = self.get_sync_events( center=center, time_before=time_before, time_after=time_after, filt=filt, mask=mask @@ -3026,8 +3024,8 @@ def art_detect( if method in ["cov", "covar", "covariance", "riemann", "potato"]: method = "covar" is_pyriemann_installed() - from pyriemann.estimation import Covariances, Shrinkage from pyriemann.clustering import Potato + from pyriemann.estimation import Covariances, Shrinkage # Must have at least 4 channels to use method='covar' if n_chan <= 4: diff --git a/yasa/evaluation.py b/src/yasa/evaluation.py similarity index 99% rename from yasa/evaluation.py rename to src/yasa/evaluation.py index 007d4db..e67cb7b 100644 --- a/yasa/evaluation.py +++ b/src/yasa/evaluation.py @@ -16,7 +16,6 @@ import scipy.stats as sps import sklearn.metrics as skm - logger = logging.getLogger("yasa") __all__ = [ @@ -219,7 +218,7 @@ def __init__(self, ref_hyps, obs_hyps): # Generate some mapping dictionaries to be used later in class methods skm_labels = np.unique(data).tolist() # all unique YASA integer codes in this hypno - skm2yasa_map = {i: l for i, l in enumerate(skm_labels)} # skm order to YASA integers + skm2yasa_map = {i: lab for i, lab in enumerate(skm_labels)} # skm order to YASA integers yasa2yasa_map = ref_hyps[sleep_ids[0]].mapping_int.copy() # YASA integer to YASA string # Set attributes @@ -908,7 +907,6 @@ def __init__( verbose=True, bootstrap_kwargs={}, ): - restricted_bootstrap_kwargs = ["confidence_level", "vectorized", "paired"] assert isinstance(ref_data, pd.DataFrame), "`ref_data` must be a pandas DataFrame" @@ -1293,7 +1291,7 @@ def get_table(self, bias_method="auto", loa_method="auto", ci_method="auto", fst "bias_regr": "{bias_intercept_center:.2f} + {bias_slope_center:.2f}x", "loa_parm": "{lloa_parm_center:.2f}, {uloa_parm_center:.2f}", "loa_regr": ( - "Bias \u00B1 {loa_regr_agreement:.2f} " + "Bias \u00b1 {loa_regr_agreement:.2f} " "* ({loa_intercept_center:.2f} + {loa_slope_center:.2f}x)" ), "bias_parm_ci": ("[{bias_parm_lower:.2f}, {bias_parm_upper:.2f}]"), diff --git a/yasa/features.py b/src/yasa/features.py similarity index 99% rename from yasa/features.py rename to src/yasa/features.py index cd2773e..42b1e95 100644 --- a/yasa/features.py +++ b/src/yasa/features.py @@ -17,15 +17,16 @@ Use at your own risk. """ -import mne -import yasa import logging + +import antropy as ant +import mne import numpy as np import pandas as pd -import antropy as ant import scipy.signal as sp_sig import scipy.stats as sp_stats +import yasa logger = logging.getLogger("yasa") diff --git a/yasa/heart.py b/src/yasa/heart.py similarity index 99% rename from yasa/heart.py rename to src/yasa/heart.py index fe7447e..2849e6a 100644 --- a/yasa/heart.py +++ b/src/yasa/heart.py @@ -6,12 +6,13 @@ """ import logging + import numpy as np import pandas as pd -from .hypno import hypno_find_periods from .detection import _check_data_hypno -from .io import set_log_level, is_sleepecg_installed +from .hypno import hypno_find_periods +from .io import is_sleepecg_installed, set_log_level logger = logging.getLogger("yasa") diff --git a/yasa/hypno.py b/src/yasa/hypno.py similarity index 99% rename from yasa/hypno.py rename to src/yasa/hypno.py index e782361..a6d72db 100644 --- a/yasa/hypno.py +++ b/src/yasa/hypno.py @@ -2,17 +2,19 @@ Hypnogram-related functions and class. """ -import mne import logging +import mne + # import warnings import numpy as np import pandas as pd +from pandas.api.types import CategoricalDtype + +from yasa.evaluation import EpochByEpochAgreement from yasa.io import set_log_level from yasa.plotting import plot_hypnogram from yasa.sleepstats import transition_matrix -from yasa.evaluation import EpochByEpochAgreement -from pandas.api.types import CategoricalDtype __all__ = [ "Hypnogram", diff --git a/yasa/io.py b/src/yasa/io.py similarity index 97% rename from yasa/io.py rename to src/yasa/io.py index 1e0aaf5..9d0d2b0 100644 --- a/yasa/io.py +++ b/src/yasa/io.py @@ -1,9 +1,7 @@ -"""Helper functions for YASA (e.g. logger) -""" +"""Helper functions for YASA (e.g. logger)""" import logging - LOGGING_TYPES = dict( DEBUG=logging.DEBUG, INFO=logging.INFO, diff --git a/yasa/numba.py b/src/yasa/numba.py similarity index 100% rename from yasa/numba.py rename to src/yasa/numba.py diff --git a/yasa/others.py b/src/yasa/others.py similarity index 99% rename from yasa/others.py rename to src/yasa/others.py index af80928..1be821f 100644 --- a/yasa/others.py +++ b/src/yasa/others.py @@ -3,9 +3,11 @@ """ import logging + import numpy as np from scipy.interpolate import interp1d -from .numba import _slope_lstsq, _covar, _corr, _rms + +from .numba import _corr, _covar, _rms, _slope_lstsq logger = logging.getLogger("yasa") @@ -472,7 +474,7 @@ def get_centered_indices(data, idx, npts_before, npts_after): def rng(x): """Create a range before and after a given value.""" - return np.arange(x - npts_before, x + npts_after + 1, dtype="int") + return np.arange(x[0] - npts_before, x[0] + npts_after + 1, dtype="int") idx_ep = np.apply_along_axis(rng, 1, idx[..., np.newaxis]) # We drop the events for which the indices exceed data diff --git a/yasa/plotting.py b/src/yasa/plotting.py similarity index 99% rename from yasa/plotting.py rename to src/yasa/plotting.py index 70fab24..9177617 100644 --- a/yasa/plotting.py +++ b/src/yasa/plotting.py @@ -2,14 +2,14 @@ Plotting functions of YASA. """ +import matplotlib.dates as mdates +import matplotlib.pyplot as plt import mne import numpy as np import pandas as pd import seaborn as sns -import matplotlib.pyplot as plt -import matplotlib.dates as mdates from lspopt import spectrogram_lspopt -from matplotlib.colors import Normalize, ListedColormap +from matplotlib.colors import ListedColormap, Normalize __all__ = ["plot_hypnogram", "plot_spectrogram", "topoplot"] diff --git a/yasa/sleepstats.py b/src/yasa/sleepstats.py similarity index 100% rename from yasa/sleepstats.py rename to src/yasa/sleepstats.py diff --git a/yasa/spectral.py b/src/yasa/spectral.py similarity index 99% rename from yasa/spectral.py rename to src/yasa/spectral.py index 139d225..3bf5723 100644 --- a/yasa/spectral.py +++ b/src/yasa/spectral.py @@ -3,13 +3,15 @@ 1D and 2D EEG data. """ -import mne import logging + +import mne import numpy as np import pandas as pd from scipy import signal from scipy.integrate import simpson from scipy.interpolate import RectBivariateSpline + from .io import set_log_level logger = logging.getLogger("yasa") @@ -151,7 +153,7 @@ def bandpower( assert hypno.size == npts, "Hypno must have same size as data.shape[1]" assert include.size >= 1, "`include` must have at least one element." assert hypno.dtype.kind == include.dtype.kind, "hypno and include must have same dtype" - assert np.in1d( + assert np.isin( hypno, include ).any(), "None of the stages specified in `include` are present in hypno." # Initialize empty dataframe and loop over stages diff --git a/yasa/staging.py b/src/yasa/staging.py similarity index 99% rename from yasa/staging.py rename to src/yasa/staging.py index 7b6df76..e66218b 100644 --- a/yasa/staging.py +++ b/src/yasa/staging.py @@ -1,17 +1,19 @@ """Automatic sleep staging of polysomnography data.""" -import os -import mne import glob -import joblib import logging +import os + +import antropy as ant +import joblib +import matplotlib.pyplot as plt +import mne import numpy as np import pandas as pd -import antropy as ant import scipy.signal as sp_sig import scipy.stats as sp_stats -import matplotlib.pyplot as plt from mne.filter import filter_data +from scipy.integrate import trapezoid from sklearn.preprocessing import robust_scale from .others import sliding_window @@ -289,7 +291,7 @@ def fit(self): # Add total power idx_broad = np.logical_and(freqs >= freq_broad[0], freqs <= freq_broad[1]) dx = freqs[1] - freqs[0] - feat["abspow"] = np.trapz(psd[:, idx_broad], dx=dx) + feat["abspow"] = trapezoid(psd[:, idx_broad], dx=dx) # Calculate entropy and fractal dimension features feat["perm"] = np.apply_along_axis(ant.perm_entropy, axis=1, arr=epochs, normalize=True) diff --git a/yasa/tests/__init__.py b/tests/__init__.py similarity index 100% rename from yasa/tests/__init__.py rename to tests/__init__.py diff --git a/yasa/tests/test_detection.py b/tests/test_detection.py similarity index 99% rename from yasa/tests/test_detection.py rename to tests/test_detection.py index a9b893e..19f8f47 100644 --- a/yasa/tests/test_detection.py +++ b/tests/test_detection.py @@ -1,15 +1,17 @@ """Test the functions in yasa/spectral.py.""" -import mne -import pytest import unittest -import numpy as np -import pandas as pd from itertools import product + import matplotlib.pyplot as plt +import mne +import numpy as np +import pandas as pd +import pytest from mne.filter import filter_data + +from yasa.detection import art_detect, compare_detection, rem_detect, spindles_detect, sw_detect from yasa.hypno import hypno_str_to_int, hypno_upsample_to_data -from yasa.detection import spindles_detect, sw_detect, rem_detect, art_detect, compare_detection ############################################################################## # DATA LOADING diff --git a/yasa/tests/test_heart.py b/tests/test_heart.py similarity index 99% rename from yasa/tests/test_heart.py rename to tests/test_heart.py index 0f3a383..0aa8dd0 100644 --- a/yasa/tests/test_heart.py +++ b/tests/test_heart.py @@ -1,7 +1,9 @@ """Test the functions in the yasa/heart.py file.""" import unittest + import numpy as np + from yasa.heart import hrv_stage # Load data diff --git a/yasa/tests/test_hypno.py b/tests/test_hypno.py similarity index 99% rename from yasa/tests/test_hypno.py rename to tests/test_hypno.py index bb7cb12..7c5ee98 100644 --- a/yasa/tests/test_hypno.py +++ b/tests/test_hypno.py @@ -1,22 +1,23 @@ """Test the functions in the yasa/hypno.py file.""" -import mne -import pytest import unittest + +import mne import numpy as np import pandas as pd +import pytest from pandas.testing import assert_frame_equal + +from yasa.hypno import hypno_find_periods as hfp from yasa.hypno import ( - hypno_str_to_int, - hypno_int_to_str, - hypno_upsample_to_sf, hypno_fit_to_data, + hypno_int_to_str, + hypno_str_to_int, hypno_upsample_to_data, + hypno_upsample_to_sf, simulate_hypnogram, ) -from yasa.hypno import hypno_find_periods as hfp - hypno = np.array([0, 0, 0, 1, 2, 2, 3, 3, 4]) hypno_txt = np.array(["W", "W", "W", "N1", "N2", "N2", "N3", "N3", "R"]) diff --git a/yasa/tests/test_hypnoclass.py b/tests/test_hypnoclass.py similarity index 99% rename from yasa/tests/test_hypnoclass.py rename to tests/test_hypnoclass.py index 7adf4fd..ed6eeb3 100644 --- a/yasa/tests/test_hypnoclass.py +++ b/tests/test_hypnoclass.py @@ -1,12 +1,14 @@ """Test the class Hypnogram.""" -import mne -import pytest import unittest + +import matplotlib.pyplot as plt +import mne import numpy as np import pandas as pd -import matplotlib.pyplot as plt -from yasa.hypno import simulate_hypnogram, Hypnogram, hypno_str_to_int +import pytest + +from yasa.hypno import Hypnogram, hypno_str_to_int, simulate_hypnogram def create_raw(npts, ch_names=["F4-M1", "F3-M2"], sf=100): diff --git a/yasa/tests/test_io.py b/tests/test_io.py similarity index 99% rename from yasa/tests/test_io.py rename to tests/test_io.py index 486a598..92aec7e 100644 --- a/yasa/tests/test_io.py +++ b/tests/test_io.py @@ -1,13 +1,15 @@ """Test I/O.""" -import pytest import logging import unittest + +import pytest + from yasa.io import ( + is_pyriemann_installed, is_sleepecg_installed, - set_log_level, is_tensorpac_installed, - is_pyriemann_installed, + set_log_level, ) logger = logging.getLogger("yasa") diff --git a/yasa/tests/test_numba.py b/tests/test_numba.py similarity index 94% rename from yasa/tests/test_numba.py rename to tests/test_numba.py index b9533a8..24bec24 100644 --- a/yasa/tests/test_numba.py +++ b/tests/test_numba.py @@ -1,9 +1,11 @@ """Test the functions in the yasa/numba.py file.""" import unittest + import numpy as np from scipy.signal import detrend -from yasa.numba import _corr, _covar, _rms, _slope_lstsq, _detrend + +from yasa.numba import _corr, _covar, _detrend, _rms, _slope_lstsq class TestNumba(unittest.TestCase): diff --git a/yasa/tests/test_others.py b/tests/test_others.py similarity index 99% rename from yasa/tests/test_others.py rename to tests/test_others.py index a5d5830..11b426e 100644 --- a/yasa/tests/test_others.py +++ b/tests/test_others.py @@ -1,20 +1,21 @@ """Test the functions in the yasa/others.py file.""" -import mne import unittest -import numpy as np from itertools import product + +import mne +import numpy as np from mne.filter import filter_data from yasa.hypno import hypno_str_to_int, hypno_upsample_to_data from yasa.others import ( - moving_transform, - trimbothstd, - get_centered_indices, - sliding_window, + _index_to_events, _merge_close, _zerocrossings, - _index_to_events, + get_centered_indices, + moving_transform, + sliding_window, + trimbothstd, ) # Load data diff --git a/yasa/tests/test_plotting.py b/tests/test_plotting.py similarity index 97% rename from yasa/tests/test_plotting.py rename to tests/test_plotting.py index 078c059..57c327f 100644 --- a/yasa/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -1,12 +1,13 @@ """Test the functions in the yasa/plotting.py file.""" -import pytest import unittest + +import matplotlib.pyplot as plt import numpy as np import pandas as pd -import matplotlib.pyplot as plt + from yasa.hypno import simulate_hypnogram -from yasa.plotting import topoplot, plot_hypnogram +from yasa.plotting import plot_hypnogram, topoplot class TestPlotting(unittest.TestCase): diff --git a/yasa/tests/test_sleepstats.py b/tests/test_sleepstats.py similarity index 97% rename from yasa/tests/test_sleepstats.py rename to tests/test_sleepstats.py index cda5373..812c57e 100644 --- a/yasa/tests/test_sleepstats.py +++ b/tests/test_sleepstats.py @@ -1,9 +1,11 @@ """Test the functions in the yasa/sleepstats.py file.""" import unittest + import numpy as np import pandas as pd -from yasa.sleepstats import transition_matrix, sleep_statistics + +from yasa.sleepstats import sleep_statistics, transition_matrix hypno = np.array([0, 0, 0, 1, 2, 2, 3, 3, 2, 2, 2, 0, 0, 0, 2, 2, 4, 4, 0, 0]) diff --git a/yasa/tests/test_spectral.py b/tests/test_spectral.py similarity index 99% rename from yasa/tests/test_spectral.py rename to tests/test_spectral.py index 458b300..11082ba 100644 --- a/yasa/tests/test_spectral.py +++ b/tests/test_spectral.py @@ -1,15 +1,16 @@ """Test the functions in the yasa/spectral.py file.""" -import mne -import pytest import unittest -import numpy as np from itertools import product -from scipy.signal import welch + import matplotlib.pyplot as plt +import mne +import numpy as np +import pytest +from scipy.signal import welch -from yasa.plotting import plot_spectrogram from yasa.hypno import hypno_str_to_int, hypno_upsample_to_data +from yasa.plotting import plot_spectrogram from yasa.spectral import ( bandpower, bandpower_from_psd, diff --git a/yasa/tests/test_staging.py b/tests/test_staging.py similarity index 99% rename from yasa/tests/test_staging.py rename to tests/test_staging.py index 8a07629..6796a7f 100644 --- a/yasa/tests/test_staging.py +++ b/tests/test_staging.py @@ -1,9 +1,11 @@ """Test the functions in yasa/staging.py.""" -import mne import unittest -import numpy as np + import matplotlib.pyplot as plt +import mne +import numpy as np + from yasa.staging import SleepStaging ############################################################################## diff --git a/yasa/push_pypi.md b/yasa/push_pypi.md deleted file mode 100644 index b415546..0000000 --- a/yasa/push_pypi.md +++ /dev/null @@ -1,7 +0,0 @@ -# Build and upload a new version of YASA - -```bash -python setup.py sdist -python setup.py sdist bdist_wheel --universal -twine upload dist/* -```