diff --git a/.github/workflows/check_pylint_diff.sh b/.github/workflows/check_pylint_diff.sh deleted file mode 100755 index b53aae6eb..000000000 --- a/.github/workflows/check_pylint_diff.sh +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env bash -# Copyright (C) 2016 Kernc, Google Inc., authors, and contributors -# Licensed under http://www.apache.org/licenses/LICENSE-2.0 -# Created By: miha@reciprocitylabs.com - -set -o pipefail -set -o nounset -set -o errexit - -ARG1=${1:-} -GIT_REPO="$(pwd)" -TMP_REPO="$GIT_REPO/$(mktemp -d pylint_diff.XXXXXXX)" -CACHE_DIR="$GIT_REPO/.pylint_cache" -UNCOMMITED_PATCH="$TMP_REPO/uncommited.patch" -SCRIPT=$(basename "$0") -PYLINT="$(command -v pylint 2>/dev/null || true)" -RADON="$(command -v radon 2>/dev/null || true)" -PYLINT_ARGS="--msg-template='{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}'" -RADON_ARGS='cc --min C --no-assert --show-closures --show-complexity --average' - -trap "status=\$?; cd '$GIT_REPO'; rm -rf '$TMP_REPO'; exit \$status" EXIT -mkdir -p "$CACHE_DIR" - - -print_help () -{ - echo " -Usage: $SCRIPT [TEST_COMMIT | -h] - -This script will compare pylint error count from two different commits. -Note: all changes that are not committed will be ignored. - -The script will work only if the current commit is a merge commit, or if the -second test_commit argument is provided. - -Given the commit tree: - - D---E---F---G---H - \\ / - A---B---C - -- Running '$SCRIPT' on H will check the diff between G and H. -- Running '$SCRIPT F' on H will check the diff between F and H. -- Running '$SCRIPT F' on C will check the diff between E and C. The E commit is - set by the merge base of the current head and the specified commit F. -" - exit 0 -} - -case $ARG1 in -h|--help) print_help ; esac - -if [ ! "$PYLINT$RADON" ]; then - echo 'Error: pylint and/or radon required' - exit 3 -fi - -# Make a local clone: prevents copying of objects -# Handle shallow git clones -is_shallow=$([ -f "$GIT_REPO/.git/shallow" ] && echo true || echo) -if [ "$is_shallow" ]; then - mv "$GIT_REPO/.git/shallow" "$GIT_REPO/.git/shallow-bak" -fi -git clone -q --local --depth=50 "$GIT_REPO" "$TMP_REPO" 2>/dev/null -if [ "$is_shallow" ]; then - mv "$GIT_REPO/.git/shallow-bak" "$GIT_REPO/.git/shallow" - cp "$GIT_REPO/.git/shallow" "$TMP_REPO/.git/shallow" -fi - -# Move over any modified but uncommited files ... -if ! git diff-index --quiet HEAD; then - git stash save -q --keep-index - git stash show -p stash@\{0\} > "$UNCOMMITED_PATCH" - git stash pop -q --index -fi - -cd "$TMP_REPO" - -# ... and commit them -if [ "$(cat "$UNCOMMITED_PATCH" 2>/dev/null || true)" ]; then - git apply "$UNCOMMITED_PATCH" - git commit -a -m 'Commit changed files' - was_dirty='+' -fi >/dev/null 2>&1 - -git reset --hard -q HEAD - -CURRENT_COMMIT=$(git rev-parse HEAD) -if [ "$ARG1" ]; then - PREVIOUS_COMMIT=$(git merge-base HEAD "$ARG1") -else - PREVIOUS_COMMIT=$(git show --pretty=raw HEAD | - awk '/^parent /{ print $2; exit }') -fi - -echo -echo "Comparing commits ${CURRENT_COMMIT:0:10}${was_dirty:-} and ${PREVIOUS_COMMIT:0:10}" - -CHANGED_FILES=$(git diff --name-only $CURRENT_COMMIT $PREVIOUS_COMMIT | - grep "\.py$" || true ) -[ ! "$(command -v md5sum 2>/dev/null)" ] && md5sum() { md5; } # for OS X -CHANGED_FILES_HASH=$(echo "$CHANGED_FILES" | md5sum | cut -d ' ' -f 1) -if [ ! "$CHANGED_FILES" ]; then - echo "No python files changed. Skipping lint checks." - exit 0 -fi - -echo -echo "Comparing files" -echo "===============" -echo "$CHANGED_FILES" -echo - -# Run pylint on the old and new code, to compare the quality. -# If pylint is run multiple times it will store the previous results and show -# the change in quality with a non-negative number if code was improved or not -# changed, and a negative number if more code issues have been introduced. - -checkout () -{ - { git checkout -q "$1" - git reset --hard -q HEAD - } 2>/dev/null -} - -Number_of_issues () -{ - cached="$1" - { cat "$cached" 2>/dev/null || - echo "$CHANGED_FILES" | - xargs "$PYLINT" "$PYLINT_ARGS" | - tee "$cached" - } | awk -F'[\\. ]' '/^Your code has been rated at /{ print $7 }' || true -} - -Cyclomatic_complexity () -{ - cached="$1" - { cat "$cached" 2>/dev/null || - echo "$CHANGED_FILES" | - xargs "$RADON" $RADON_ARGS | - tee "$cached" - } | awk -F'[()]' '/ .+\([0-9]+\)$/ { tot += $2 } END { print tot }' || true -} - -Get_diffable () -{ - sed -E "/$diff_block_end/,\$d" | - sort | - sed -E "s/$match_line_num/$replace_line_num/" -} - -for check in \ - 'Pylint,Number_of_issues,^Report$,^([^:]+:)[0-9]+:,\\1,^\\+' \ - 'radon,Cyclomatic_complexity,^[0-9]+ blocks,^( +[MCF]) [0-9:]+,\\1:,^[+-]' -do - IFS=',' read check \ - func \ - diff_block_end \ - match_line_num \ - replace_line_num \ - show_diff_lines < <(echo "$check") - # If command not available, skip it - if [ ! "$(eval echo \$$(echo $check | tr '[:lower:]' '[:upper:]') )" ]; then - continue - fi - - cached_previous="$CACHE_DIR/previous.$check.$PREVIOUS_COMMIT.$CHANGED_FILES_HASH" - cached_current="$CACHE_DIR/current.$check.$CURRENT_COMMIT.$CHANGED_FILES_HASH" - [ -f "$cached_previous" ] || rm -r "$CACHE_DIR/previous."* 2>/dev/null || true - [ -f "$cached_current" ] || rm -r "$CACHE_DIR/current."* 2>/dev/null || true - - [ -f "$cached_previous" ] || checkout $PREVIOUS_COMMIT - RESULT_PARENT=$($func "$cached_previous") - [ -f "$cached_current" ] || checkout $CURRENT_COMMIT - RESULT_CURRENT=$($func "$cached_current") - - echo - echo "$check result" - echo "=================================================================" - cat "$cached_current" - echo - - echo - echo "$check diff" - echo "=================================================================" - diff --unified=0 --minimal \ - <(Get_diffable < "$cached_previous") \ - <(Get_diffable < "$cached_current") | - grep -E "$show_diff_lines" | tail -n +3 || true - echo - - echo - echo "$check results" - echo "=================================================================" - echo "${func//_/ } on parent commit: $RESULT_PARENT" - echo "${func//_/ } on the pull request: $RESULT_CURRENT ($(printf "%+d" $((RESULT_CURRENT - RESULT_PARENT))))" - echo - - if awk "BEGIN { exit ${RESULT_CURRENT:-0} > ${RESULT_PARENT:-0} ? 0 : 1 }"; then - echo "FAIL: ${func//_/ } got worse" - exit 1 - fi -done - -echo "OK" diff --git a/.github/workflows/lint_workflow.yml b/.github/workflows/lint_workflow.yml index e977eff1c..e1e08a7b5 100644 --- a/.github/workflows/lint_workflow.yml +++ b/.github/workflows/lint_workflow.yml @@ -1,4 +1,4 @@ -name: Lint workflow +name: Lint on: push: @@ -11,17 +11,20 @@ on: jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + steps: - - uses: actions/checkout@v2 - with: - fetch-depth: '2' - - name: Setup Python - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: ${{ matrix.python-version }} - - name: Install Tox - run: pip install tox + - name: Install dependencies + run: | + python -m pip install pre-commit - - name: Run Pylint - run: tox -e pylint-ci + - name: Lint + run: pre-commit run --all-files --show-diff-on-failure diff --git a/.gitignore b/.gitignore index a908c18ef..79b2f6e9a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ __pycache__ # Coverage reports htmlcov/* .coverage +coverage.xml # check_pylint_diff .pylint_cache diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..13b699055 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.14.14 + hooks: + # Run the linter. + - id: ruff-check + args: [ --fix ] + # Run the formatter. + - id: ruff-format \ No newline at end of file diff --git a/doc/pyproject.toml b/doc/pyproject.toml new file mode 100644 index 000000000..42b5a4b6d --- /dev/null +++ b/doc/pyproject.toml @@ -0,0 +1,4 @@ +[tool.ruff] +extend = "../pyproject.toml" +# Exclude conf.py +extend-exclude = ["conf.py"] \ No newline at end of file diff --git a/orangecontrib/spectroscopy/__init__.py b/orangecontrib/spectroscopy/__init__.py index 405dde5ac..f8aedb2f9 100644 --- a/orangecontrib/spectroscopy/__init__.py +++ b/orangecontrib/spectroscopy/__init__.py @@ -16,9 +16,10 @@ def get_sample_datasets_dir(): try: import dask import dask.distributed - dask_client = dask.distributed.Client(processes=False, n_workers=2, - set_as_default=False, - dashboard_address=None) + + dask_client = dask.distributed.Client( + processes=False, n_workers=2, set_as_default=False, dashboard_address=None + ) except ImportError: dask = None - dask_client = None \ No newline at end of file + dask_client = None diff --git a/orangecontrib/spectroscopy/data.py b/orangecontrib/spectroscopy/data.py index c14fb7001..7da14df54 100644 --- a/orangecontrib/spectroscopy/data.py +++ b/orangecontrib/spectroscopy/data.py @@ -1,10 +1,14 @@ # pylint: disable=unused-import +# ruff: noqa: F401 # import for compatibility before the restructure import Orange.data.io # legacy import so that file readers are registered -from orangecontrib.spectroscopy.io.util import \ - _metatable_maplocs, _spectra_from_image, _spectra_from_image_2d,\ - build_spec_table +from orangecontrib.spectroscopy.io.util import ( + _metatable_maplocs, + _spectra_from_image, + _spectra_from_image_2d, + build_spec_table, +) from orangecontrib.spectroscopy.util import getx, spectra_mean @@ -12,8 +16,13 @@ # In the owfile settings, explicitly selected selected readers are stored with # full module and class name. Moving but not adding imports would show # the missing reader error. -from orangecontrib.spectroscopy.io.agilent import AgilentImageReader, AgilentImageIFGReader,\ - agilentMosaicReader, agilentMosaicIFGReader, agilentMosaicTileReader +from orangecontrib.spectroscopy.io.agilent import ( + AgilentImageReader, + AgilentImageIFGReader, + agilentMosaicReader, + agilentMosaicIFGReader, + agilentMosaicTileReader, +) from orangecontrib.spectroscopy.io.ascii import AsciiColReader, AsciiMapReader from orangecontrib.spectroscopy.io.diamond import NXS_STXM_Diamond_I08 from orangecontrib.spectroscopy.io.envi import EnviMapReader @@ -25,6 +34,9 @@ from orangecontrib.spectroscopy.io.omnic import OmnicMapReader, SPCReader, SPAReader from orangecontrib.spectroscopy.io.opus import OPUSReader from orangecontrib.spectroscopy.io.ptir import PTIRFileReader -from orangecontrib.spectroscopy.io.soleil import SelectColumnReader, HDF5Reader_HERMES, \ - HDF5Reader_ROCK +from orangecontrib.spectroscopy.io.soleil import ( + SelectColumnReader, + HDF5Reader_HERMES, + HDF5Reader_ROCK, +) from orangecontrib.spectroscopy.io.wire import WiREReaders diff --git a/orangecontrib/spectroscopy/io/__init__.py b/orangecontrib/spectroscopy/io/__init__.py index a7a97e54a..b043fcdb0 100644 --- a/orangecontrib/spectroscopy/io/__init__.py +++ b/orangecontrib/spectroscopy/io/__init__.py @@ -10,8 +10,13 @@ from .matlab import MatlabReader # Instrument-specific readers -from .agilent import AgilentImageReader, AgilentImageIFGReader, agilentMosaicReader,\ - agilentMosaicIFGReader, agilentMosaicTileReader +from .agilent import ( + AgilentImageReader, + AgilentImageIFGReader, + agilentMosaicReader, + agilentMosaicIFGReader, + agilentMosaicTileReader, +) from .neaspec import NeaReader, NeaReaderGSF, NeaReaderMultiChannel from .omnic import OmnicMapReader, SPAReader, SPCReader from .opus import OPUSReader diff --git a/orangecontrib/spectroscopy/io/agilent.py b/orangecontrib/spectroscopy/io/agilent.py index c0db47059..1fd3c53d4 100644 --- a/orangecontrib/spectroscopy/io/agilent.py +++ b/orangecontrib/spectroscopy/io/agilent.py @@ -5,10 +5,19 @@ import numpy as np from Orange.data import FileFormat, ContinuousVariable, Domain -from agilent_format import agilentImage, agilentImageIFG, agilentMosaic, agilentMosaicIFG, \ - agilentMosaicTiles -from orangecontrib.spectroscopy.io.util import SpectralFileFormat, _spectra_from_image, \ - TileFileFormat, ConstantBytesVisibleImage +from agilent_format import ( + agilentImage, + agilentImageIFG, + agilentMosaic, + agilentMosaicIFG, + agilentMosaicTiles, +) +from orangecontrib.spectroscopy.io.util import ( + SpectralFileFormat, + _spectra_from_image, + TileFileFormat, + ConstantBytesVisibleImage, +) from orangecontrib.spectroscopy.utils import MAP_X_VAR, MAP_Y_VAR @@ -18,21 +27,23 @@ def load_visible_images(vis_img_list: list[dict]) -> list[ConstantBytesVisibleIm try: with open(img['image_ref'], 'rb') as fh: image_bytes = io.BytesIO(fh.read()) - vimage = ConstantBytesVisibleImage(name=img["name"], - pos_x=img['pos_x'], - pos_y=img['pos_y'], - size_x=img['img_size_x'], - size_y=img['img_size_y'], - image_bytes=image_bytes, - ) + vimage = ConstantBytesVisibleImage( + name=img["name"], + pos_x=img['pos_x'], + pos_y=img['pos_y'], + size_x=img['img_size_x'], + size_y=img['img_size_y'], + image_bytes=image_bytes, + ) visible_images.append(vimage) except (KeyError, OSError) as e: - warnings.warn(f"Visible images load failed: {e}") + warnings.warn(f"Visible images load failed: {e}") # noqa: B028 return visible_images class AgilentImageReader(FileFormat, SpectralFileFormat): - """ Reader for Agilent FPA single tile image files""" + """Reader for Agilent FPA single tile image files""" + EXTENSIONS = ('.dat',) DESCRIPTION = 'Agilent Single Tile Image' @@ -44,7 +55,7 @@ def read_spectra(self): try: features = info['wavenumbers'] except KeyError: - #just start counting from 0 when nothing is known + # just start counting from 0 when nothing is known features = np.arange(X.shape[-1]) try: @@ -52,14 +63,15 @@ def read_spectra(self): except KeyError: # Use pixel units if FPA Pixel Size is not known px_size = 1 - x_locs = np.linspace(0, X.shape[1]*px_size, num=X.shape[1], endpoint=False) - y_locs = np.linspace(0, X.shape[0]*px_size, num=X.shape[0], endpoint=False) + x_locs = np.linspace(0, X.shape[1] * px_size, num=X.shape[1], endpoint=False) + y_locs = np.linspace(0, X.shape[0] * px_size, num=X.shape[0], endpoint=False) return _spectra_from_image(X, features, x_locs, y_locs) class AgilentImageIFGReader(FileFormat, SpectralFileFormat): - """ Reader for Agilent FPA single tile image files (IFG)""" + """Reader for Agilent FPA single tile image files (IFG)""" + EXTENSIONS = ('.seq',) DESCRIPTION = 'Agilent Single Tile Image (IFG)' @@ -75,13 +87,16 @@ def read_spectra(self): except KeyError: # Use pixel units if FPA Pixel Size is not known px_size = 1 - x_locs = np.linspace(0, X.shape[1]*px_size, num=X.shape[1], endpoint=False) - y_locs = np.linspace(0, X.shape[0]*px_size, num=X.shape[0], endpoint=False) + x_locs = np.linspace(0, X.shape[1] * px_size, num=X.shape[1], endpoint=False) + y_locs = np.linspace(0, X.shape[0] * px_size, num=X.shape[0], endpoint=False) - features, data, additional_table = _spectra_from_image(X, features, x_locs, y_locs) + features, data, additional_table = _spectra_from_image( + X, features, x_locs, y_locs + ) - import_params = ['Effective Laser Wavenumber', - 'Under Sampling Ratio', + import_params = [ + 'Effective Laser Wavenumber', + 'Under Sampling Ratio', ] new_attributes = [] new_columns = [] @@ -94,9 +109,11 @@ def read_spectra(self): new_attributes.append(ContinuousVariable.make(param_key)) new_columns.append(np.full((len(data),), param)) - domain = Domain(additional_table.domain.attributes, - additional_table.domain.class_vars, - additional_table.domain.metas + tuple(new_attributes)) + domain = Domain( + additional_table.domain.attributes, + additional_table.domain.class_vars, + additional_table.domain.metas + tuple(new_attributes), + ) table = additional_table.transform(domain) with table.unlocked(): table[:, new_attributes] = np.asarray(new_columns).T @@ -105,7 +122,8 @@ def read_spectra(self): class agilentMosaicReader(FileFormat, SpectralFileFormat): - """ Reader for Agilent FPA mosaic image files""" + """Reader for Agilent FPA mosaic image files""" + EXTENSIONS = ('.dmt',) DESCRIPTION = 'Agilent Mosaic Image' @@ -118,7 +136,7 @@ def read_spectra(self): try: features = info['wavenumbers'] except KeyError: - #just start counting from 0 when nothing is known + # just start counting from 0 when nothing is known features = np.arange(X.shape[-1]) try: @@ -126,10 +144,12 @@ def read_spectra(self): except KeyError: # Use pixel units if FPA Pixel Size is not known px_size = 1 - x_locs = np.linspace(0, X.shape[1]*px_size, num=X.shape[1], endpoint=False) - y_locs = np.linspace(0, X.shape[0]*px_size, num=X.shape[0], endpoint=False) + x_locs = np.linspace(0, X.shape[1] * px_size, num=X.shape[1], endpoint=False) + y_locs = np.linspace(0, X.shape[0] * px_size, num=X.shape[0], endpoint=False) - features, data, additional_table = _spectra_from_image(X, features, x_locs, y_locs) + features, data, additional_table = _spectra_from_image( + X, features, x_locs, y_locs + ) if visible_images: additional_table.attributes['visible_images'] = visible_images @@ -137,7 +157,8 @@ def read_spectra(self): class agilentMosaicIFGReader(FileFormat, SpectralFileFormat): - """ Reader for Agilent FPA mosaic image files""" + """Reader for Agilent FPA mosaic image files""" + EXTENSIONS = ('.dmt',) DESCRIPTION = 'Agilent Mosaic Image (IFG)' PRIORITY = agilentMosaicReader.PRIORITY + 1 @@ -154,13 +175,16 @@ def read_spectra(self): except KeyError: # Use pixel units if FPA Pixel Size is not known px_size = 1 - x_locs = np.linspace(0, X.shape[1]*px_size, num=X.shape[1], endpoint=False) - y_locs = np.linspace(0, X.shape[0]*px_size, num=X.shape[0], endpoint=False) + x_locs = np.linspace(0, X.shape[1] * px_size, num=X.shape[1], endpoint=False) + y_locs = np.linspace(0, X.shape[0] * px_size, num=X.shape[0], endpoint=False) - features, data, additional_table = _spectra_from_image(X, features, x_locs, y_locs) + features, data, additional_table = _spectra_from_image( + X, features, x_locs, y_locs + ) - import_params = ['Effective Laser Wavenumber', - 'Under Sampling Ratio', + import_params = [ + 'Effective Laser Wavenumber', + 'Under Sampling Ratio', ] new_attributes = [] new_columns = [] @@ -173,9 +197,11 @@ def read_spectra(self): new_attributes.append(ContinuousVariable.make(param_key)) new_columns.append(np.full((len(data),), param)) - domain = Domain(additional_table.domain.attributes, - additional_table.domain.class_vars, - additional_table.domain.metas + tuple(new_attributes)) + domain = Domain( + additional_table.domain.attributes, + additional_table.domain.class_vars, + additional_table.domain.metas + tuple(new_attributes), + ) table = additional_table.transform(domain) with table.unlocked(): table[:, new_attributes] = np.asarray(new_columns).T @@ -184,7 +210,8 @@ def read_spectra(self): class agilentMosaicTileReader(FileFormat, TileFileFormat): - """ Tile-by-tile reader for Agilent FPA mosaic image files""" + """Tile-by-tile reader for Agilent FPA mosaic image files""" + EXTENSIONS = ('.dmt',) DESCRIPTION = 'Agilent Mosaic Tile-by-tile' PRIORITY = agilentMosaicReader.PRIORITY + 100 @@ -202,7 +229,6 @@ def preprocess(self, table): else: return table - def read_tile(self): am = agilentMosaicTiles(self.filename) info = am.info @@ -213,10 +239,14 @@ def read_tile(self): features = info['wavenumbers'] attrs = [Orange.data.ContinuousVariable.make("%f" % f) for f in features] - domain = Orange.data.Domain(attrs, None, - metas=[Orange.data.ContinuousVariable.make(MAP_X_VAR), - Orange.data.ContinuousVariable.make(MAP_Y_VAR)] - ) + domain = Orange.data.Domain( + attrs, + None, + metas=[ + Orange.data.ContinuousVariable.make(MAP_X_VAR), + Orange.data.ContinuousVariable.make(MAP_Y_VAR), + ], + ) try: px_size = info['FPA Pixel Size'] * info['PixelAggregationSize'] @@ -233,10 +263,26 @@ def read_tile(self): # Return an empty Table if tile doesn't exist (instead of storing a tile of nans) yield Orange.data.Table.from_domain(domain) x_size, y_size = tile.shape[1], tile.shape[0] - x_locs = np.linspace(x*x_size*px_size, (x+1)*x_size*px_size, num=x_size, endpoint=False) - y_locs = np.linspace((ytiles-y-1)*y_size*px_size, (ytiles-y)*y_size*px_size, num=y_size, endpoint=False) - - _, data, additional_table = _spectra_from_image(tile, None, x_locs, y_locs) - data = np.asarray(data, dtype=np.float64) # Orange assumes X to be float64 - tile_table = Orange.data.Table.from_numpy(domain, X=data, metas=additional_table.metas) + x_locs = np.linspace( + x * x_size * px_size, + (x + 1) * x_size * px_size, + num=x_size, + endpoint=False, + ) + y_locs = np.linspace( + (ytiles - y - 1) * y_size * px_size, + (ytiles - y) * y_size * px_size, + num=y_size, + endpoint=False, + ) + + _, data, additional_table = _spectra_from_image( + tile, None, x_locs, y_locs + ) + data = np.asarray( + data, dtype=np.float64 + ) # Orange assumes X to be float64 + tile_table = Orange.data.Table.from_numpy( + domain, X=data, metas=additional_table.metas + ) yield tile_table diff --git a/orangecontrib/spectroscopy/io/ascii.py b/orangecontrib/spectroscopy/io/ascii.py index be269b16c..558a52ea3 100644 --- a/orangecontrib/spectroscopy/io/ascii.py +++ b/orangecontrib/spectroscopy/io/ascii.py @@ -9,8 +9,9 @@ class AsciiColReader(FileFormat, SpectralFileFormat): - """ Reader for files with multiple columns of numbers. The first column - contains the wavelengths, the others contain the spectra. """ + """Reader for files with multiple columns of numbers. The first column + contains the wavelengths, the others contain the spectra.""" + EXTENSIONS = ('.dat', '.dpt', '.xy', '.csv') DESCRIPTION = 'Spectra ASCII' @@ -24,14 +25,22 @@ def read_spectra(self): for d in delimiters: try: comments = [a for a in [";", "#"] if a != d] - tbl = np.loadtxt(self.filename, ndmin=2, delimiter=d, comments=comments, skiprows=skiprows) + tbl = np.loadtxt( + self.filename, + ndmin=2, + delimiter=d, + comments=comments, + skiprows=skiprows, + ) break except ValueError: pass if tbl is not None: break if tbl is None: - raise ValueError('File should be delimited by , ";", ":", or ",".') + raise ValueError( + 'File should be delimited by , ";", ":", or ",".' + ) wavenumbers = tbl.T[0] # first column is attribute name datavals = tbl.T[1:] return wavenumbers, datavals, None @@ -45,10 +54,11 @@ def write_file(filename, data): class AsciiMapReader(FileFormat): - """ Reader ascii map files. + """Reader ascii map files. First row contains wavelengths, then each row describes a spectrum, starting with (x, y) - coordinates: http://www.cytospec.com/file.php#FileASCII3 """ + coordinates: http://www.cytospec.com/file.php#FileASCII3""" + EXTENSIONS = ('.xyz',) DESCRIPTION = 'Hyperspectral map ASCII' @@ -59,10 +69,15 @@ def read(self): header = [a.strip() for a in header] assert header[0] == header[1] == "" dom_vals = [float(v) for v in header[2:]] - domain = Orange.data.Domain([ContinuousVariable.make("%f" % f) for f in dom_vals], None) + domain = Orange.data.Domain( + [ContinuousVariable.make("%f" % f) for f in dom_vals], None + ) tbl = np.loadtxt(f, ndmin=2) data = Table.from_numpy(domain, X=tbl[:, 2:]) - metas = [ContinuousVariable.make(MAP_X_VAR), ContinuousVariable.make(MAP_Y_VAR)] + metas = [ + ContinuousVariable.make(MAP_X_VAR), + ContinuousVariable.make(MAP_Y_VAR), + ] domain = Orange.data.Domain(domain.attributes, None, metas=metas) data = data.transform(domain) with data.unlocked(data.metas): @@ -73,11 +88,19 @@ def read(self): @staticmethod def write_file(filename, data): wavelengths = getx(data) - map_x = data.domain[MAP_X_VAR] if MAP_X_VAR in data.domain else ContinuousVariable(MAP_X_VAR) - map_y = data.domain[MAP_Y_VAR] if MAP_Y_VAR in data.domain else ContinuousVariable(MAP_Y_VAR) + map_x = ( + data.domain[MAP_X_VAR] + if MAP_X_VAR in data.domain + else ContinuousVariable(MAP_X_VAR) + ) + map_y = ( + data.domain[MAP_Y_VAR] + if MAP_Y_VAR in data.domain + else ContinuousVariable(MAP_Y_VAR) + ) ndom = Domain([map_x, map_y] + list(data.domain.attributes)) data = data.transform(ndom) with open(filename, "wb") as f: header = ["", ""] + [("%g" % w) for w in wavelengths] f.write(('\t'.join(header) + '\n').encode("ascii")) - np.savetxt(f, data.X, delimiter="\t", fmt="%g") \ No newline at end of file + np.savetxt(f, data.X, delimiter="\t", fmt="%g") diff --git a/orangecontrib/spectroscopy/io/cls.py b/orangecontrib/spectroscopy/io/cls.py index 229b6e095..4d109c7c8 100644 --- a/orangecontrib/spectroscopy/io/cls.py +++ b/orangecontrib/spectroscopy/io/cls.py @@ -9,8 +9,9 @@ class HDF5Reader_SGM(FileFormat, SpectralFileFormat): - """ A very case specific reader for interpolated hyperspectral mapping HDF5 + """A very case specific reader for interpolated hyperspectral mapping HDF5 files from the SGM beamline at the CLS""" + EXTENSIONS = ('.h5',) DESCRIPTION = 'HDF5 file @SGM/CLS' @@ -18,27 +19,65 @@ class HDF5Reader_SGM(FileFormat, SpectralFileFormat): def sheets(self) -> List: sheets = ["All"] with h5py.File(self.filename, 'r') as h5: - NXentries = [str(x) for x in h5['/'].keys() if 'NXentry' in str(h5[x].attrs.get('NX_class'))] - NXdata = [entry + "/" + str(x) for entry in NXentries for x in h5['/' + entry].keys() - if 'NXdata' in str(h5[entry + "/" + x].attrs.get('NX_class'))] + NXentries = [ + str(x) + for x in h5['/'].keys() + if 'NXentry' in str(h5[x].attrs.get('NX_class')) + ] + NXdata = [ + entry + "/" + str(x) + for entry in NXentries + for x in h5['/' + entry].keys() + if 'NXdata' in str(h5[entry + "/" + x].attrs.get('NX_class')) + ] for d in NXdata: - sheets.extend([k for k, v in h5[d].items() if len(v.shape) == 3 and v.shape[-1] > 1]) + sheets.extend( + [ + k + for k, v in h5[d].items() + if len(v.shape) == 3 and v.shape[-1] > 1 + ] + ) return sheets def read_spectra(self): if self.sheet is None: self.sheet = self.sheets[0] with h5py.File(self.filename, 'r') as h5: - NXentries = [str(x) for x in h5['/'].keys() if 'NXentry' in str(h5[x].attrs.get('NX_class'))] - NXdata = [entry + "/" + str(x) for entry in NXentries for x in h5['/' + entry].keys() - if 'NXdata' in str(h5[entry + "/" + x].attrs.get('NX_class'))] - axes = [[str(nm) for nm in h5[nxdata].keys() for s in h5[nxdata].attrs.get('axes') if str(s) in str(nm) or - str(nm) in str(s)] for nxdata in NXdata] - indep_shape = [v.shape for i, d in enumerate(NXdata) for k, v in h5[d].items() if k in axes[i][0]] + NXentries = [ + str(x) + for x in h5['/'].keys() + if 'NXentry' in str(h5[x].attrs.get('NX_class')) + ] + NXdata = [ + entry + "/" + str(x) + for entry in NXentries + for x in h5['/' + entry].keys() + if 'NXdata' in str(h5[entry + "/" + x].attrs.get('NX_class')) + ] + axes = [ + [ + str(nm) + for nm in h5[nxdata].keys() + for s in h5[nxdata].attrs.get('axes') + if str(s) in str(nm) or str(nm) in str(s) + ] + for nxdata in NXdata + ] + indep_shape = [ + v.shape + for i, d in enumerate(NXdata) + for k, v in h5[d].items() + if k in axes[i][0] + ] data = [ - {k: np.squeeze(v[()]) for k, v in h5[d].items() if v.shape[0] == indep_shape[i][0] and k not in axes[i]} - for i, d in - enumerate(NXdata)] + { + k: np.squeeze(v[()]) + for k, v in h5[d].items() + if v.shape[0] == indep_shape[i][0] and k not in axes[i] + } + for i, d in enumerate(NXdata) + ] features_entries = [] X_entries = [] @@ -47,7 +86,7 @@ def read_spectra(self): d = NXdata[i] if len(axes[i]) == 1: - warnings.warn(f"1D datasets not yet implemented: {d} not loaded.") + warnings.warn(f"1D datasets not yet implemented: {d} not loaded.") # noqa: B028 x_locs = h5[d][axes[i][0]] y_locs = h5[d][axes[i][1]] en = h5[d]['en'] @@ -58,8 +97,9 @@ def read_spectra(self): meta_data = {} for k, v in data[i].items(): dims = len(v.shape) - _, spectra, meta_table = _spectra_from_image(np.transpose(np.atleast_3d(v), (1, 0, 2)), - None, x_locs, y_locs) + _, spectra, meta_table = _spectra_from_image( + np.transpose(np.atleast_3d(v), (1, 0, 2)), None, x_locs, y_locs + ) if dims == len(axes[i]) + 1 and self.sheet in [k, "All"]: # sdd-type 3D data X_data[k] = spectra @@ -74,17 +114,25 @@ def read_spectra(self): features = np.array(emission) features_entries.append(features) - meta_table = meta_table.add_column(ContinuousVariable("en"), np.ones(X.shape[0]) * en, to_metas=True) + meta_table = meta_table.add_column( + ContinuousVariable("en"), np.ones(X.shape[0]) * en, to_metas=True + ) for k, v in meta_data.items(): - meta_table = meta_table.add_column(ContinuousVariable(k), v[:, 0], to_metas=True) + meta_table = meta_table.add_column( + ContinuousVariable(k), v[:, 0], to_metas=True + ) meta_table_entries.append(meta_table) - if not all(np.array_equal(f, features_entries[0]) for f in features_entries): - warnings.warn("Multiple NXdata entries with incompatible shape.") + if not all( + np.array_equal(f, features_entries[0]) for f in features_entries + ): + warnings.warn("Multiple NXdata entries with incompatible shape.") # noqa: B028 try: X = np.vstack(X_entries) - except: - warnings.warn("Multiple NXdata entries with incompatible shape.\nLoading first entry only.") + except: # noqa: E722 + warnings.warn( # noqa: B028 + "Multiple NXdata entries with incompatible shape.\nLoading first entry only." + ) return features_entries[0], X_entries[0], meta_table_entries[0] meta_table = meta_table.concatenate(meta_table_entries) diff --git a/orangecontrib/spectroscopy/io/diamond.py b/orangecontrib/spectroscopy/io/diamond.py index 077bc0bbc..79eef45f2 100644 --- a/orangecontrib/spectroscopy/io/diamond.py +++ b/orangecontrib/spectroscopy/io/diamond.py @@ -5,25 +5,31 @@ class NXS_STXM_Diamond_I08(FileFormat, SpectralFileFormat): - """ A case specific reader for hyperspectral imaging NXS HDF5 + """A case specific reader for hyperspectral imaging NXS HDF5 files from the I08 beamline of the Diamond Light Source. These files have very little metadata and don't fully adhere to - https://manual.nexusformat.org/classes/applications/NXstxm.html """ + https://manual.nexusformat.org/classes/applications/NXstxm.html""" + EXTENSIONS = ('.nxs',) DESCRIPTION = 'NXS HDF5 file @I08/Diamond Light Source' def read_spectra(self): import h5py + hdf5_file = h5py.File(self.filename, mode='r') - if 'entry1/definition' in hdf5_file and \ - hdf5_file['entry1/definition'][()].astype('str') == 'NXstxm': + if ( + 'entry1/definition' in hdf5_file + and hdf5_file['entry1/definition'][()].astype('str') == 'NXstxm' + ): grp = hdf5_file['entry1/Counter1'] x_locs = np.array(grp['sample_x']) y_locs = np.array(grp['sample_y']) energy = np.array(grp['photon_energy']) - order = [grp[n].attrs['axis'] - 1 for n in - ['sample_y', 'sample_x', 'photon_energy']] + order = [ + grp[n].attrs['axis'] - 1 + for n in ['sample_y', 'sample_x', 'photon_energy'] + ] intensities = np.array(grp['data']).transpose(order) return _spectra_from_image(intensities, energy, x_locs, y_locs) else: - raise IOError("Not an NXS HDF5 @I08/Diamond file") \ No newline at end of file + raise IOError("Not an NXS HDF5 @I08/Diamond file") diff --git a/orangecontrib/spectroscopy/io/envi.py b/orangecontrib/spectroscopy/io/envi.py index 179c667b2..255e96cc2 100644 --- a/orangecontrib/spectroscopy/io/envi.py +++ b/orangecontrib/spectroscopy/io/envi.py @@ -16,10 +16,10 @@ def read_spectra(self): lv = a.metadata["wavelength"] features = np.array(list(map(float, lv))) except KeyError: - #just start counting from 0 when nothing is known + # just start counting from 0 when nothing is known features = np.arange(X.shape[-1]) x_locs = np.arange(X.shape[1]) y_locs = np.arange(X.shape[0]) - return _spectra_from_image(X, features, x_locs, y_locs) \ No newline at end of file + return _spectra_from_image(X, features, x_locs, y_locs) diff --git a/orangecontrib/spectroscopy/io/gsf.py b/orangecontrib/spectroscopy/io/gsf.py index 3e4f7a212..e9d2e5c6e 100644 --- a/orangecontrib/spectroscopy/io/gsf.py +++ b/orangecontrib/spectroscopy/io/gsf.py @@ -5,14 +5,13 @@ def reader_gsf(file_path): - with open(file_path, "rb") as f: if not f.readline() == b'Gwyddion Simple Field 1.0\n': raise ValueError('Not a correct GSF file, wrong header.') meta = {} - term = False #there are mandatory fileds + term = False # there are mandatory fileds while term != b'\x00': l = f.readline().decode('utf-8') name, value = l.split("=") @@ -34,14 +33,16 @@ def reader_gsf(file_path): meta["XYUnits"] = meta.get("XYUnits", None) meta["ZUnits"] = meta.get("ZUnits", None) - X = np.fromfile(f, dtype='float32', count=XR*YR).reshape(XR, YR) + X = np.fromfile(f, dtype='float32', count=XR * YR).reshape(XR, YR) XRr = np.arange(XR) # TODO change this to the NeaSCAN orientation - YRr = np.arange(YR-1, -1, -1) # needed to have the same orientation as in Gwyddion + YRr = np.arange( + YR - 1, -1, -1 + ) # needed to have the same orientation as in Gwyddion - XRr = XOffset*1E6 + (XReal*1E6/XR) * XRr - YRr = YOffset*1E6 + (YReal*1E6/YR) * YRr + XRr = XOffset * 1e6 + (XReal * 1e6 / XR) * XRr + YRr = YOffset * 1e6 + (YReal * 1e6 / YR) * YRr X = X.reshape((meta["YRes"], meta["XRes"]) + (1,)) @@ -49,7 +50,6 @@ def reader_gsf(file_path): class GSFReader(FileFormat, SpectralFileFormat): - EXTENSIONS = (".gsf",) DESCRIPTION = 'Gwyddion Simple Field' diff --git a/orangecontrib/spectroscopy/io/matlab.py b/orangecontrib/spectroscopy/io/matlab.py index 492d1b3f9..20c283e6a 100644 --- a/orangecontrib/spectroscopy/io/matlab.py +++ b/orangecontrib/spectroscopy/io/matlab.py @@ -84,7 +84,7 @@ def is_number_array(array): if f.shape[1] == 1: names = [m] else: - names = [m + "_" + str(i+1) for i in range(f.shape[1])] + names = [m + "_" + str(i + 1) for i in range(f.shape[1])] for n in names: metas.append(ContinuousVariable.make(n)) meta_data.append(f) @@ -94,4 +94,4 @@ def is_number_array(array): domain = Domain(attributes, metas=metas) if X is None: X = np.zeros((meta_size, 0)) - return Orange.data.Table.from_numpy(domain, X, Y=None, metas=meta_data) \ No newline at end of file + return Orange.data.Table.from_numpy(domain, X, Y=None, metas=meta_data) diff --git a/orangecontrib/spectroscopy/io/maxiv.py b/orangecontrib/spectroscopy/io/maxiv.py index 5eb7a3849..60c76ee69 100644 --- a/orangecontrib/spectroscopy/io/maxiv.py +++ b/orangecontrib/spectroscopy/io/maxiv.py @@ -8,11 +8,12 @@ class HDRReader_STXM(FileFormat, SpectralFileFormat): - """ Reader for STXM/NEXAFS files from MAX IV and other synchrotrons. + """Reader for STXM/NEXAFS files from MAX IV and other synchrotrons. It is assumed that there are .xim files with plain text data in the same directory as the .hdr file. For foo.hdr these are called foo_a000.xim, foo_a001.xim etc. """ + EXTENSIONS = ('.hdr',) DESCRIPTION = 'STXM/NEXAFS .hdr+.xim files' @@ -49,7 +50,7 @@ def read_hdr_list(self): def read_hdr_dict(self, inner=True): """Read a dict {name = 'value'; foo = (...);} from self._lex; - inner=False for the outermost level. + inner=False for the outermost level. """ d = {} while True: @@ -94,14 +95,17 @@ def read_spectra(self): except AssertionError as e: raise IOError('Error parsing hdr file ' + self.filename) from e regions = hdrdata['ScanDefinition']['Regions'][0] - axes = [regions['QAxis'], regions['PAxis'], - hdrdata['ScanDefinition']['StackAxis']] + axes = [ + regions['QAxis'], + regions['PAxis'], + hdrdata['ScanDefinition']['StackAxis'], + ] dims = [len(ax['Points']) for ax in axes] spectra = np.empty(dims) for nf in range(dims[2]): ximname = '%s_a%03d.xim' % (self.filename[:-4], nf) xim = np.loadtxt(ximname) - spectra[...,nf] = xim + spectra[..., nf] = xim x_loc = axes[1]['Points'] y_loc = axes[0]['Points'] @@ -110,16 +114,19 @@ def read_spectra(self): class HDF5Reader_SoftiMAX(FileFormat, SpectralFileFormat): - """ A very case specific reader for HDF5 files from the SoftiMAX beamline in MAX-IV""" + """A very case specific reader for HDF5 files from the SoftiMAX beamline in MAX-IV""" + EXTENSIONS = ('.hdf5',) DESCRIPTION = 'HDF5 file @SoftiMAX/MAX-IV' PRIORITY = HDF5Reader_HERMES.PRIORITY + 1 def read_spectra(self): import h5py + with h5py.File(self.filename, 'r') as hdf5_file: - if 'entry1/collection/beamline' in hdf5_file and \ - hdf5_file['entry1/collection/beamline'][()].astype('str') == ['SLS Sophie at Softimax MAXIV']: + if 'entry1/collection/beamline' in hdf5_file and hdf5_file[ + 'entry1/collection/beamline' + ][()].astype('str') == ['SLS Sophie at Softimax MAXIV']: x_locs = np.array(hdf5_file['entry1/counter0/sample_x']) y_locs = np.array(hdf5_file['entry1/counter0/sample_y']) energy = np.array(hdf5_file['entry1/counter0/energy']) diff --git a/orangecontrib/spectroscopy/io/meta.py b/orangecontrib/spectroscopy/io/meta.py index 7424050d7..dd4b51fc3 100644 --- a/orangecontrib/spectroscopy/io/meta.py +++ b/orangecontrib/spectroscopy/io/meta.py @@ -12,8 +12,9 @@ class DatMetaReader(FileFormat): - """ Meta-reader to handle agilentImageReader and AsciiColReader name clash - over .dat extension. """ + """Meta-reader to handle agilentImageReader and AsciiColReader name clash + over .dat extension.""" + EXTENSIONS = ('.dat',) DESCRIPTION = 'Spectra ASCII or Agilent Single Tile Image' PRIORITY = min(AsciiColReader.PRIORITY, AgilentImageReader.PRIORITY) - 1 @@ -27,7 +28,8 @@ def read(self): class HDF5MetaReader(FileFormat): - """ Meta-reader to handle HDF5 (currently just .h5) extension(s)""" + """Meta-reader to handle HDF5 (currently just .h5) extension(s)""" + EXTENSIONS = ('.h5',) DESCRIPTION = "HDF5 files (ROCK/SGM)" PRIORITY = min(HDF5Reader_ROCK.PRIORITY, HDF5Reader_SGM.PRIORITY) - 1 @@ -38,7 +40,7 @@ def sheets(self) -> List: for reader in [HDF5Reader_SGM, HDF5Reader_ROCK]: try: sheets.extend(reader(self.filename).sheets) - except: + except: # noqa: E722 pass return sheets @@ -50,9 +52,11 @@ def read(self): except (ValueError, IndexError): return HDF5Reader_ROCK(filename=self.filename).read() + class HDRMetaReader(FileFormat): - """ Meta-reader to handle EnviMapReader and HDRReader_STXM name clash - over .hdr extension. """ + """Meta-reader to handle EnviMapReader and HDRReader_STXM name clash + over .hdr extension.""" + EXTENSIONS = ('.hdr',) DESCRIPTION = 'Envi hdr or STXM hdr+xim files' PRIORITY = min(EnviMapReader.PRIORITY, HDRReader_STXM.PRIORITY) - 1 @@ -61,4 +65,4 @@ def read(self): try: return EnviMapReader(filename=self.filename).read() except spectral.io.envi.FileNotAnEnviHeader: - return HDRReader_STXM(filename=self.filename).read() \ No newline at end of file + return HDRReader_STXM(filename=self.filename).read() diff --git a/orangecontrib/spectroscopy/io/neaspec.py b/orangecontrib/spectroscopy/io/neaspec.py index 4d8d61230..a6b94c0e3 100644 --- a/orangecontrib/spectroscopy/io/neaspec.py +++ b/orangecontrib/spectroscopy/io/neaspec.py @@ -276,7 +276,10 @@ def read_spectra(self): # register the calculated spacing in the metadata measparams["Calculated Datapoint Spacing (Δx)"] = ["[cm]", dx] measparams["Domain Units"] = "[µm]" - measparams["Channel Data Type"] = "Polar", "i.e. Amplitude and Phase separated" + measparams["Channel Data Type"] = ( + "Polar", + "i.e. Amplitude and Phase separated", + ) meta_data.attributes = measparams @@ -284,12 +287,10 @@ def read_spectra(self): class NeaReaderGSF(FileFormat, SpectralFileFormat): - EXTENSIONS = (".gsf",) DESCRIPTION = 'NeaSPEC legacy spectrum files' def read_spectra(self): - file_channel = str(self.filename.split(' ')[-2]).strip() folder_file = str(self.filename.split(file_channel)[-2]).strip() @@ -320,14 +321,19 @@ def read_spectra(self): data_gsf_a, data_gsf_p, info, channel_a, channel_p ) - metas = [Orange.data.ContinuousVariable.make("column"), - Orange.data.ContinuousVariable.make("row"), - Orange.data.ContinuousVariable.make("run"), - Orange.data.StringVariable.make("channel")] + metas = [ + Orange.data.ContinuousVariable.make("column"), + Orange.data.ContinuousVariable.make("row"), + Orange.data.ContinuousVariable.make("run"), + Orange.data.StringVariable.make("channel"), + ] domain = Orange.data.Domain([], None, metas=metas) - meta_data = Table.from_numpy(domain, X=np.zeros((len(final_data), 0)), - metas=np.asarray(final_metas, dtype=object)) + meta_data = Table.from_numpy( + domain, + X=np.zeros((len(final_data), 0)), + metas=np.asarray(final_metas, dtype=object), + ) meta_data.attributes = parameters @@ -336,7 +342,6 @@ def read_spectra(self): return depth, final_data, meta_data def _format_file(self, gsf_a, gsf_p, parameters, channel_a, channel_p): - info = {} for row in parameters: key = row[0].strip(':') @@ -345,7 +350,9 @@ def _format_file(self, gsf_a, gsf_p, parameters, channel_a, channel_p): value = value[0] info.update({key: value}) - info.update({'Reader': 'NeaReaderGSF'}) # key used in confirmation for complex fft calculation + info.update( + {'Reader': 'NeaReaderGSF'} + ) # key used in confirmation for complex fft calculation averaging = int(info['Averaging']) px_x = int(info['Pixel Area (X, Y, Z)'][1]) @@ -388,9 +395,7 @@ def _format_file(self, gsf_a, gsf_p, parameters, channel_a, channel_p): return np.asarray(data_complete), info, final_metas def _html_reader(self, path): - class HTMLTableParser(HTMLParser): - def __init__(self): super().__init__() self._current_row = [] diff --git a/orangecontrib/spectroscopy/io/omnic.py b/orangecontrib/spectroscopy/io/omnic.py index 6f90f4f4a..fe9889de6 100644 --- a/orangecontrib/spectroscopy/io/omnic.py +++ b/orangecontrib/spectroscopy/io/omnic.py @@ -10,7 +10,8 @@ class OmnicMapReader(FileFormat, SpectralFileFormat): - """ Reader for files with two columns of numbers (X and Y)""" + """Reader for files with two columns of numbers (X and Y)""" + EXTENSIONS = ('.map',) DESCRIPTION = 'Omnic map' @@ -24,16 +25,22 @@ def read_spectra(self): fv = info['OmnicInfo']['First X value'] features = np.linspace(fv, lv, num=X.shape[-1]) except KeyError: - #just start counting from 0 when nothing is known + # just start counting from 0 when nothing is known features = np.arange(X.shape[-1]) try: loc_first = info['OmnicInfo']["First map location"] loc_last = info['OmnicInfo']["Last map location"] - x_locs = np.linspace(min(loc_first[0], loc_last[0]), - max(loc_first[0], loc_last[0]), X.shape[1]) - y_locs = np.linspace(min(loc_first[1], loc_last[1]), - max(loc_first[1], loc_last[1]), X.shape[0]) + x_locs = np.linspace( + min(loc_first[0], loc_last[0]), + max(loc_first[0], loc_last[0]), + X.shape[1], + ) + y_locs = np.linspace( + min(loc_first[1], loc_last[1]), + max(loc_first[1], loc_last[1]), + X.shape[0], + ) except KeyError: x_locs = np.arange(X.shape[1]) y_locs = np.arange(X.shape[0]) @@ -42,7 +49,7 @@ def read_spectra(self): class SPAReader(FileFormat, SpectralFileFormat): - #based on code by Zack Gainsforth + # based on code by Zack Gainsforth EXTENSIONS = (".spa", ".SPA", ".srs") DESCRIPTION = 'SPA' @@ -62,8 +69,9 @@ def sections(self): for i in range(n): # Go to the section start. f.seek(304 + (22 if extended else 16) * i) - t, offset, length = struct.unpack(' 1 # ignore backgrounds and unchecked data if not hyperspectra: - if meas_attrs.keys().__contains__('IsBackground') and meas_attrs['IsBackground'][0]: + if ( + meas_attrs.keys().__contains__('IsBackground') + and meas_attrs['IsBackground'][0] + ): continue - if meas_attrs.keys().__contains__('Checked') and not meas_attrs['Checked'][0]: + if ( + meas_attrs.keys().__contains__('Checked') + and not meas_attrs['Checked'][0] + ): continue if len(wavenumbers) == 0: @@ -178,9 +197,11 @@ def read_spectra(self): if hyperspectra: x_len = meas_attrs['RangeXPoints'][0] y_len = meas_attrs['RangeYPoints'][0] - x_locs = pos_vals[:x_len,0] - y_indices = np.round(np.linspace(0, pos_vals.shape[0] - 1, y_len)).astype(int) - y_locs = pos_vals[y_indices,1] + x_locs = pos_vals[:x_len, 0] + y_indices = np.round( + np.linspace(0, pos_vals.shape[0] - 1, y_len) + ).astype(int) + y_locs = pos_vals[y_indices, 1] # adding focus positions z_locs = hdf5_meas['Dataset_Focus'][:] else: @@ -202,10 +223,10 @@ def read_spectra(self): rows = meas_attrs['RangeYPoints'][0] cols = meas_attrs['RangeXPoints'][0] # organized rows, columns, wavelengths - intensities = np.reshape(data, (rows,cols,data.shape[1])) + intensities = np.reshape(data, (rows, cols, data.shape[1])) break else: - intensities.append(data[0,:]) + intensities.append(data[0, :]) spectra = np.array(intensities) features = np.array(wavenumbers) @@ -214,16 +235,20 @@ def read_spectra(self): z_locs = np.array(z_locs).flatten() if hyperspectra: - features, spectra, additional_table = _spectra_from_image(spectra, features, x_locs, y_locs) + features, spectra, additional_table = _spectra_from_image( + spectra, features, x_locs, y_locs + ) new_attributes = [] new_columns = [] new_attributes.append(ContinuousVariable.make('z-focus')) new_columns.append(np.full((len(z_locs),), z_locs)) - domain = Domain(additional_table.domain.attributes, - additional_table.domain.class_vars, - additional_table.domain.metas + tuple(new_attributes)) + domain = Domain( + additional_table.domain.attributes, + additional_table.domain.class_vars, + additional_table.domain.metas + tuple(new_attributes), + ) data = additional_table.transform(domain) with data.unlocked(): data[:, new_attributes] = np.asarray(new_columns).T @@ -232,16 +257,23 @@ def read_spectra(self): # locations metas = np.vstack((x_locs, y_locs, z_locs)).T - domain = Orange.data.Domain([], None, - metas=[Orange.data.ContinuousVariable.make(MAP_X_VAR), - Orange.data.ContinuousVariable.make(MAP_Y_VAR), - Orange.data.ContinuousVariable.make("z-focus")] - ) - data = Orange.data.Table.from_numpy(domain, X=np.zeros((len(spectra), 0)), - metas=np.asarray(metas, dtype=object)) + domain = Orange.data.Domain( + [], + None, + metas=[ + Orange.data.ContinuousVariable.make(MAP_X_VAR), + Orange.data.ContinuousVariable.make(MAP_Y_VAR), + Orange.data.ContinuousVariable.make("z-focus"), + ], + ) + data = Orange.data.Table.from_numpy( + domain, + X=np.zeros((len(spectra), 0)), + metas=np.asarray(metas, dtype=object), + ) # Add vis and other images to data if visible_images: data.attributes['visible_images'] = visible_images - return features, spectra, data \ No newline at end of file + return features, spectra, data diff --git a/orangecontrib/spectroscopy/io/soleil.py b/orangecontrib/spectroscopy/io/soleil.py index 0d7805ff2..6c8062662 100644 --- a/orangecontrib/spectroscopy/io/soleil.py +++ b/orangecontrib/spectroscopy/io/soleil.py @@ -5,15 +5,15 @@ class SelectColumnReader(FileFormat, SpectralFileFormat): - """ Reader for files with multiple columns of numbers. The first column - contains the wavelengths, the others contain the spectra. """ + """Reader for files with multiple columns of numbers. The first column + contains the wavelengths, the others contain the spectra.""" + EXTENSIONS = ('.txt',) DESCRIPTION = 'XAS ascii spectrum from ROCK' PRIORITY = 9999 @property def sheets(self): - with open(self.filename, 'rt', encoding="utf8") as dataf: l = "" for l in dataf: @@ -24,28 +24,32 @@ def sheets(self): return list(map(str, col_nbrs)) def read_spectra(self): - if self.sheet: col_nb = int(self.sheet) else: col_nb = int(self.sheets[0]) - spectrum = np.loadtxt(self.filename, comments='#', - usecols=(0, col_nb - 1), - unpack=True) + spectrum = np.loadtxt( + self.filename, comments='#', usecols=(0, col_nb - 1), unpack=True + ) return spectrum[0], np.atleast_2d(spectrum[1]), None class HDF5Reader_HERMES(FileFormat, SpectralFileFormat): - """ A very case specific reader for HDF5 files from the HEREMES beamline in SOLEIL""" + """A very case specific reader for HDF5 files from the HEREMES beamline in SOLEIL""" + EXTENSIONS = ('.hdf5',) DESCRIPTION = 'HDF5 file @HERMRES/SOLEIL' def read_spectra(self): import h5py + with h5py.File(self.filename, 'r') as hdf5_file: - if 'entry1/collection/beamline' in hdf5_file and \ - hdf5_file['entry1/collection/beamline'][()].astype('str') == 'Hermes': + if ( + 'entry1/collection/beamline' in hdf5_file + and hdf5_file['entry1/collection/beamline'][()].astype('str') + == 'Hermes' + ): x_locs = np.array(hdf5_file['entry1/Counter0/sample_x']) y_locs = np.array(hdf5_file['entry1/Counter0/sample_y']) energy = np.array(hdf5_file['entry1/Counter0/energy']) @@ -56,8 +60,9 @@ def read_spectra(self): class HDF5Reader_ROCK(FileFormat, SpectralFileFormat): - """ A very case specific reader for hyperspectral imaging HDF5 + """A very case specific reader for hyperspectral imaging HDF5 files from the ROCK beamline in SOLEIL""" + EXTENSIONS = ('.h5',) DESCRIPTION = 'HDF5 file @ROCK(hyperspectral imaging)/SOLEIL' @@ -66,12 +71,11 @@ def sheets(self): import h5py as h5 with h5.File(self.filename, "r") as dataf: - cube_nbrs = range(1, len(dataf["data"].keys())+1) + cube_nbrs = range(1, len(dataf["data"].keys()) + 1) return list(map(str, cube_nbrs)) def read_spectra(self): - import h5py as h5 if self.sheet: @@ -95,4 +99,4 @@ def read_spectra(self): x_locs = np.arange(width) y_locs = np.arange(height) - return _spectra_from_image(intensities, energies, x_locs, y_locs) \ No newline at end of file + return _spectra_from_image(intensities, energies, x_locs, y_locs) diff --git a/orangecontrib/spectroscopy/io/util.py b/orangecontrib/spectroscopy/io/util.py index c38402f71..f4ff49a92 100644 --- a/orangecontrib/spectroscopy/io/util.py +++ b/orangecontrib/spectroscopy/io/util.py @@ -7,9 +7,8 @@ class SpectralFileFormat: - def read_spectra(self): - """ Fast reading of spectra. Return spectral information + """Fast reading of spectra. Return spectral information in two arrays (wavelengths and values). Only additional attributes (usually metas) are returned as a Table. @@ -25,17 +24,19 @@ def read(self): def _metatable_maplocs(x_locs, y_locs): - """ Create an Orange table containing (x,y) map locations as metas. """ + """Create an Orange table containing (x,y) map locations as metas.""" x_locs = np.asarray(x_locs) y_locs = np.asarray(y_locs) metas = np.vstack((x_locs, y_locs)).T - domain = Domain([], None, - metas=[ContinuousVariable.make(MAP_X_VAR), - ContinuousVariable.make(MAP_Y_VAR)] - ) - data = Table.from_numpy(domain, X=np.zeros((len(metas), 0)), - metas=np.asarray(metas, dtype=object)) + domain = Domain( + [], + None, + metas=[ContinuousVariable.make(MAP_X_VAR), ContinuousVariable.make(MAP_Y_VAR)], + ) + data = Table.from_numpy( + domain, X=np.zeros((len(metas), 0)), metas=np.asarray(metas, dtype=object) + ) return data @@ -49,7 +50,7 @@ def _spectra_from_image(X, features, x_locs, y_locs): y_locs = np.asarray(y_locs) # each spectrum has its own row - spectra = X.reshape((X.shape[0]*X.shape[1], X.shape[2])) + spectra = X.reshape((X.shape[0] * X.shape[1], X.shape[2])) # locations y_loc = np.repeat(np.arange(X.shape[0]), X.shape[1]) @@ -72,9 +73,9 @@ def _spectra_from_image_2d(X, wn, x_locs, y_locs): def build_spec_table(domvals, data, additional_table=None): """Create a an Orange data table from a triplet: - - 1D numpy array defining wavelengths (size m) - - 2D numpy array (shape (n, m)) with values - - Orange.data.Table with only meta or class attributes (size n) + - 1D numpy array defining wavelengths (size m) + - 2D numpy array (shape (n, m)) with values + - Orange.data.Table with only meta or class attributes (size n) """ data = np.atleast_2d(data) features = [ContinuousVariable.make("%f" % f) for f in domvals] @@ -82,19 +83,24 @@ def build_spec_table(domvals, data, additional_table=None): domain = Domain(features, None) return Table.from_numpy(domain, X=data) else: - domain = Domain(features, - class_vars=additional_table.domain.class_vars, - metas=additional_table.domain.metas) - ret_data = Table.from_numpy(domain, X=data, Y=additional_table.Y, - metas=additional_table.metas, - attributes=additional_table.attributes) + domain = Domain( + features, + class_vars=additional_table.domain.class_vars, + metas=additional_table.domain.metas, + ) + ret_data = Table.from_numpy( + domain, + X=data, + Y=additional_table.Y, + metas=additional_table.metas, + attributes=additional_table.attributes, + ) return ret_data class TileFileFormat: - def read_tile(self): - """ Read file in chunks (tiles) to allow preprocessing before combining + """Read file in chunks (tiles) to allow preprocessing before combining into one large Table. Return a generator of Tables, where each Table is a chunk of the total. @@ -116,7 +122,6 @@ def read(self): class VisibleImage: - def __init__(self, name, pos_x, pos_y, size_x, size_y): self.name = name self.pos_x = pos_x @@ -142,6 +147,7 @@ def __init__(self, name, pos_x, pos_y, size_x, size_y, image_bytes): @property def image(self): from PIL import Image + return Image.open(self._image_bytes) def __deepcopy__(self, memo): diff --git a/orangecontrib/spectroscopy/io/wire.py b/orangecontrib/spectroscopy/io/wire.py index c52551ff9..beb8b98c7 100644 --- a/orangecontrib/spectroscopy/io/wire.py +++ b/orangecontrib/spectroscopy/io/wire.py @@ -2,7 +2,10 @@ import numpy as np from Orange.data import FileFormat -from orangecontrib.spectroscopy.io.util import SpectralFileFormat, _spectra_from_image_2d +from orangecontrib.spectroscopy.io.util import ( + SpectralFileFormat, + _spectra_from_image_2d, +) class WiREReaders(FileFormat, SpectralFileFormat): @@ -12,14 +15,15 @@ class WiREReaders(FileFormat, SpectralFileFormat): def read_spectra(self): # renishawWiRE is imported here so that its API changes would not block spectroscopy from renishawWiRE import WDFReader # pylint: disable=import-outside-toplevel + wdf_file = WDFReader(self.filename) try: - if wdf_file.measurement_type == 1: # single point spectra + if wdf_file.measurement_type == 1: # single point spectra table = self.single_reader(wdf_file) - elif wdf_file.measurement_type == 2: # series scan + elif wdf_file.measurement_type == 2: # series scan table = self.series_reader(wdf_file) - elif wdf_file.measurement_type == 3: # line scan + elif wdf_file.measurement_type == 3: # line scan table = self.map_reader(wdf_file) finally: wdf_file.close() @@ -27,22 +31,24 @@ def read_spectra(self): return table def single_reader(self, wdf_file): - domvals = wdf_file.xdata # energies - y_data = wdf_file.spectra # spectra + domvals = wdf_file.xdata # energies + y_data = wdf_file.spectra # spectra return domvals, y_data, None def series_reader(self, wdf_file): - domvals = wdf_file.xdata # energies - y_data = wdf_file.spectra # spectra - z_locs = wdf_file.zpos # depth info + domvals = wdf_file.xdata # energies + y_data = wdf_file.spectra # spectra + z_locs = wdf_file.zpos # depth info z_locs = z_locs.reshape(-1, 1) - domain = Orange.data.Domain([], None, - metas=[Orange.data.ContinuousVariable.make("map_z")]) + domain = Orange.data.Domain( + [], None, metas=[Orange.data.ContinuousVariable.make("map_z")] + ) - data = Orange.data.Table.from_numpy(domain, X=np.zeros((len(y_data), 0)), - metas=np.asarray(z_locs, dtype=object)) + data = Orange.data.Table.from_numpy( + domain, X=np.zeros((len(y_data), 0)), metas=np.asarray(z_locs, dtype=object) + ) return domvals, y_data, data def map_reader(self, wdf_file): @@ -56,4 +62,4 @@ def map_reader(self, wdf_file): raise NotImplementedError x_locs = wdf_file.xpos y_locs = wdf_file.ypos - return _spectra_from_image_2d(spectra, domvals, x_locs, y_locs) \ No newline at end of file + return _spectra_from_image_2d(spectra, domvals, x_locs, y_locs) diff --git a/orangecontrib/spectroscopy/irfft.py b/orangecontrib/spectroscopy/irfft.py index 8d364bf0b..6a14b2e27 100644 --- a/orangecontrib/spectroscopy/irfft.py +++ b/orangecontrib/spectroscopy/irfft.py @@ -7,6 +7,7 @@ class ApodFunc(IntEnum): """ Implemented apodization functions in apodize """ + BOXCAR = 0 BLACKMAN_HARRIS_3 = 1 BLACKMAN_HARRIS_4 = 2 @@ -17,6 +18,7 @@ class PhaseCorrection(IntEnum): """ Implemented phase correction methods """ + MERTZ = 0 MERTZSIGNED = 1 STORED = 2 @@ -27,6 +29,7 @@ class PeakSearch(IntEnum): """ Implemented peak search functions """ + MAXIMUM = 0 MINIMUM = 1 ABSOLUTE = 2 @@ -55,6 +58,7 @@ def find_zpd(ifg, peak_search): else: raise NotImplementedError + def apodize(ifg, zpd, apod_func): """ Perform apodization of asymmetric interferogram using selected apodization @@ -93,14 +97,18 @@ def apodize(ifg, zpd, apod_func): A3 = 0.0 n_n = np.arange(wing_n) n_p = np.arange(wing_p) - Bs_n = A0\ - + A1 * np.cos(np.pi*n_n/wing_n)\ - + A2 * np.cos(np.pi*2*n_n/wing_n)\ - + A3 * np.cos(np.pi*3*n_n/wing_n) - Bs_p = A0\ - + A1 * np.cos(np.pi*n_p/wing_p)\ - + A2 * np.cos(np.pi*2*n_p/wing_p)\ - + A3 * np.cos(np.pi*3*n_p/wing_p) + Bs_n = ( + A0 + + A1 * np.cos(np.pi * n_n / wing_n) + + A2 * np.cos(np.pi * 2 * n_n / wing_n) + + A3 * np.cos(np.pi * 3 * n_n / wing_n) + ) + Bs_p = ( + A0 + + A1 * np.cos(np.pi * n_p / wing_p) + + A2 * np.cos(np.pi * 2 * n_p / wing_p) + + A3 * np.cos(np.pi * 3 * n_p / wing_p) + ) Bs = np.hstack((Bs_n[::-1], Bs_p)) elif apod_func == ApodFunc.BLACKMAN_HARRIS_4: @@ -113,14 +121,18 @@ def apodize(ifg, zpd, apod_func): A3 = 0.01168 n_n = np.arange(wing_n) n_p = np.arange(wing_p) - Bs_n = A0\ - + A1 * np.cos(np.pi*n_n/wing_n)\ - + A2 * np.cos(np.pi*2*n_n/wing_n)\ - + A3 * np.cos(np.pi*3*n_n/wing_n) - Bs_p = A0\ - + A1 * np.cos(np.pi*n_p/wing_p)\ - + A2 * np.cos(np.pi*2*n_p/wing_p)\ - + A3 * np.cos(np.pi*3*n_p/wing_p) + Bs_n = ( + A0 + + A1 * np.cos(np.pi * n_n / wing_n) + + A2 * np.cos(np.pi * 2 * n_n / wing_n) + + A3 * np.cos(np.pi * 3 * n_n / wing_n) + ) + Bs_p = ( + A0 + + A1 * np.cos(np.pi * n_p / wing_p) + + A2 * np.cos(np.pi * 2 * n_p / wing_p) + + A3 * np.cos(np.pi * 3 * n_p / wing_p) + ) Bs = np.hstack((Bs_n[::-1], Bs_p)) elif apod_func == ApodFunc.BLACKMAN_NUTTALL: @@ -131,10 +143,12 @@ def apodize(ifg, zpd, apod_func): # Create Blackman Nuttall Window according to the formula given by Wolfram. xs = np.arange(ifg_N) Bs = np.zeros(ifg_N) - Bs = 0.3635819\ - - 0.4891775 * np.cos(2*np.pi*xs/(2*delta - 1))\ - + 0.1365995 * np.cos(4*np.pi*xs/(2*delta - 1))\ - - 0.0106411 * np.cos(6*np.pi*xs/(2*delta - 1)) + Bs = ( + 0.3635819 + - 0.4891775 * np.cos(2 * np.pi * xs / (2 * delta - 1)) + + 0.1365995 * np.cos(4 * np.pi * xs / (2 * delta - 1)) + - 0.0106411 * np.cos(6 * np.pi * xs / (2 * delta - 1)) + ) # Apodize the sampled Interferogram try: @@ -144,16 +158,19 @@ def apodize(ifg, zpd, apod_func): return ifg_apod + def _zero_fill_size(ifg_N, zff): # Calculate desired array size Nzff = ifg_N * zff # Calculate final size to next power of two for DFT efficiency return int(np.exp2(np.ceil(np.log2(Nzff)))) + def _zero_fill_pad(ifg, zerofill): zeroshape = (ifg.shape[0], zerofill) if ifg.ndim == 2 else zerofill return np.hstack((ifg, np.zeros(zeroshape, dtype=ifg.dtype))) + def zero_fill(ifg, zff): """ Zero-fill interferogram to DFT-efficient power of two. @@ -172,24 +189,29 @@ def zero_fill(ifg, zff): # Pad array return _zero_fill_pad(ifg, zero_fill) -class IRFFT(): + +class IRFFT: """ Calculate FFT of a single interferogram sweep. Based on mertz module by Eric Peach, 2014 """ + # Calculated attributes zpd = None wavenumbers = None spectrum = None phase = None - - def __init__(self, dx, - apod_func=ApodFunc.BLACKMAN_HARRIS_3, zff=2, - phase_res=None, phase_corr=PhaseCorrection.MERTZ, - peak_search=PeakSearch.MAXIMUM, - ): + def __init__( + self, + dx, + apod_func=ApodFunc.BLACKMAN_HARRIS_3, + zff=2, + phase_res=None, + phase_corr=PhaseCorrection.MERTZ, + peak_search=PeakSearch.MAXIMUM, + ): self.dx = dx self.apod_func = apod_func self.zff = zff @@ -213,11 +235,11 @@ def __call__(self, ifg, zpd=None, phase=None): # Calculate phase on interferogram of specified size 2*L L = self.phase_ifg_size(ifg.shape[0]) - if L == 0: # Use full ifg for phase + if L == 0: # Use full ifg for phase ifg = apodize(ifg, self.zpd, self.apod_func) ifg = zero_fill(ifg, self.zff) # Rotate the Complete IFG so that the centerburst is at edges. - ifg = np.hstack((ifg[self.zpd:], ifg[0:self.zpd])) + ifg = np.hstack((ifg[self.zpd :], ifg[0 : self.zpd])) Nzff = ifg.shape[0] # Take FFT of Rotated Complete Graph ifg = np.fft.rfft(ifg) @@ -228,7 +250,7 @@ def __call__(self, ifg, zpd=None, phase=None): Ixs = ifg[self.zpd - L : self.zpd + L].copy() ifg = apodize(ifg, self.zpd, self.apod_func) ifg = zero_fill(ifg, self.zff) - ifg = np.hstack((ifg[self.zpd:], ifg[0:self.zpd])) + ifg = np.hstack((ifg[self.zpd :], ifg[0 : self.zpd])) Nzff = ifg.shape[0] Ixs = apodize(Ixs, L, self.apod_func) @@ -247,7 +269,9 @@ def __call__(self, ifg, zpd=None, phase=None): self.phase = ifg.imag else: try: - self.spectrum = np.cos(self.phase) * ifg.real + np.sin(self.phase) * ifg.imag + self.spectrum = ( + np.cos(self.phase) * ifg.real + np.sin(self.phase) * ifg.imag + ) except ValueError as e: raise ValueError("Incompatible phase: {}".format(e)) @@ -276,13 +300,12 @@ def compute_phase(self, ifg_sub_fft): elif self.phase_corr == PhaseCorrection.MERTZ: self.phase = np.arctan2(ifg_sub_fft.imag, ifg_sub_fft.real) elif self.phase_corr == PhaseCorrection.MERTZSIGNED: - self.phase = np.arctan(ifg_sub_fft.imag/ifg_sub_fft.real) + self.phase = np.arctan(ifg_sub_fft.imag / ifg_sub_fft.real) else: raise ValueError("Invalid PhaseCorrection: {}".format(self.phase_corr)) class MultiIRFFT(IRFFT): - def __call__(self, ifg, zpd=None, phase=None): if ifg.ndim != 2: raise ValueError("ifg must be 2D array of row-wise interferograms") @@ -291,18 +314,20 @@ def __call__(self, ifg, zpd=None, phase=None): try: self.zpd = int(zpd) except TypeError: - raise TypeError("zpd must be specified as a single value valid for all interferograms") + raise TypeError( + "zpd must be specified as a single value valid for all interferograms" + ) # Subtract DC value from interferogram ifg = ifg - ifg.mean(axis=1, keepdims=True) # Calculate phase on interferogram of specified size 2*L L = self.phase_ifg_size(ifg.shape[1]) - if L == 0: # Use full ifg for phase #TODO multi is this code tested + if L == 0: # Use full ifg for phase #TODO multi is this code tested ifg = apodize(ifg, self.zpd, self.apod_func) ifg = zero_fill(ifg, self.zff) # Rotate the Complete IFG so that the centerburst is at edges. - ifg = np.hstack((ifg[self.zpd:], ifg[0:self.zpd])) + ifg = np.hstack((ifg[self.zpd :], ifg[0 : self.zpd])) Nzff = ifg.shape[0] # Take FFT of Rotated Complete Graph ifg = np.fft.rfft(ifg) @@ -313,7 +338,7 @@ def __call__(self, ifg, zpd=None, phase=None): Ixs = ifg[:, self.zpd - L : self.zpd + L].copy() ifg = apodize(ifg, self.zpd, self.apod_func) ifg = zero_fill(ifg, self.zff) - ifg = np.hstack((ifg[:, self.zpd:], ifg[:, 0:self.zpd])) + ifg = np.hstack((ifg[:, self.zpd :], ifg[:, 0 : self.zpd])) Nzff = ifg.shape[1] Ixs = apodize(Ixs, L, self.apod_func) @@ -332,7 +357,9 @@ def __call__(self, ifg, zpd=None, phase=None): self.phase = ifg.imag else: try: - self.spectrum = np.cos(self.phase) * ifg.real + np.sin(self.phase) * ifg.imag + self.spectrum = ( + np.cos(self.phase) * ifg.real + np.sin(self.phase) * ifg.imag + ) except ValueError as e: raise ValueError("Incompatible phase: {}".format(e)) @@ -340,7 +367,6 @@ def __call__(self, ifg, zpd=None, phase=None): class ComplexFFT(IRFFT): - def __call__(self, ifg, zpd=None, phase=None): ifg -= np.mean(ifg) @@ -352,14 +378,14 @@ def __call__(self, ifg, zpd=None, phase=None): ifg = apodize(ifg, self.zpd, self.apod_func) ifg = zero_fill(ifg, self.zff) # Rotate the Complete IFG so that the centerburst is at edges. - ifg = np.hstack((ifg[self.zpd:], ifg[0:self.zpd])) + ifg = np.hstack((ifg[self.zpd :], ifg[0 : self.zpd])) Nzff = ifg.shape[0] ifg = np.fft.fft(ifg) - + magnitude = np.abs(ifg) angle = np.angle(ifg) self.wavenumbers = np.fft.rfftfreq(Nzff, self.dx) - self.spectrum = magnitude[:len(self.wavenumbers)] - self.phase = angle[:len(self.wavenumbers)] + self.spectrum = magnitude[: len(self.wavenumbers)] + self.phase = angle[: len(self.wavenumbers)] return self.spectrum, self.phase, self.wavenumbers diff --git a/orangecontrib/spectroscopy/models/pls.py b/orangecontrib/spectroscopy/models/pls.py index 7e440b63c..ea0c90a8a 100644 --- a/orangecontrib/spectroscopy/models/pls.py +++ b/orangecontrib/spectroscopy/models/pls.py @@ -1,8 +1,7 @@ import numpy as np import sklearn.cross_decomposition as skl_pls -from Orange.data import Table, Domain, Variable, \ - ContinuousVariable, StringVariable +from Orange.data import Table, Domain, Variable, ContinuousVariable, StringVariable from Orange.data.util import get_unique_names, SharedComputeValue from Orange.preprocess.score import LearnerScorer from Orange.regression import SklLearner, SklModel @@ -18,7 +17,6 @@ def score(self, data): class _PLSCommonTransform: - def __init__(self, pls_model): self.pls_model = pls_model @@ -92,8 +90,8 @@ def trvar(i, name): domain = Domain( [trvar(i, var_names_X[i]) for i in range(n_components)], data.domain.class_vars, - list(data.domain.metas) + - [trvar(n_components + i, var_names_Y[i]) for i in range(n_components)] + list(data.domain.metas) + + [trvar(n_components + i, var_names_Y[i]) for i in range(n_components)], ) return data.transform(domain) @@ -112,11 +110,14 @@ def components(self): dom = Domain( [ContinuousVariable(a.name) for a in orig_domain.attributes], [ContinuousVariable(a.name) for a in orig_domain.class_vars], - metas=meta_vars) - components = Table(dom, - self.skl_model.x_loadings_.T, - Y=self.skl_model.y_loadings_.T, - metas=metas) + metas=meta_vars, + ) + components = Table( + dom, + self.skl_model.x_loadings_.T, + Y=self.skl_model.y_loadings_.T, + metas=metas, + ) components.name = 'components' return components @@ -124,7 +125,7 @@ def coefficients_table(self): coeffs = self.coefficients.T domain = Domain( [ContinuousVariable(f"coef {i}") for i in range(coeffs.shape[1])], - metas=[StringVariable("name")] + metas=[StringVariable("name")], ) waves = [[attr.name] for attr in self.domain.attributes] coef_table = Table.from_numpy(domain, X=coeffs, metas=waves) @@ -140,14 +141,13 @@ class PLSRegressionLearner(SklLearner, _FeatureScorerMixin): def fit(self, X, Y, W=None): params = self.params.copy() - params["n_components"] = min(X.shape[1] - 1, - X.shape[0] - 1, - params["n_components"]) + params["n_components"] = min( + X.shape[1] - 1, X.shape[0] - 1, params["n_components"] + ) clf = self.__wraps__(**params) return self.__returns__(clf.fit(X, Y)) - def __init__(self, n_components=2, scale=True, - max_iter=500, preprocessors=None): + def __init__(self, n_components=2, scale=True, max_iter=500, preprocessors=None): super().__init__(preprocessors=preprocessors) self.params = vars() @@ -161,11 +161,12 @@ def incompatibility_reason(self, domain): reason = "Only numeric target variables expected." return reason + if __name__ == '__main__': import Orange data = Orange.data.Table('housing') learners = [PLSRegressionLearner(n_components=2, max_iter=100)] res = Orange.evaluation.CrossValidation()(data, learners) - for l, ca in zip(learners, Orange.evaluation.RMSE(res)): + for l, ca in zip(learners, Orange.evaluation.RMSE(res), strict=True): print("learner: {}\nRMSE: {}\n".format(l, ca)) diff --git a/orangecontrib/spectroscopy/preprocess/__init__.py b/orangecontrib/spectroscopy/preprocess/__init__.py index 9de1ded31..e53f5a1cf 100644 --- a/orangecontrib/spectroscopy/preprocess/__init__.py +++ b/orangecontrib/spectroscopy/preprocess/__init__.py @@ -21,18 +21,32 @@ from orangecontrib.spectroscopy.preprocess.integrate import Integrate from orangecontrib.spectroscopy.preprocess.emsc import EMSC -from orangecontrib.spectroscopy.preprocess.transform import Absorbance, Transmittance, \ - CommonDomainRef -from orangecontrib.spectroscopy.preprocess.utils import SelectColumn, CommonDomain, \ - CommonDomainOrder, CommonDomainOrderUnknowns, nan_extend_edges_and_interpolate, \ - remove_whole_nan_ys, interp1d_with_unknowns_numpy, interp1d_with_unknowns_scipy, \ - interp1d_wo_unknowns_scipy, edge_baseline, MissingReferenceException, \ - WrongReferenceException, replace_infs, transform_to_sorted_features, PreprocessException, \ - linear_baseline +from orangecontrib.spectroscopy.preprocess.transform import ( + Absorbance, + Transmittance, + CommonDomainRef, +) +from orangecontrib.spectroscopy.preprocess.utils import ( + SelectColumn, + CommonDomain, + CommonDomainOrder, + CommonDomainOrderUnknowns, + nan_extend_edges_and_interpolate, + remove_whole_nan_ys, + interp1d_with_unknowns_numpy, + interp1d_with_unknowns_scipy, + interp1d_wo_unknowns_scipy, + edge_baseline, + MissingReferenceException, + WrongReferenceException, + replace_infs, + transform_to_sorted_features, + PreprocessException, + linear_baseline, +) class MNFDenoising(Preprocess): - def __init__(self, components=None): self.components = components @@ -41,15 +55,18 @@ def __call__(self, data): # maxsvd = min(len(data.domain.attributes), len(data)) commonfn = _MNFCommon(data.domain, self.components) - nats = [at.copy(compute_value=MNFDenoisingFeature(i, commonfn)) - for i, at in enumerate(data.domain.attributes)] + nats = [ + at.copy(compute_value=MNFDenoisingFeature(i, commonfn)) + for i, at in enumerate(data.domain.attributes) + ] else: # FIXME we should have a warning here - nats = [at.copy(compute_value=lambda d: np.full((len(d), 1), np.nan)) - for at in data.domain.attributes] + nats = [ + at.copy(compute_value=lambda d: np.full((len(d), 1), np.nan)) + for at in data.domain.attributes + ] - domain = Orange.data.Domain(nats, data.domain.class_vars, - data.domain.metas) + domain = Orange.data.Domain(nats, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -59,7 +76,6 @@ class MNFDenoisingFeature(SelectColumn): class _MNFCommon(CommonDomainOrderUnknowns): - def __init__(self, domain, components): super().__init__(domain) self.domain = domain @@ -102,7 +118,7 @@ def transformed(self, X, _): # choice of top components R = np.eye(m[1], m[1]) - R[self.components:, self.components:] = 0 + R[self.components :, self.components :] = 0 # inverse MNF transformation D = np.dot(np.dot(M, R), np.linalg.inv(P)) @@ -128,7 +144,7 @@ def transformed(self, data): # set unused components to zero remove = np.ones(pca_space.shape[1]) if isinstance(self.components, int): - remove[:self.components] = 0 + remove[: self.components] = 0 else: remove[self.components] = 0 remove = np.extract(remove, np.arange(pca_space.shape[1])) @@ -137,9 +153,12 @@ def transformed(self, data): class PCADenoising(Preprocess): - - def __init__(self, components: Union[None, int, Sequence[int]]=None, - random_state=0, svd_solver="randomized"): + def __init__( + self, + components: Union[None, int, Sequence[int]] = None, + random_state=0, + svd_solver="randomized", + ): self.components = components self.random_state = random_state self.svd_solver = svd_solver @@ -147,19 +166,24 @@ def __init__(self, components: Union[None, int, Sequence[int]]=None, def __call__(self, data): if data and len(data.domain.attributes): maxpca = min(len(data.domain.attributes), len(data), MAX_COMPONENTS) - pca = Orange.projection.PCA(n_components=maxpca, - random_state=self.random_state, - svd_solver=self.svd_solver)(data) + pca = Orange.projection.PCA( + n_components=maxpca, + random_state=self.random_state, + svd_solver=self.svd_solver, + )(data) commonfn = _PCAReconstructCommon(pca, self.components) - nats = [at.copy(compute_value=PCADenoisingFeature(i, commonfn)) - for i, at in enumerate(data.domain.attributes)] + nats = [ + at.copy(compute_value=PCADenoisingFeature(i, commonfn)) + for i, at in enumerate(data.domain.attributes) + ] else: # FIXME we should have a warning here - nats = [at.copy(compute_value=lambda d: np.full((len(d), 1), np.nan)) - for at in data.domain.attributes] + nats = [ + at.copy(compute_value=lambda d: np.full((len(d), 1), np.nan)) + for at in data.domain.attributes + ] - domain = Orange.data.Domain(nats, data.domain.class_vars, - data.domain.metas) + domain = Orange.data.Domain(nats, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -169,7 +193,6 @@ class GaussianFeature(SelectColumn): class _GaussianCommon(CommonDomainOrderUnknowns): - def __init__(self, sd, domain): super().__init__(domain) self.sd = sd @@ -182,21 +205,20 @@ def transformed(self, X, wavenumbers): class GaussianSmoothing(Preprocess): - - def __init__(self, sd=10.): + def __init__(self, sd=10.0): self.sd = sd def __call__(self, data): common = _GaussianCommon(self.sd, data.domain) - atts = [a.copy(compute_value=GaussianFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + atts = [ + a.copy(compute_value=GaussianFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) class Cut(Preprocess): - def __init__(self, lowlim=None, highlim=None, inverse=False): self.lowlim = lowlim self.highlim = highlim @@ -205,14 +227,22 @@ def __init__(self, lowlim=None, highlim=None, inverse=False): def __call__(self, data): x = getx(data) if not self.inverse: - okattrs = [at for at, v in zip(data.domain.attributes, x) - if (self.lowlim is None or self.lowlim <= v) and - (self.highlim is None or v <= self.highlim)] + okattrs = [ + at + for at, v in zip(data.domain.attributes, x, strict=True) + if (self.lowlim is None or self.lowlim <= v) + and (self.highlim is None or v <= self.highlim) + ] else: - okattrs = [at for at, v in zip(data.domain.attributes, x) - if (self.lowlim is not None and v <= self.lowlim) or - (self.highlim is not None and self.highlim <= v)] - domain = Orange.data.Domain(okattrs, data.domain.class_vars, metas=data.domain.metas) + okattrs = [ + at + for at, v in zip(data.domain.attributes, x, strict=True) + if (self.lowlim is not None and v <= self.lowlim) + or (self.highlim is not None and self.highlim <= v) + ] + domain = Orange.data.Domain( + okattrs, data.domain.class_vars, metas=data.domain.metas + ) return data.from_table(domain, data) @@ -221,7 +251,6 @@ class SavitzkyGolayFeature(SelectColumn): class _SavitzkyGolayCommon(CommonDomainOrderUnknowns): - def __init__(self, window, polyorder, deriv, domain): super().__init__(domain) self.window = window @@ -229,15 +258,21 @@ def __init__(self, window, polyorder, deriv, domain): self.deriv = deriv def transformed(self, X, wavenumbers): - return savgol_filter(X, window_length=self.window, - polyorder=self.polyorder, - deriv=self.deriv, mode="nearest") + return savgol_filter( + X, + window_length=self.window, + polyorder=self.polyorder, + deriv=self.deriv, + mode="nearest", + ) def __eq__(self, other): - return super().__eq__(other) \ - and self.window == other.window \ - and self.polyorder == other.polyorder \ - and self.deriv == other.deriv + return ( + super().__eq__(other) + and self.window == other.window + and self.polyorder == other.polyorder + and self.deriv == other.deriv + ) def __hash__(self): return hash((super().__hash__(), self.window, self.polyorder, self.deriv)) @@ -254,12 +289,14 @@ def __init__(self, window=5, polyorder=2, deriv=0): self.deriv = deriv def __call__(self, data): - common = _SavitzkyGolayCommon(self.window, self.polyorder, - self.deriv, data.domain) - atts = [a.copy(compute_value=SavitzkyGolayFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + common = _SavitzkyGolayCommon( + self.window, self.polyorder, self.deriv, data.domain + ) + atts = [ + a.copy(compute_value=SavitzkyGolayFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -268,7 +305,6 @@ class RubberbandBaselineFeature(SelectColumn): class _RubberbandBaselineCommon(CommonDomainOrder): - def __init__(self, peak_dir, sub, domain): super().__init__(domain) self.peak_dir = peak_dir @@ -288,10 +324,10 @@ def transformed(self, X, x): else: if self.peak_dir == RubberbandBaseline.PeakPositive: v = np.roll(v, -v.argmin()) - v = v[:v.argmax() + 1] + v = v[: v.argmax() + 1] elif self.peak_dir == RubberbandBaseline.PeakNegative: v = np.roll(v, -v.argmax()) - v = v[:v.argmin() + 1] + v = v[: v.argmin() + 1] # If there are NaN values at the edges of data then convex hull # does not include the endpoints. Because the same values are also # NaN in the current row, we can fill them with NaN (bounds_error @@ -318,12 +354,12 @@ def __init__(self, peak_dir=PeakPositive, sub=Subtract): self.sub = sub def __call__(self, data): - common = _RubberbandBaselineCommon(self.peak_dir, self.sub, - data.domain) - atts = [a.copy(compute_value=RubberbandBaselineFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + common = _RubberbandBaselineCommon(self.peak_dir, self.sub, data.domain) + atts = [ + a.copy(compute_value=RubberbandBaselineFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -332,7 +368,6 @@ class LinearBaselineFeature(SelectColumn): class _LinearBaselineCommon(CommonDomainOrderUnknowns): - def __init__(self, peak_dir, sub, zero_points, domain): super().__init__(domain) self.peak_dir = peak_dir @@ -361,12 +396,14 @@ def __init__(self, peak_dir=PeakPositive, sub=Subtract, zero_points=None): self.zero_points = zero_points def __call__(self, data): - common = _LinearBaselineCommon(self.peak_dir, self.sub, self.zero_points, - data.domain) - atts = [a.copy(compute_value=LinearBaselineFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + common = _LinearBaselineCommon( + self.peak_dir, self.sub, self.zero_points, data.domain + ) + atts = [ + a.copy(compute_value=LinearBaselineFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -375,7 +412,6 @@ class NormalizeFeature(SelectColumn): class _NormalizeCommon(CommonDomain): - def __init__(self, method, lower, upper, int_method, attr, domain): super().__init__(domain) self.method = method @@ -404,21 +440,24 @@ def transformed(self, data): # keep nans where they were data.X[nans] = float("nan") elif self.method == Normalize.Area: - norm_data = Integrate(methods=self.int_method, - limits=[[self.lower, self.upper]])(data) + norm_data = Integrate( + methods=self.int_method, limits=[[self.lower, self.upper]] + )(data) data.X /= norm_data.X replace_infs(data.X) elif self.method == Normalize.SNV: - data.X = (data.X - bottleneck.nanmean(data.X, axis=1).reshape(-1, 1)) / \ - bottleneck.nanstd(data.X, axis=1).reshape(-1, 1) + data.X = ( + data.X - bottleneck.nanmean(data.X, axis=1).reshape(-1, 1) + ) / bottleneck.nanstd(data.X, axis=1).reshape(-1, 1) replace_infs(data.X) elif self.method == Normalize.Attribute: - if self.attr in data.domain and isinstance(data.domain[self.attr], Orange.data.ContinuousVariable): + if self.attr in data.domain and isinstance( + data.domain[self.attr], Orange.data.ContinuousVariable + ): ndom = Orange.data.Domain([data.domain[self.attr]]) factors = data.transform(ndom) data.X /= factors.X replace_infs(data.X) - nd = data.domain[self.attr] else: # invalid attribute for normalization data.X *= float("nan") elif self.method == Normalize.MinMax: @@ -429,23 +468,35 @@ def transformed(self, data): return data.X def __eq__(self, other): - return super().__eq__(other) \ - and self.method == other.method \ - and self.lower == other.lower \ - and self.upper == other.upper \ - and self.int_method == other.int_method \ - and self.attr == other.attr + return ( + super().__eq__(other) + and self.method == other.method + and self.lower == other.lower + and self.upper == other.upper + and self.int_method == other.int_method + and self.attr == other.attr + ) def __hash__(self): - return hash((super().__hash__(), self.method, self.lower, - self.upper, self.int_method, self.attr)) + return hash( + ( + super().__hash__(), + self.method, + self.lower, + self.upper, + self.int_method, + self.attr, + ) + ) class Normalize(Preprocess): # Normalization methods Vector, Area, Attribute, MinMax, SNV = 0, 1, 2, 3, 4 - def __init__(self, method=Vector, lower=float, upper=float, int_method=0, attr=None): + def __init__( + self, method=Vector, lower=float, upper=float, int_method=0, attr=None + ): self.method = method self.lower = lower self.upper = upper @@ -453,24 +504,24 @@ def __init__(self, method=Vector, lower=float, upper=float, int_method=0, attr=N self.attr = attr def __call__(self, data): - common = _NormalizeCommon(self.method, self.lower, self.upper, - self.int_method, self.attr, data.domain) - atts = [a.copy(compute_value=NormalizeFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + common = _NormalizeCommon( + self.method, self.lower, self.upper, self.int_method, self.attr, data.domain + ) + atts = [ + a.copy(compute_value=NormalizeFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) class _NormalizeReferenceCommon(CommonDomainRef): - def transformed(self, data): ref_X = self.interpolate_extend_to(self.reference, getx(data)) return replace_infs(data.X / ref_X) class NormalizeReference(Preprocess): - def __init__(self, reference): if reference is None: raise MissingReferenceException() @@ -480,39 +531,44 @@ def __init__(self, reference): def __call__(self, data): common = _NormalizeReferenceCommon(self.reference, data.domain) - atts = [a.copy(compute_value=NormalizeFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + atts = [ + a.copy(compute_value=NormalizeFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.transform(domain) class _NormalizePhaseReferenceCommon(CommonDomainRef): - def transformed(self, data): ref_X = self.interpolate_extend_to(self.reference, getx(data)) return replace_infs(np.angle(np.exp(data.X * 1j) / np.exp(ref_X * 1j))) - class NormalizePhaseReference(NormalizeReference): - def __call__(self, data): common = _NormalizePhaseReferenceCommon(self.reference, data.domain) - atts = [a.copy(compute_value=NormalizeFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + atts = [ + a.copy(compute_value=NormalizeFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.transform(domain) -def features_with_interpolation(points, kind="linear", domain=None, handle_nans=True, interpfn=None): - common = _InterpolateCommon(points, kind, domain, handle_nans=handle_nans, interpfn=interpfn) +def features_with_interpolation( + points, kind="linear", domain=None, handle_nans=True, interpfn=None +): + common = _InterpolateCommon( + points, kind, domain, handle_nans=handle_nans, interpfn=interpfn + ) atts = [] for i, p in enumerate(points): atts.append( - Orange.data.ContinuousVariable(name=str(p), - compute_value=InterpolatedFeature(i, common))) + Orange.data.ContinuousVariable( + name=str(p), compute_value=InterpolatedFeature(i, common) + ) + ) return atts @@ -521,7 +577,6 @@ class InterpolatedFeature(SelectColumn): class _InterpolateCommon: - def __init__(self, points, kind, domain, handle_nans=True, interpfn=None): self.points = points self.kind = kind @@ -532,8 +587,11 @@ def __init__(self, points, kind, domain, handle_nans=True, interpfn=None): def __call__(self, data): # convert to data domain if any conversion is possible, # otherwise we use the interpolator directly to make domains compatible - if self.domain is not None and data.domain != self.domain \ - and any(at.compute_value for at in self.domain.attributes): + if ( + self.domain is not None + and data.domain != self.domain + and any(at.compute_value for at in self.domain.attributes) + ): data = data.from_table(self.domain, data) x = getx(data) # removing whole NaN columns from the data will effectively replace @@ -555,16 +613,26 @@ def __call__(self, data): return interpfn(x, ys, self.points, kind=self.kind) def __eq__(self, other): - return type(self) is type(other) \ - and np.all(self.points == other.points) \ - and self.kind == other.kind \ - and self.domain == other.domain \ - and self.handle_nans == other.handle_nans \ - and self.interpfn == other.interpfn + return ( + type(self) is type(other) + and np.all(self.points == other.points) + and self.kind == other.kind + and self.domain == other.domain + and self.handle_nans == other.handle_nans + and self.interpfn == other.interpfn + ) def __hash__(self): - return hash((type(self), tuple(self.points[:5]), self.kind, - self.domain, self.handle_nans, self.interpfn)) + return hash( + ( + type(self), + tuple(self.points[:5]), + self.kind, + self.domain, + self.handle_nans, + self.interpfn, + ) + ) class Interpolate(Preprocess): @@ -585,10 +653,14 @@ def __init__(self, points, kind="linear", handle_nans=True): self.interpfn = None def __call__(self, data): - atts = features_with_interpolation(self.points, self.kind, data.domain, - self.handle_nans, interpfn=self.interpfn) - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + atts = features_with_interpolation( + self.points, + self.kind, + data.domain, + self.handle_nans, + interpfn=self.interpfn, + ) + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.transform(domain) @@ -609,7 +681,10 @@ class InterpolateToDomain(Preprocess): def __init__(self, target, kind="linear", handle_nans=True): self.target = target - if not all(isinstance(a, Orange.data.ContinuousVariable) for a in self.target.domain.attributes): + if not all( + isinstance(a, Orange.data.ContinuousVariable) + for a in self.target.domain.attributes + ): raise NotAllContinuousException() self.points = getx(self.target) self.kind = kind @@ -617,10 +692,16 @@ def __init__(self, target, kind="linear", handle_nans=True): self.interpfn = None def __call__(self, data): - X = _InterpolateCommon(self.points, self.kind, None, handle_nans=self.handle_nans, - interpfn=self.interpfn)(data) - domain = Orange.data.Domain(self.target.domain.attributes, data.domain.class_vars, - data.domain.metas) + X = _InterpolateCommon( + self.points, + self.kind, + None, + handle_nans=self.handle_nans, + interpfn=self.interpfn, + )(data) + domain = Orange.data.Domain( + self.target.domain.attributes, data.domain.class_vars, data.domain.metas + ) data = data.transform(domain) with data.unlocked_reference(data.X): data.X = X @@ -633,7 +714,6 @@ class XASnormalizationFeature(SelectColumn): class _XASnormalizationCommon(CommonDomainOrderUnknowns): - def __init__(self, edge, preedge_dict, postedge_dict, domain): super().__init__(domain) self.edge = edge @@ -646,7 +726,8 @@ def transformed(self, X, energies): try: spectra, jump_vals = normal_xas.normalize_all( - energies, X, self.edge, self.preedge_params, self.postedge_params) + energies, X, self.edge, self.preedge_params, self.postedge_params + ) jump_vals = jump_vals.reshape(-1, 1) except Exception: # TODO handle meaningful exceptions with PreprocessException @@ -671,16 +752,23 @@ def __init__(self, edge=None, preedge_dict=None, postedge_dict=None): self.postedge_params = postedge_dict def __call__(self, data): - common = _XASnormalizationCommon(self.edge, self.preedge_params, - self.postedge_params, data.domain) - newattrs = [ContinuousVariable(name=var.name, - compute_value=XASnormalizationFeature(i, common)) - for i, var in enumerate(data.domain.attributes)] - newmetas = data.domain.metas + (ContinuousVariable( - name='edge_jump', compute_value=XASnormalizationFeature(len(newattrs), common)),) - - domain = Orange.data.Domain( - newattrs, data.domain.class_vars, newmetas) + common = _XASnormalizationCommon( + self.edge, self.preedge_params, self.postedge_params, data.domain + ) + newattrs = [ + ContinuousVariable( + name=var.name, compute_value=XASnormalizationFeature(i, common) + ) + for i, var in enumerate(data.domain.attributes) + ] + newmetas = data.domain.metas + ( + ContinuousVariable( + name='edge_jump', + compute_value=XASnormalizationFeature(len(newattrs), common), + ), + ) + + domain = Orange.data.Domain(newattrs, data.domain.class_vars, newmetas) return data.from_table(domain, data) @@ -702,7 +790,9 @@ class _ExtractEXAFSCommon(CommonDomain): # not CommonDomainOrderUnknowns because E -> K # and because transformed needs Edge jumps - def __init__(self, edge, extra_from, extra_to, poly_deg, kweight, m, k_interp, domain): + def __init__( + self, edge, extra_from, extra_to, poly_deg, kweight, m, k_interp, domain + ): super().__init__(domain) self.edge = edge self.extra_from = extra_from @@ -720,7 +810,8 @@ def __call__(self, data): I_jumps = edges.X[:, 0] else: raise NoEdgejumpProvidedException( - 'Invalid meta data: Intensity jump at edge is missing') + 'Invalid meta data: Intensity jump at edge is missing' + ) # order X by wavenumbers: # xs non ordered energies @@ -738,7 +829,7 @@ def __call__(self, data): # Results are going to be discarded later. nan_rows = bottleneck.allnan(X, axis=1) if np.any(nan_rows): # if there were no nans X is a view, so do not modify - X[nan_rows] = 1. + X[nan_rows] = 1.0 # do the transformation X = self.transformed(X, xs[xsind], I_jumps) @@ -751,14 +842,21 @@ def __call__(self, data): def transformed(self, X, energies, I_jumps): try: - Km_Chi, Chi, bkgr = extra_exafs.extract_all(energies, X, - self.edge, I_jumps, - self.extra_from, self.extra_to, - self.poly_deg, self.kweight, self.m) + Km_Chi, Chi, bkgr = extra_exafs.extract_all( + energies, + X, + self.edge, + I_jumps, + self.extra_from, + self.extra_to, + self.poly_deg, + self.kweight, + self.m, + ) except Exception as e: # extra_exafs should be fixed to return a specific exception if "jump at edge" in e.args[0]: - raise EdgeJumpException("Problem with edge jump.") + raise EdgeJumpException("Problem with edge jump.") from e else: raise @@ -780,8 +878,15 @@ class ExtractEXAFS(Preprocess): ref : reference single-channel (Orange.data.Table) """ - def __init__(self, edge=None, extra_from=None, extra_to=None, - poly_deg=None, kweight=None, m=None): + def __init__( + self, + edge=None, + extra_from=None, + extra_to=None, + poly_deg=None, + kweight=None, + m=None, + ): self.edge = edge self.extra_from = extra_from self.extra_to = extra_to @@ -790,23 +895,34 @@ def __init__(self, edge=None, extra_from=None, extra_to=None, self.m = m def __call__(self, data): - if data.X.shape[1] > 0: # --- compute K energies = np.sort(getx(data)) # input data can be in any order - start_idx, end_idx = extra_exafs.get_idx_bounds(energies, self.edge, - self.extra_from, self.extra_to) - k_interp, k_points = extra_exafs.get_K_points(energies, self.edge, - start_idx, end_idx) + start_idx, end_idx = extra_exafs.get_idx_bounds( + energies, self.edge, self.extra_from, self.extra_to + ) + k_interp, k_points = extra_exafs.get_K_points( + energies, self.edge, start_idx, end_idx + ) # ---------- - common = _ExtractEXAFSCommon(self.edge, self.extra_from, self.extra_to, - self.poly_deg, self.kweight, self.m, k_interp, - data.domain) - - newattrs = [ContinuousVariable(name=str(var), - compute_value=ExtractEXAFSFeature(i, common)) - for i, var in enumerate(k_interp)] + common = _ExtractEXAFSCommon( + self.edge, + self.extra_from, + self.extra_to, + self.poly_deg, + self.kweight, + self.m, + k_interp, + data.domain, + ) + + newattrs = [ + ContinuousVariable( + name=str(var), compute_value=ExtractEXAFSFeature(i, common) + ) + for i, var in enumerate(k_interp) + ] else: newattrs = [] @@ -819,13 +935,11 @@ class ShiftAndScaleFeature(SelectColumn): class _ShiftAndScaleCommon(CommonDomain): - def __init__(self, offset, scale, domain): super().__init__(domain) self.offset = offset self.scale = scale - def transformed(self, data): return self.scale * data.X + self.offset @@ -841,16 +955,17 @@ class ShiftAndScale(Preprocess): scale : float """ - def __init__(self, offset=0., scale=1.): + def __init__(self, offset=0.0, scale=1.0): self.offset = offset self.scale = scale def __call__(self, data): common = _ShiftAndScaleCommon(self.offset, self.scale, data.domain) - atts = [a.copy(compute_value=ShiftAndScaleFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + atts = [ + a.copy(compute_value=ShiftAndScaleFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -877,14 +992,18 @@ def interpolatespikes(spikes, row_in): w = np.arange(max(i - self.dis, 0), min(i + self.dis + 1, len(spikes))) w2 = w[spikes[w] == 0] row_out[i] = np.nanmean(row_in[w2]) - if np.isnan(row_out[i]): # no valid points within distance, find closest value + if np.isnan( + row_out[i] + ): # no valid points within distance, find closest value if non_nan_nor_spike is None: # compute it only once - non_nan_nor_spike = np.nonzero(~(np.isnan(row_in) | (spikes > 0)))[0] + non_nan_nor_spike = np.nonzero( + ~(np.isnan(row_in) | (spikes > 0)) + )[0] # find nearest non-nan indices valid_ind = [] insertp = np.searchsorted(non_nan_nor_spike, i, side="left") if insertp > 0: - valid_ind.append(non_nan_nor_spike[insertp-1]) # left non-nan + valid_ind.append(non_nan_nor_spike[insertp - 1]) # left non-nan if insertp < len(non_nan_nor_spike): valid_ind.append(non_nan_nor_spike[insertp]) # right non-nan valid_ind = np.array(valid_ind) @@ -904,7 +1023,7 @@ def interpolatespikes(spikes, row_in): median1 = np.median(row) mad_int = np.median(np.abs(row - median1)) modified_z_scores = 0.6745 * (row - median1) / mad_int - spikes = (abs(modified_z_scores) > self.threshold) + spikes = abs(modified_z_scores) > self.threshold despiked = interpolatespikes(spikes, row) out.append(despiked) else: @@ -915,29 +1034,26 @@ def interpolatespikes(spikes, row_in): class Despike(Preprocess): - def __init__(self, threshold=7, cutoff=100, dis=5): self.threshold = threshold self.cutoff = cutoff self.dis = dis def __call__(self, data): - common = _DespikeCommon(self.threshold, self.cutoff, - self.dis, data.domain) - atts = [a.copy(compute_value=DespikeFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + common = _DespikeCommon(self.threshold, self.cutoff, self.dis, data.domain) + atts = [ + a.copy(compute_value=DespikeFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) - class SpSubtractFeature(SelectColumn): InheritEq = True class _SpSubtractCommon(CommonDomainRef): - def __init__(self, amount, reference, domain): super().__init__(reference, domain) self.amount = amount @@ -960,7 +1076,7 @@ class SpSubtract(Preprocess): reference : reference single-channel (Orange.data.Table) """ - def __init__(self, reference, amount=0.): + def __init__(self, reference, amount=0.0): if reference is None or len(reference) != 1: raise WrongReferenceException("Reference data should have length 1") self.reference = reference @@ -968,9 +1084,9 @@ def __init__(self, reference, amount=0.): def __call__(self, data): common = _SpSubtractCommon(self.amount, self.reference, data.domain) - atts = [a.copy(compute_value=SpSubtractFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + atts = [ + a.copy(compute_value=SpSubtractFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) - diff --git a/orangecontrib/spectroscopy/preprocess/als/__init__.py b/orangecontrib/spectroscopy/preprocess/als/__init__.py index 46ad912ce..8a4e1366f 100644 --- a/orangecontrib/spectroscopy/preprocess/als/__init__.py +++ b/orangecontrib/spectroscopy/preprocess/als/__init__.py @@ -4,10 +4,11 @@ import Orange.data from Orange.preprocess.preprocess import Preprocess -from orangecontrib.spectroscopy.preprocess.utils import SelectColumn, \ - CommonDomainOrderUnknowns -from orangecontrib.spectroscopy.preprocess.als.baseline import als, arpls, \ - airpls +from orangecontrib.spectroscopy.preprocess.utils import ( + SelectColumn, + CommonDomainOrderUnknowns, +) +from orangecontrib.spectroscopy.preprocess.als.baseline import als, arpls, airpls class ALSFeature(SelectColumn): @@ -27,15 +28,17 @@ def transformed(self, data, X): if data.size > 0: for row in data: input_array = row - final.append(input_array - als(input_array, lam=self.lam, - p=self.p, itermax=self.itermax)) + final.append( + input_array + - als(input_array, lam=self.lam, p=self.p, itermax=self.itermax) + ) return np.array(final) else: return data class ALSP(Preprocess): - lam = 1E+6 + lam = 1e6 itermax = 10 p = 0.1 @@ -46,10 +49,11 @@ def __init__(self, lam=lam, itermax=itermax, p=p): def __call__(self, data): common = ALSCommon(self.lam, self.itermax, self.p, data.domain) - atts = [a.copy(compute_value=ALSFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + atts = [ + a.copy(compute_value=ALSFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -58,8 +62,7 @@ class ARPLSFeature(SelectColumn): class ARPLSCommon(CommonDomainOrderUnknowns): - def __init__(self, lam, itermax, - ratio, domain): + def __init__(self, lam, itermax, ratio, domain): super().__init__(domain) self.lam = lam self.itermax = itermax @@ -70,32 +73,32 @@ def transformed(self, data, X): final = [] if data.size > 0: for row in data: - final.append(row - arpls(row, lam=self.lam, itermax=self.itermax, - ratio=self.ratio)) + final.append( + row + - arpls(row, lam=self.lam, itermax=self.itermax, ratio=self.ratio) + ) return np.array(final) else: return data class ARPLS(Preprocess): - lam = 100E+6 + lam = 100e6 itermax = 10 ratio = 0.5 - def __init__(self, lam=lam, - ratio=ratio, itermax=itermax): + def __init__(self, lam=lam, ratio=ratio, itermax=itermax): self.lam = lam self.itermax = itermax self.ratio = ratio def __call__(self, data): - common = ARPLSCommon(self.lam, - self.itermax, self.ratio, - data.domain) - atts = [a.copy(compute_value=ARPLSFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + common = ARPLSCommon(self.lam, self.itermax, self.ratio, data.domain) + atts = [ + a.copy(compute_value=ARPLSFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -115,8 +118,12 @@ def transformed(self, data, X): final = [] if data.size > 0: for row in data: - final.append(row - airpls(row, lam=self.lam, - porder=self.porder, itermax=self.itermax)) + final.append( + row + - airpls( + row, lam=self.lam, porder=self.porder, itermax=self.itermax + ) + ) return np.array(final) else: @@ -124,7 +131,7 @@ def transformed(self, data, X): class AIRPLS(Preprocess): - lam = 1E+6 + lam = 1e6 itermax = 10 porder = 1 @@ -134,10 +141,10 @@ def __init__(self, lam=lam, itermax=itermax, porder=porder): self.porder = porder def __call__(self, data): - common = AIRPLSCommon(self.lam, self.itermax, - self.porder, data.domain) - atts = [a.copy(compute_value=AIRPLSFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + common = AIRPLSCommon(self.lam, self.itermax, self.porder, data.domain) + atts = [ + a.copy(compute_value=AIRPLSFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) diff --git a/orangecontrib/spectroscopy/preprocess/als/baseline.py b/orangecontrib/spectroscopy/preprocess/als/baseline.py index 83e539add..b60468559 100644 --- a/orangecontrib/spectroscopy/preprocess/als/baseline.py +++ b/orangecontrib/spectroscopy/preprocess/als/baseline.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# ruff: noqa """ Created on Wed Dec 30 16:05:47 2015 @@ -28,6 +29,7 @@ import numpy as np from scipy.sparse.linalg import spsolve from scipy.linalg import cholesky + # from scikits.sparse.cholmod import cholesky from scipy import sparse @@ -75,9 +77,11 @@ def als(y, lam=1e6, p=0.1, itermax=10): """ L = len(y) -# D = sparse.csc_matrix(np.diff(np.eye(L), 2)) + # D = sparse.csc_matrix(np.diff(np.eye(L), 2)) D = sparse.eye(L, format='csc') - D = D[1:] - D[:-1] # numpy.diff( ,2) does not work with sparse matrix. This is a workaround. + D = ( + D[1:] - D[:-1] + ) # numpy.diff( ,2) does not work with sparse matrix. This is a workaround. D = D[1:] - D[:-1] D = D.T w = np.ones(L) @@ -127,9 +131,11 @@ def arpls(y, lam=1e4, ratio=0.05, itermax=100): """ N = len(y) -# D = sparse.csc_matrix(np.diff(np.eye(N), 2)) + # D = sparse.csc_matrix(np.diff(np.eye(N), 2)) D = sparse.eye(N, format='csc') - D = D[1:] - D[:-1] # numpy.diff( ,2) does not work with sparse matrix. This is a workaround. + D = ( + D[1:] - D[:-1] + ) # numpy.diff( ,2) does not work with sparse matrix. This is a workaround. D = D[1:] - D[:-1] H = lam * D.T * D @@ -146,7 +152,7 @@ def arpls(y, lam=1e4, ratio=0.05, itermax=100): dn = d[d < 0] m = np.mean(dn) s = np.std(dn) - wt = 1. / (1 + np.exp(2 * (d - (2 * s - m)) / s)) + wt = 1.0 / (1 + np.exp(2 * (d - (2 * s - m)) / s)) if np.linalg.norm(w - wt) / np.linalg.norm(w) < ratio: break w = wt @@ -175,10 +181,12 @@ def WhittakerSmooth(x, w, lam, differences=1): # X = np.matrix(x) # replaced with the line below to avoid np.matrix X = np.asarray(x) m = X.size -# D = csc_matrix(np.diff(np.eye(m), differences)) + # D = csc_matrix(np.diff(np.eye(m), differences)) D = sparse.eye(m, format='csc') for i in range(differences): - D = D[1:] - D[:-1] # numpy.diff() does not work with sparse matrix. This is a workaround. + D = ( + D[1:] - D[:-1] + ) # numpy.diff() does not work with sparse matrix. This is a workaround. W = sparse.diags(w, 0, shape=(m, m)) A = sparse.csc_matrix(W + (lam * D.T * D)) # B = sparse.csc_matrix(W * X.T) # replaced with the line below to avoid np.matrix @@ -233,14 +241,16 @@ def airpls(x, lam=100, porder=1, itermax=100): z = WhittakerSmooth(x, w, lam, porder) d = x - z dssn = np.abs(d[d < 0].sum()) - if(dssn < 0.001 * (abs(x)).sum() or i == itermax): - if(i == itermax): - #print('airpls: max iteration reached!') # commented in Orange-spectroscopy + if dssn < 0.001 * (abs(x)).sum() or i == itermax: + if i == itermax: + # print('airpls: max iteration reached!') # commented in Orange-spectroscopy pass break w[d >= 0] = 0 # d>0 means that this point is part of a peak, # so its weight is set to 0 in order to ignore it - if np.shape(w[d >= 0]) == (0,) or np.shape(w[d < 0]) == (0,): # addded in Orange-spectroscopy + if np.shape(w[d >= 0]) == (0,) or np.shape(w[d < 0]) == ( + 0, + ): # addded in Orange-spectroscopy break w[d < 0] = np.exp(i * np.abs(d[d < 0]) / dssn) w[0] = np.exp(i * (d[d < 0]).max() / dssn) diff --git a/orangecontrib/spectroscopy/preprocess/atm_corr.py b/orangecontrib/spectroscopy/preprocess/atm_corr.py index 90cf3281b..f21814a83 100755 --- a/orangecontrib/spectroscopy/preprocess/atm_corr.py +++ b/orangecontrib/spectroscopy/preprocess/atm_corr.py @@ -7,8 +7,13 @@ from Orange.preprocess.preprocess import Preprocess from orangecontrib.spectroscopy.data import getx, spectra_mean -from orangecontrib.spectroscopy.preprocess.utils import SelectColumn, CommonDomainOrderUnknowns, \ - interp1d_with_unknowns_numpy, nan_extend_edges_and_interpolate, MissingReferenceException +from orangecontrib.spectroscopy.preprocess.utils import ( + SelectColumn, + CommonDomainOrderUnknowns, + interp1d_with_unknowns_numpy, + nan_extend_edges_and_interpolate, + MissingReferenceException, +) class _AtmCorr(CommonDomainOrderUnknowns): @@ -31,8 +36,18 @@ class _AtmCorr(CommonDomainOrderUnknowns): spline that merges gradually with the data at its edges (using a Tukey window with alpha=0.3). """ - def __init__(self, reference, *, correct_ranges, spline_ranges, - smooth_win, spline_base_win, mean_reference, domain): + + def __init__( + self, + reference, + *, + correct_ranges, + spline_ranges, + smooth_win, + spline_base_win, + mean_reference, + domain, + ): super().__init__(domain) self.reference = reference if correct_ranges is not None: @@ -49,12 +64,15 @@ def __init__(self, reference, *, correct_ranges, spline_ranges, self.mean_reference = mean_reference def transformed(self, X, wavenumbers): - def interpolate_to_data(other_xs, other_data): # all input data needs to be interpolated (and NaNs removed) - interpolated = interp1d_with_unknowns_numpy(other_xs, other_data, wavenumbers) + interpolated = interp1d_with_unknowns_numpy( + other_xs, other_data, wavenumbers + ) # we know that X is not NaN. same handling of reference as of X - interpolated, _ = nan_extend_edges_and_interpolate(wavenumbers, interpolated) + interpolated, _ = nan_extend_edges_and_interpolate( + wavenumbers, interpolated + ) return interpolated def find_wn_ranges(wn, ranges): @@ -62,8 +80,13 @@ def find_wn_ranges(wn, ranges): if not len(ranges): return np.zeros((0, 0)) ranges = np.asarray(ranges) - r = np.stack((np.searchsorted(wn, ranges[:,0]), - np.searchsorted(wn, ranges[:,1], 'right')), 1) + r = np.stack( + ( + np.searchsorted(wn, ranges[:, 0]), + np.searchsorted(wn, ranges[:, 1], 'right'), + ), + 1, + ) # r[r == len(wn)] = len(wn) - 1 return r @@ -83,11 +106,12 @@ def find_wn_ranges(wn, ranges): # remove baseline in reference (skip this?) for p, q in ranges: - atms[:, p:q] -= interp1d(wavenumbers[[p,q-1]], - atms[:, [p,q-1]])(wavenumbers[p:q]) + atms[:, p:q] -= interp1d(wavenumbers[[p, q - 1]], atms[:, [p, q - 1]])( + wavenumbers[p:q] + ) # Basic removal of atmospheric spectrum - dy = X[:,:-1] - X[:,1:] + dy = X[:, :-1] - X[:, 1:] az = np.zeros((len(atms), len(X), len(ranges))) for ia, atm in enumerate(atms): dh = atm[:-1] - atm[1:] @@ -95,9 +119,11 @@ def find_wn_ranges(wn, ranges): dhdy = np.cumsum(dy * dh, 1) for i, (p, q) in enumerate(ranges): r = q - 2 - az[ia, :, i] = \ - ((dhdy[:,r] - dhdy[:,p-1]) / (dh2[r] - dh2[p-1])) \ - if p > 0 else (dhdy[:,r] / dh2[r]) + az[ia, :, i] = ( + ((dhdy[:, r] - dhdy[:, p - 1]) / (dh2[r] - dh2[p - 1])) + if p > 0 + else (dhdy[:, r] / dh2[r]) + ) az2sum = (az * az).sum(0) az = az**3 / az2sum for ia, atm in enumerate(atms): @@ -118,28 +144,48 @@ def find_wn_ranges(wn, ranges): continue Pw, Qw = wavenumbers[[P, Q]] bw = int(self.spline_base_win) // 2 - cr = [max(P - bw, 0), min(P + bw + 1, len(wavenumbers)), - max(Q - bw, 0), min(Q + bw + 1, len(wavenumbers))] + cr = [ + max(P - bw, 0), + min(P + bw + 1, len(wavenumbers)), + max(Q - bw, 0), + min(Q + bw + 1, len(wavenumbers)), + ] a = np.empty((4, len(y))) - a[0:2,:] = np.polyfit(wavenumbers[cr[0]:cr[1]] - Pw, - y[:,cr[0]:cr[1]].T, deg=1) - a[2:4,:] = np.polyfit(wavenumbers[cr[2]:cr[3]] - Qw, - y[:,cr[2]:cr[3]].T, deg=1) - a[::2,:] = a[::2,:] * (Qw - Pw) - t = np.interp(wavenumbers[P:Q+1], [Pw, Qw], [0, 1]) - tt = np.array([t**3-2*t**2+t, 2*t**3-3*t**2+1, t**3-t**2, -2*t**3+3*t**2]) + a[0:2, :] = np.polyfit( + wavenumbers[cr[0] : cr[1]] - Pw, y[:, cr[0] : cr[1]].T, deg=1 + ) + a[2:4, :] = np.polyfit( + wavenumbers[cr[2] : cr[3]] - Qw, y[:, cr[2] : cr[3]].T, deg=1 + ) + a[::2, :] = a[::2, :] * (Qw - Pw) + t = np.interp(wavenumbers[P : Q + 1], [Pw, Qw], [0, 1]) + tt = np.array( + [ + t**3 - 2 * t**2 + t, + 2 * t**3 - 3 * t**2 + 1, + t**3 - t**2, + -2 * t**3 + 3 * t**2, + ] + ) pt = a.T @ tt - y[:, P:Q+1] += (pt - y[:, P:Q+1]) * tukey(len(t), self.spline_tukey_param) + y[:, P : Q + 1] += (pt - y[:, P : Q + 1]) * tukey( + len(t), self.spline_tukey_param + ) return y class AtmCorr(Preprocess): - - def __init__(self, reference=None, correct_ranges=None, - spline_ranges=None, smooth_win=0, spline_base_win=9, - mean_reference=True): + def __init__( + self, + reference=None, + correct_ranges=None, + spline_ranges=None, + smooth_win=0, + spline_base_win=9, + mean_reference=True, + ): if reference is None: raise MissingReferenceException() self.reference = reference @@ -151,17 +197,20 @@ def __init__(self, reference=None, correct_ranges=None, def __call__(self, data): # creates function for transforming data - common = _AtmCorr(reference=self.reference, - correct_ranges=self.correct_ranges, - spline_ranges=self.spline_ranges, - smooth_win=self.smooth_win, - spline_base_win=self.spline_base_win, - mean_reference=self.mean_reference, - domain=data.domain) + common = _AtmCorr( + reference=self.reference, + correct_ranges=self.correct_ranges, + spline_ranges=self.spline_ranges, + smooth_win=self.smooth_win, + spline_base_win=self.spline_base_win, + mean_reference=self.mean_reference, + domain=data.domain, + ) # takes care of domain column-wise, by above transformation function - atts = [a.copy(compute_value=SelectColumn(i, common)) - for i, a in enumerate(data.domain.attributes)] + atts = [ + a.copy(compute_value=SelectColumn(i, common)) + for i, a in enumerate(data.domain.attributes) + ] - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas) + domain = Orange.data.Domain(atts, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) diff --git a/orangecontrib/spectroscopy/preprocess/emsc.py b/orangecontrib/spectroscopy/preprocess/emsc.py index 8fdac4317..1a9d6775b 100644 --- a/orangecontrib/spectroscopy/preprocess/emsc.py +++ b/orangecontrib/spectroscopy/preprocess/emsc.py @@ -6,9 +6,16 @@ from Orange.data.util import get_unique_names from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.preprocess.utils import SelectColumn, CommonDomainOrderUnknowns, \ - interp1d_with_unknowns_numpy, MissingReferenceException, interpolate_extend_to, \ - CommonDomainRef, table_eq_x, subset_for_hash +from orangecontrib.spectroscopy.preprocess.utils import ( + SelectColumn, + CommonDomainOrderUnknowns, + interp1d_with_unknowns_numpy, + MissingReferenceException, + interpolate_extend_to, + CommonDomainRef, + table_eq_x, + subset_for_hash, +) from orangecontrib.spectroscopy.preprocess.npfunc import Function, Segments @@ -16,6 +23,7 @@ class SelectionFunction(Function): """ Weighted selection function. Includes min and max. """ + def __init__(self, min_, max_, w): super().__init__(None) self.min_ = min_ @@ -23,17 +31,22 @@ def __init__(self, min_, max_, w): self.w = w def __call__(self, x): - seg = Segments((lambda x: True, lambda x: 0), - (lambda x: np.logical_and(x >= self.min_, x <= self.max_), - lambda x: self.w) - ) + seg = Segments( + (lambda x: True, lambda x: 0), + ( + lambda x: np.logical_and(x >= self.min_, x <= self.max_), + lambda x: self.w, + ), + ) return seg(x) def __eq__(self, other): - return super().__eq__(other) \ - and self.min_ == other.min_ \ - and self.max_ == other.max_ \ - and self.w == other.w + return ( + super().__eq__(other) + and self.min_ == other.min_ + and self.max_ == other.max_ + and self.w == other.w + ) def __hash__(self): return hash((super().__hash__(), self.min_, self.max_, self.w)) @@ -44,22 +57,27 @@ class SmoothedSelectionFunction(SelectionFunction): Weighted selection function. Min and max points are middle points of smoothing with hyperbolic tangent. """ + def __init__(self, min_, max_, s, w): super().__init__(min_, max_, w) self.s = s def __call__(self, x): middle = (self.min_ + self.max_) / 2 - seg = Segments((lambda x: x < middle, - lambda x: (np.tanh((x - self.min_) / self.s) + 1) / 2 * self.w), - (lambda x: x >= middle, - lambda x: (-np.tanh((x - self.max_) / self.s) + 1) / 2 * self.w) - ) + seg = Segments( + ( + lambda x: x < middle, + lambda x: (np.tanh((x - self.min_) / self.s) + 1) / 2 * self.w, + ), + ( + lambda x: x >= middle, + lambda x: (-np.tanh((x - self.max_) / self.s) + 1) / 2 * self.w, + ), + ) return seg(x) def __eq__(self, other): - return super().__eq__(other) \ - and self.s == other.s + return super().__eq__(other) and self.s == other.s def __hash__(self): return hash((super().__hash__(), self.s)) @@ -93,7 +111,6 @@ class EMSCModel(SelectColumn): class _EMSC(CommonDomainOrderUnknowns, CommonDomainRef): - def __init__(self, reference, badspectra, weights, order, scaling, domain): CommonDomainOrderUnknowns.__init__(self, domain) CommonDomainRef.__init__(self, reference, domain) @@ -110,7 +127,7 @@ def transformed(self, X, wavenumbers): wei_X = weighted_wavenumbers(self.weights, wavenumbers) N = wavenumbers.shape[0] - m0 = - 2.0 / (wavenumbers[0] - wavenumbers[N - 1]) + m0 = -2.0 / (wavenumbers[0] - wavenumbers[N - 1]) c_coeff = 0.5 * (wavenumbers[0] + wavenumbers[N - 1]) n_badspec = len(self.badspectra) if self.badspectra is not None else 0 @@ -118,26 +135,28 @@ def transformed(self, X, wavenumbers): badspectra_X = interpolate_extend_to(self.badspectra, wavenumbers) M = [] - for x in range(0, self.order+1): + for x in range(0, self.order + 1): M.append((m0 * (wavenumbers - c_coeff)) ** x) for y in range(0, n_badspec): M.append(badspectra_X[y]) M.append(ref_X) # always add reference spectrum to the model n_add_model = len(M) - M = np.vstack(M).T # M is for the correction, for par. estimation M_weighted is used + M = np.vstack( + M + ).T # M is for the correction, for par. estimation M_weighted is used - M_weighted = M*wei_X.T + M_weighted = M * wei_X.T newspectra = np.zeros((X.shape[0], X.shape[1] + n_add_model)) for i, rawspectrum in enumerate(X): - rawspectrumW = (rawspectrum*wei_X)[0] + rawspectrumW = (rawspectrum * wei_X)[0] # noqa: F841 m = np.linalg.lstsq(M_weighted, rawspectrum, rcond=-1)[0] corrected = rawspectrum - for x in range(0, self.order+1+n_badspec): - corrected = (corrected - (m[x] * M[:, x])) + for x in range(0, self.order + 1 + n_badspec): + corrected = corrected - (m[x] * M[:, x]) if self.scaling: - corrected = corrected/m[self.order+1+n_badspec] + corrected = corrected / m[self.order + 1 + n_badspec] corrected[np.isinf(corrected)] = np.nan # fix values caused by zero weights corrected = np.hstack((corrected, m)) # append the model parameters newspectra[i] = corrected @@ -145,31 +164,55 @@ def transformed(self, X, wavenumbers): return newspectra def __eq__(self, other): - return CommonDomainRef.__eq__(self, other) \ - and table_eq_x(self.badspectra, other.badspectra) \ - and self.order == other.order \ - and self.scaling == other.scaling \ - and (self.weights == other.weights - if not isinstance(self.weights, Table) - else table_eq_x(self.weights, other.weights)) + return ( + CommonDomainRef.__eq__(self, other) + and table_eq_x(self.badspectra, other.badspectra) + and self.order == other.order + and self.scaling == other.scaling + and ( + self.weights == other.weights + if not isinstance(self.weights, Table) + else table_eq_x(self.weights, other.weights) + ) + ) def __hash__(self): domain = self.badspectra.domain if self.badspectra is not None else None fv = subset_for_hash(self.badspectra.X) if self.badspectra is not None else None - weights = self.weights if not isinstance(self.weights, Table) \ + weights = ( + self.weights + if not isinstance(self.weights, Table) else subset_for_hash(self.weights.X) - return hash((CommonDomainRef.__hash__(self), domain, fv, weights, self.order, self.scaling)) + ) + return hash( + ( + CommonDomainRef.__hash__(self), + domain, + fv, + weights, + self.order, + self.scaling, + ) + ) def average_table_x(data): - return Orange.data.Table.from_numpy(Orange.data.Domain(data.domain.attributes), - X=data.X.mean(axis=0, keepdims=True)) + return Orange.data.Table.from_numpy( + Orange.data.Domain(data.domain.attributes), X=data.X.mean(axis=0, keepdims=True) + ) class EMSC(Preprocess): - - def __init__(self, reference=None, badspectra=None, weights=None, order=2, scaling=True, - output_model=False, ranges=None): + def __init__( + self, + reference=None, + badspectra=None, + weights=None, + order=2, + scaling=True, + output_model=False, + ranges=None, + ): # the first non-kwarg can not be a data table (Preprocess limitations) # ranges could be a list like this [[800, 1000], [1300, 1500]] if reference is None: @@ -185,32 +228,49 @@ def __init__(self, reference=None, badspectra=None, weights=None, order=2, scali def __call__(self, data): # creates function for transforming data - common = _EMSC(self.reference, self.badspectra, self.weights, self.order, - self.scaling, data.domain) + common = _EMSC( + self.reference, + self.badspectra, + self.weights, + self.order, + self.scaling, + data.domain, + ) # takes care of domain column-wise, by above transformation function - atts = [a.copy(compute_value=EMSCFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] + atts = [ + a.copy(compute_value=EMSCFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] model_metas = [] n_badspec = len(self.badspectra) if self.badspectra is not None else 0 - used_names = set([var.name for var in data.domain.variables + data.domain.metas]) + used_names = set( + [var.name for var in data.domain.variables + data.domain.metas] + ) if self.output_model: i = len(data.domain.attributes) - for o in range(self.order+1): + for o in range(self.order + 1): n = get_unique_names(used_names, "EMSC parameter " + str(o)) model_metas.append( - Orange.data.ContinuousVariable(name=n, - compute_value=EMSCModel(i, common))) + Orange.data.ContinuousVariable( + name=n, compute_value=EMSCModel(i, common) + ) + ) i += 1 for o in range(n_badspec): n = get_unique_names(used_names, "EMSC parameter bad spec " + str(o)) model_metas.append( - Orange.data.ContinuousVariable(name=n, - compute_value=EMSCModel(i, common))) + Orange.data.ContinuousVariable( + name=n, compute_value=EMSCModel(i, common) + ) + ) i += 1 n = get_unique_names(used_names, "EMSC scaling parameter") model_metas.append( - Orange.data.ContinuousVariable(name=n, - compute_value=EMSCModel(i, common))) - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas + tuple(model_metas)) + Orange.data.ContinuousVariable( + name=n, compute_value=EMSCModel(i, common) + ) + ) + domain = Orange.data.Domain( + atts, data.domain.class_vars, data.domain.metas + tuple(model_metas) + ) return data.from_table(domain, data) diff --git a/orangecontrib/spectroscopy/preprocess/integrate.py b/orangecontrib/spectroscopy/preprocess/integrate.py index 88266f817..a801e8669 100644 --- a/orangecontrib/spectroscopy/preprocess/integrate.py +++ b/orangecontrib/spectroscopy/preprocess/integrate.py @@ -11,19 +11,24 @@ from AnyQt.QtCore import Qt from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.preprocess.utils import nan_extend_edges_and_interpolate, \ - CommonDomain, \ - edge_baseline, linear_baseline +from orangecontrib.spectroscopy.preprocess.utils import ( + nan_extend_edges_and_interpolate, + CommonDomain, + edge_baseline, + linear_baseline, +) INTEGRATE_DRAW_CURVE_WIDTH = 2 INTEGRATE_DRAW_EDGE_WIDTH = 1 -INTEGRATE_DRAW_BASELINE_PENARGS = {"width": INTEGRATE_DRAW_CURVE_WIDTH, "style": Qt.DotLine} +INTEGRATE_DRAW_BASELINE_PENARGS = { + "width": INTEGRATE_DRAW_CURVE_WIDTH, + "style": Qt.DotLine, +} INTEGRATE_DRAW_CURVE_PENARGS = {"width": INTEGRATE_DRAW_CURVE_WIDTH} INTEGRATE_DRAW_EDGE_PENARGS = {"width": INTEGRATE_DRAW_EDGE_WIDTH} class IntegrateFeature(SharedComputeValue): - def __init__(self, limits, commonfn): self.limits = limits super().__init__(commonfn) @@ -60,7 +65,7 @@ def compute_draw_info(self, x_s, y_s): @staticmethod def parameters(): - """ Return parameters for this type of integral """ + """Return parameters for this type of integral""" raise NotImplementedError def compute_baseline(self, x_s, y_s): @@ -74,24 +79,24 @@ def compute(self, data, common): return self.compute_integral(x_s, y_s) def __eq__(self, other): - return super().__eq__(other) \ - and self.limits == other.limits + return super().__eq__(other) and self.limits == other.limits def __hash__(self): return hash((super().__hash__(), tuple(self.limits))) class IntegrateFeatureEdgeBaseline(IntegrateFeature): - """ A linear edge-to-edge baseline subtraction. """ + """A linear edge-to-edge baseline subtraction.""" name = "Integral from baseline" InheritEq = True @staticmethod def parameters(): - return (("Low limit", "Low limit for integration (inclusive)"), - ("High limit", "High limit for integration (inclusive)"), - ) + return ( + ("Low limit", "Low limit for integration (inclusive)"), + ("High limit", "High limit for integration (inclusive)"), + ) def compute_baseline(self, x, y): if np.any(np.isnan(y)): @@ -106,13 +111,17 @@ def compute_integral(self, x, y_s): return scipy.integrate.trapezoid(y_s, x, axis=1) def compute_draw_info(self, x, ys): - return [("curve", (x, self.compute_baseline(x, ys), INTEGRATE_DRAW_BASELINE_PENARGS)), - ("curve", (x, ys, INTEGRATE_DRAW_BASELINE_PENARGS)), - ("fill", ((x, self.compute_baseline(x, ys)), (x, ys)))] + return [ + ( + "curve", + (x, self.compute_baseline(x, ys), INTEGRATE_DRAW_BASELINE_PENARGS), + ), + ("curve", (x, ys, INTEGRATE_DRAW_BASELINE_PENARGS)), + ("fill", ((x, self.compute_baseline(x, ys)), (x, ys))), + ] class IntegrateFeatureEdgeBaselineAbsolute(IntegrateFeatureEdgeBaseline): - name = "Absolute integral from baseline" InheritEq = True @@ -125,23 +134,25 @@ def compute_integral(self, x, y_s): def compute_draw_info(self, x, ys): baseline = self.compute_baseline(x, ys) abs_ys = np.abs(ys - baseline) + baseline - return [("curve", (x, baseline, INTEGRATE_DRAW_BASELINE_PENARGS)), - ("curve", (x, abs_ys, INTEGRATE_DRAW_BASELINE_PENARGS)), - ("fill", ((x, baseline), (x, abs_ys)))] + return [ + ("curve", (x, baseline, INTEGRATE_DRAW_BASELINE_PENARGS)), + ("curve", (x, abs_ys, INTEGRATE_DRAW_BASELINE_PENARGS)), + ("fill", ((x, baseline), (x, abs_ys))), + ] class IntegrateFeatureSeparateBaseline(IntegrateFeature): - name = "Integral from separate baseline" InheritEq = True @staticmethod def parameters(): - return (("Low limit", "Low limit for integration (inclusive)"), - ("High limit", "High limit for integration (inclusive)"), - ("Low limit (baseline)", "Low limit for baseline (inclusive)"), - ("High limit (baseline)", "High limit for baseline (inclusive)"), - ) + return ( + ("Low limit", "Low limit for integration (inclusive)"), + ("High limit", "High limit for integration (inclusive)"), + ("Low limit (baseline)", "Low limit for baseline (inclusive)"), + ("High limit (baseline)", "High limit for baseline (inclusive)"), + ) def compute_baseline(self, x_s, y_s): if np.any(np.isnan(y_s)): @@ -168,13 +179,21 @@ def compute_integral(self, x_s, y_s): def compute_draw_info(self, x_s, y_s): xl, ysl = self.limit_region(x_s, y_s) - return [("curve", (x_s, self.compute_baseline(x_s, y_s), INTEGRATE_DRAW_BASELINE_PENARGS)), - ("curve", (xl, ysl, INTEGRATE_DRAW_BASELINE_PENARGS)), - ("fill", (self.limit_region(x_s, self.compute_baseline(x_s, y_s)), (xl, ysl)))] + return [ + ( + "curve", + (x_s, self.compute_baseline(x_s, y_s), INTEGRATE_DRAW_BASELINE_PENARGS), + ), + ("curve", (xl, ysl, INTEGRATE_DRAW_BASELINE_PENARGS)), + ( + "fill", + (self.limit_region(x_s, self.compute_baseline(x_s, y_s)), (xl, ysl)), + ), + ] class IntegrateFeatureSimple(IntegrateFeatureEdgeBaseline): - """ A simple y=0 integration on the provided data window. """ + """A simple y=0 integration on the provided data window.""" name = "Integral from 0" InheritEq = True @@ -184,16 +203,17 @@ def compute_baseline(self, x_s, y_s): class IntegrateFeaturePeakEdgeBaseline(IntegrateFeature): - """ The maximum baseline-subtracted peak height in the provided window. """ + """The maximum baseline-subtracted peak height in the provided window.""" name = "Peak from baseline" InheritEq = True @staticmethod def parameters(): - return (("Low limit", "Low limit for integration (inclusive)"), - ("High limit", "High limit for integration (inclusive)"), - ) + return ( + ("Low limit", "Low limit for integration (inclusive)"), + ("High limit", "High limit for integration (inclusive)"), + ) def compute_baseline(self, x, y): return edge_baseline(x, y) @@ -206,15 +226,23 @@ def compute_integral(self, x_s, y_s): def compute_draw_info(self, x, ys): bs = self.compute_baseline(x, ys) - im = bottleneck.nanargmax(ys-bs, axis=1) - lines = (x[im], bs[np.arange(bs.shape[0]), im]), (x[im], ys[np.arange(ys.shape[0]), im]) - return [("curve", (x, self.compute_baseline(x, ys), INTEGRATE_DRAW_BASELINE_PENARGS)), - ("curve", (x, ys, INTEGRATE_DRAW_BASELINE_PENARGS)), - ("line", lines)] + im = bottleneck.nanargmax(ys - bs, axis=1) + lines = ( + (x[im], bs[np.arange(bs.shape[0]), im]), + (x[im], ys[np.arange(ys.shape[0]), im]), + ) + return [ + ( + "curve", + (x, self.compute_baseline(x, ys), INTEGRATE_DRAW_BASELINE_PENARGS), + ), + ("curve", (x, ys, INTEGRATE_DRAW_BASELINE_PENARGS)), + ("line", lines), + ] class IntegrateFeaturePeakSimple(IntegrateFeaturePeakEdgeBaseline): - """ The maximum peak height in the provided data window. """ + """The maximum peak height in the provided data window.""" name = "Peak from 0" InheritEq = True @@ -224,16 +252,17 @@ def compute_baseline(self, x_s, y_s): class IntegrateFeaturePeakXEdgeBaseline(IntegrateFeature): - """ The X-value of the maximum baseline-subtracted peak height in the provided window. """ + """The X-value of the maximum baseline-subtracted peak height in the provided window.""" name = "X-value of maximum from baseline" InheritEq = True @staticmethod def parameters(): - return (("Low limit", "Low limit for integration (inclusive)"), - ("High limit", "High limit for integration (inclusive)"), - ) + return ( + ("Low limit", "Low limit for integration (inclusive)"), + ("High limit", "High limit for integration (inclusive)"), + ) def compute_baseline(self, x, y): return edge_baseline(x, y) @@ -253,15 +282,23 @@ def compute_integral(self, x_s, y_s): def compute_draw_info(self, x, ys): bs = self.compute_baseline(x, ys) - im = bottleneck.nanargmax(ys-bs, axis=1) - lines = (x[im], bs[np.arange(bs.shape[0]), im]), (x[im], ys[np.arange(ys.shape[0]), im]) - return [("curve", (x, self.compute_baseline(x, ys), INTEGRATE_DRAW_BASELINE_PENARGS)), - ("curve", (x, ys, INTEGRATE_DRAW_BASELINE_PENARGS)), - ("line", lines)] + im = bottleneck.nanargmax(ys - bs, axis=1) + lines = ( + (x[im], bs[np.arange(bs.shape[0]), im]), + (x[im], ys[np.arange(ys.shape[0]), im]), + ) + return [ + ( + "curve", + (x, self.compute_baseline(x, ys), INTEGRATE_DRAW_BASELINE_PENARGS), + ), + ("curve", (x, ys, INTEGRATE_DRAW_BASELINE_PENARGS)), + ("line", lines), + ] class IntegrateFeaturePeakXSimple(IntegrateFeaturePeakXEdgeBaseline): - """ The X-value of the maximum peak height in the provided data window. """ + """The X-value of the maximum peak height in the provided data window.""" name = "X-value of maximum from 0" InheritEq = True @@ -271,15 +308,14 @@ def compute_baseline(self, x_s, y_s): class IntegrateFeatureAtPeak(IntegrateFeature): - """ Find the closest x and return the value there. """ + """Find the closest x and return the value there.""" name = "Closest value" InheritEq = True @staticmethod def parameters(): - return (("Closest to", "Nearest value"), - ) + return (("Closest to", "Nearest value"),) def extract_data(self, data, common): data, x, x_sorter = common @@ -299,12 +335,13 @@ def compute_draw_info(self, x, ys): im = np.array([bottleneck.nanargmin(abs(x - self.limits[0]))]) dx = [self.limits[0], self.limits[0]] dys = np.hstack((bs[:, im], ys[:, im])) - return [("curve", (dx, dys, INTEGRATE_DRAW_EDGE_PENARGS)), # line to value - ("dot", (x[im], ys[:, im]))] + return [ + ("curve", (dx, dys, INTEGRATE_DRAW_EDGE_PENARGS)), # line to value + ("dot", (x[im], ys[:, im])), + ] class _IntegrateCommon(CommonDomain): - def transformed(self, data): x = getx(data) x_sorter = np.argsort(x) @@ -320,20 +357,30 @@ def __hash__(self): class Integrate(Preprocess): - - INTEGRALS = [IntegrateFeatureSimple, - IntegrateFeatureEdgeBaseline, - IntegrateFeaturePeakSimple, - IntegrateFeaturePeakEdgeBaseline, - IntegrateFeatureAtPeak, - IntegrateFeaturePeakXSimple, - IntegrateFeaturePeakXEdgeBaseline, - IntegrateFeatureSeparateBaseline, - IntegrateFeatureEdgeBaselineAbsolute, - ] + INTEGRALS = [ + IntegrateFeatureSimple, + IntegrateFeatureEdgeBaseline, + IntegrateFeaturePeakSimple, + IntegrateFeaturePeakEdgeBaseline, + IntegrateFeatureAtPeak, + IntegrateFeaturePeakXSimple, + IntegrateFeaturePeakXEdgeBaseline, + IntegrateFeatureSeparateBaseline, + IntegrateFeatureEdgeBaselineAbsolute, + ] # Integration methods - Simple, Baseline, PeakMax, PeakBaseline, PeakAt, PeakX, PeakXBaseline, Separate, BaselineAbsolute = INTEGRALS + ( + Simple, + Baseline, + PeakMax, + PeakBaseline, + PeakAt, + PeakX, + PeakXBaseline, + Separate, + BaselineAbsolute, + ) = INTEGRALS def __init__(self, methods=Baseline, limits=None, names=None, metas=False): self.methods = methods @@ -351,7 +398,7 @@ def __call__(self, data): names = self.names if not names: names = [] - for l, m in zip(self.limits, methods): + for l, m in zip(self.limits, methods, strict=False): if m in [IntegrateFeatureSeparateBaseline]: names.append("{0} - {1} [baseline {2} - {3}]".format(*l)) else: @@ -362,14 +409,20 @@ def __call__(self, data): n = get_unique_names(used_names, n) names[i] = n used_names.append(n) - for limits, method, name in zip(self.limits, methods, names): - atts.append(Orange.data.ContinuousVariable( - name=name, - compute_value=method(limits, common))) + for limits, method, name in zip(self.limits, methods, names, strict=False): + atts.append( + Orange.data.ContinuousVariable( + name=name, compute_value=method(limits, common) + ) + ) if not self.metas: - domain = Orange.data.Domain(atts, data.domain.class_vars, - metas=data.domain.metas) + domain = Orange.data.Domain( + atts, data.domain.class_vars, metas=data.domain.metas + ) else: - domain = Orange.data.Domain(data.domain.attributes, data.domain.class_vars, - metas=data.domain.metas + tuple(atts)) + domain = Orange.data.Domain( + data.domain.attributes, + data.domain.class_vars, + metas=data.domain.metas + tuple(atts), + ) return data.from_table(domain, data) diff --git a/orangecontrib/spectroscopy/preprocess/me_emsc.py b/orangecontrib/spectroscopy/preprocess/me_emsc.py index f2fd4bf6e..73f20f3f4 100644 --- a/orangecontrib/spectroscopy/preprocess/me_emsc.py +++ b/orangecontrib/spectroscopy/preprocess/me_emsc.py @@ -8,12 +8,21 @@ from Orange.data.util import get_unique_names from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.preprocess.utils import SelectColumn, CommonDomainOrderUnknowns, \ - interpolate_extend_to, CommonDomainRef, table_eq_x, subset_for_hash -from orangecontrib.spectroscopy.preprocess.emsc import weighted_wavenumbers, average_table_x - - -def calculate_complex_n(ref_X,wavenumbers): +from orangecontrib.spectroscopy.preprocess.utils import ( + SelectColumn, + CommonDomainOrderUnknowns, + interpolate_extend_to, + CommonDomainRef, + table_eq_x, + subset_for_hash, +) +from orangecontrib.spectroscopy.preprocess.emsc import ( + weighted_wavenumbers, + average_table_x, +) + + +def calculate_complex_n(ref_X, wavenumbers): """Calculates the scaled imaginary part and scaled fluctuating real part of the refractive index.""" npr = ref_X nprs = npr / (wavenumbers * 100) @@ -21,14 +30,18 @@ def calculate_complex_n(ref_X,wavenumbers): # Extend absorbance spectrum dw = wavenumbers[1] - wavenumbers[0] wavenumbers_extended = np.hstack( - (dw * np.linspace(1, 200, 200) + (wavenumbers[0] - dw * 201), wavenumbers, - dw * np.linspace(1, 200, 200) + (wavenumbers[-1]))) + ( + dw * np.linspace(1, 200, 200) + (wavenumbers[0] - dw * 201), + wavenumbers, + dw * np.linspace(1, 200, 200) + (wavenumbers[-1]), + ) + ) extension1 = npr[0] * np.ones(200) extension2 = npr[-1] * np.ones(200) npr_extended = np.hstack((extension1, npr, extension2)) # Calculate Hilbert transform - nkks_extended = (-hilbert(npr_extended / (wavenumbers_extended * 100)).imag) + nkks_extended = -hilbert(npr_extended / (wavenumbers_extended * 100)).imag # Cut extended spectrum nkks = nkks_extended[200:-200] @@ -39,42 +52,65 @@ def calculate_Qext_curves(nprs, nkks, alpha0, gamma, wavenumbers): def mie_hulst_extinction(rho, tanbeta): beta = np.arctan(tanbeta) cosbeta = np.cos(beta) - return (2 - 4 * np.e ** (-rho * tanbeta) * (cosbeta / rho) * np.sin(rho - beta) - - 4 * np.e ** (-rho * tanbeta) * (cosbeta / rho) ** 2 * np.cos(rho - 2 * beta) + - 4 * (cosbeta / rho) ** 2 * np.cos(2 * beta)) + return ( + 2 + - 4 * np.e ** (-rho * tanbeta) * (cosbeta / rho) * np.sin(rho - beta) + - 4 + * np.e ** (-rho * tanbeta) + * (cosbeta / rho) ** 2 + * np.cos(rho - 2 * beta) + + 4 * (cosbeta / rho) ** 2 * np.cos(2 * beta) + ) rho = np.array( - [alpha0val * (1 + gammaval * nkks) * (wavenumbers * 100) for alpha0val in alpha0 for gammaval in gamma]) + [ + alpha0val * (1 + gammaval * nkks) * (wavenumbers * 100) + for alpha0val in alpha0 + for gammaval in gamma + ] + ) tanbeta = [nprs / (1 / gammaval + nkks) for _ in alpha0 for gammaval in gamma] - Qext = np.array([mie_hulst_extinction(rhoval, tanbetaval) for rhoval, tanbetaval in zip(rho, tanbeta)]) + Qext = np.array( + [ + mie_hulst_extinction(rhoval, tanbetaval) + for rhoval, tanbetaval in zip(rho, tanbeta) # noqa: B905 + ] + ) return Qext def orthogonalize_Qext(Qext, reference): m = np.dot(reference, reference) norm = np.sqrt(m) - rnorm = reference/norm + rnorm = reference / norm s = np.dot(Qext, rnorm) - Qext_orthogonalized = Qext - s[:, np.newaxis]*rnorm[np.newaxis, :] + Qext_orthogonalized = Qext - s[:, np.newaxis] * rnorm[np.newaxis, :] return Qext_orthogonalized def compress_Mie_curves(Qext_orthogonalized, numComp): - svd = TruncatedSVD(n_components=numComp, n_iter=7, random_state=42) # Self.ncomp needs to be specified + svd = TruncatedSVD( + n_components=numComp, n_iter=7, random_state=42 + ) # Self.ncomp needs to be specified svd.fit(Qext_orthogonalized) badspectra = svd.components_[0:numComp, :] return badspectra -def cal_ncomp(reference, wavenumbers, explainedVarLim, alpha0, gamma): +def cal_ncomp(reference, wavenumbers, explainedVarLim, alpha0, gamma): nprs, nkks = calculate_complex_n(reference, wavenumbers) Qext = calculate_Qext_curves(nprs, nkks, alpha0, gamma, wavenumbers) Qext_orthogonalized = orthogonalize_Qext(Qext, reference) - maxNcomp = reference.shape[0]-1 + maxNcomp = reference.shape[0] - 1 svd = TruncatedSVD(n_components=min(maxNcomp, 30), n_iter=7, random_state=42) svd.fit(Qext_orthogonalized) - lda = np.array([(sing_val**2)/(Qext_orthogonalized.shape[0]-1) for sing_val in svd.singular_values_]) - explainedVariance = 100*lda/np.sum(lda) + lda = np.array( + [ + (sing_val**2) / (Qext_orthogonalized.shape[0] - 1) + for sing_val in svd.singular_values_ + ] + ) + explainedVariance = 100 * lda / np.sum(lda) explainedVariance = np.cumsum(explainedVariance) numComp = np.argmax(explainedVariance > explainedVarLim) + 1 return numComp @@ -89,8 +125,18 @@ class ME_EMSCModel(SelectColumn): class _ME_EMSC(CommonDomainOrderUnknowns, CommonDomainRef): - - def __init__(self, reference, weights, ncomp, alpha0, gamma, maxNiter, fixedNiter, positiveRef, domain): + def __init__( + self, + reference, + weights, + ncomp, + alpha0, + gamma, + maxNiter, + fixedNiter, + positiveRef, + domain, + ): CommonDomainOrderUnknowns.__init__(self, domain) CommonDomainRef.__init__(self, reference, domain) assert len(reference) == 1 @@ -108,7 +154,7 @@ def transformed(self, X, wavenumbers): def make_basic_emsc_mod(ref_X): N = wavenumbers.shape[0] - m0 = - 2.0 / (wavenumbers[0] - wavenumbers[N - 1]) + m0 = -2.0 / (wavenumbers[0] - wavenumbers[N - 1]) c_coeff = 0.5 * (wavenumbers[0] + wavenumbers[N - 1]) M_basic = [] for x in range(0, 3): @@ -121,15 +167,15 @@ def cal_emsc_basic(M_basic, spectrum): m = np.linalg.lstsq(M_basic, spectrum, rcond=-1)[0] corrected = spectrum for x in range(0, 3): - corrected = (corrected - (m[x] * M_basic[:, x])) + corrected = corrected - (m[x] * M_basic[:, x]) corrected = corrected / m[3] scaled_spectrum = corrected return scaled_spectrum def make_emsc_model(badspectra, referenceSpec): - M = np.ones([len(wavenumbers), self.ncomp+2]) - M[:, 1:self.ncomp+1] = np.array([spectrum for spectrum in badspectra.T]) - M[:, self.ncomp+1] = referenceSpec + M = np.ones([len(wavenumbers), self.ncomp + 2]) + M[:, 1 : self.ncomp + 1] = np.array([spectrum for spectrum in badspectra.T]) + M[:, self.ncomp + 1] = referenceSpec return M def cal_emsc(M, X): @@ -138,13 +184,15 @@ def cal_emsc(M, X): m = np.linalg.lstsq(M, rawspectrum, rcond=-1)[0] corrected = rawspectrum for x in range(0, 1 + self.ncomp): - corrected = (corrected - (m[x] * M[:, x])) + corrected = corrected - (m[x] * M[:, x]) corrected = corrected / m[1 + self.ncomp] - corrected[np.isinf(corrected)] = np.nan # fix values caused by zero weights + corrected[np.isinf(corrected)] = ( + np.nan + ) # fix values caused by zero weights corrected = np.hstack((corrected, m)) # append the model parameters correctedspectra[i] = corrected - params = correctedspectra[:, -(self.ncomp+2):] + params = correctedspectra[:, -(self.ncomp + 2) :] res = X - np.dot(params, M.T) # Have to check if this is correct FIXME return correctedspectra, res @@ -158,7 +206,7 @@ def iteration_step(spectrum, reference, wavenumbers, M_basic, alpha0, gamma): raise np.linalg.LinAlgError() # Apply weights - reference = reference*wei_X + reference = reference * wei_X reference = reference[0] # set negative parts to zero @@ -183,26 +231,51 @@ def iteration_step(spectrum, reference, wavenumbers, M_basic, alpha0, gamma): return newspectrum, res - def iterate(spectra, correctedFirsIteration, residualsFirstIteration, wavenumbers, M_basic, alpha0, gamma): + def iterate( + spectra, + correctedFirsIteration, + residualsFirstIteration, + wavenumbers, + M_basic, + alpha0, + gamma, + ): newspectra = np.full(correctedFirsIteration.shape, np.nan) numberOfIterations = np.full(spectra.shape[0], np.nan) residuals = np.full(spectra.shape, np.nan) RMSEall = np.full([spectra.shape[0]], np.nan) for i in range(correctedFirsIteration.shape[0]): corrSpec = correctedFirsIteration[i] - rawSpec = spectra[i,:] - rawSpec = rawSpec.reshape(1,-1) - RMSE = [round(np.sqrt((1/len(residualsFirstIteration[i,:]))*np.sum(residualsFirstIteration[i,:]**2)),4)] - for iterationNumber in range(2, self.maxNiter+1): + rawSpec = spectra[i, :] + rawSpec = rawSpec.reshape(1, -1) + RMSE = [ + round( + np.sqrt( + (1 / len(residualsFirstIteration[i, :])) + * np.sum(residualsFirstIteration[i, :] ** 2) + ), + 4, + ) + ] + for iterationNumber in range(2, self.maxNiter + 1): try: - newSpec, res = iteration_step(rawSpec, corrSpec[:-self.ncomp-2], wavenumbers, M_basic, alpha0, gamma) + newSpec, res = iteration_step( + rawSpec, + corrSpec[: -self.ncomp - 2], + wavenumbers, + M_basic, + alpha0, + gamma, + ) except np.linalg.LinAlgError: - newspectra[i, :] = np.full([rawSpec.shape[1] + self.ncomp + 2], np.nan) + newspectra[i, :] = np.full( + [rawSpec.shape[1] + self.ncomp + 2], np.nan + ) residuals[i, :] = np.full(rawSpec.shape, np.nan) RMSEall[i] = np.nan break - corrSpec = newSpec[0,:] - rmse = round(np.sqrt((1/len(res[0,:]))*np.sum(res**2)),4) + corrSpec = newSpec[0, :] + rmse = round(np.sqrt((1 / len(res[0, :])) * np.sum(res**2)), 4) RMSE.append(rmse) # Stop criterion if iterationNumber == self.maxNiter: @@ -213,13 +286,16 @@ def iterate(spectra, correctedFirsIteration, residualsFirstIteration, wavenumber break elif self.fixedNiter and iterationNumber < self.fixedNiter: continue - elif iterationNumber == self.maxNiter or iterationNumber == self.fixedNiter: + elif ( + iterationNumber == self.maxNiter + or iterationNumber == self.fixedNiter + ): newspectra[i, :] = corrSpec numberOfIterations[i] = iterationNumber residuals[i, :] = res RMSEall[i] = RMSE[-1] break - elif iterationNumber > 2 and self.fixedNiter == False: + elif iterationNumber > 2 and not self.fixedNiter: if (rmse == RMSE[-2] and rmse == RMSE[-3]) or rmse > RMSE[-2]: newspectra[i, :] = corrSpec numberOfIterations[i] = iterationNumber @@ -233,7 +309,7 @@ def iterate(spectra, correctedFirsIteration, residualsFirstIteration, wavenumber wei_X = weighted_wavenumbers(self.weights, wavenumbers) - ref_X = ref_X*wei_X + ref_X = ref_X * wei_X ref_X = ref_X[0] nonzeroReference = ref_X @@ -252,7 +328,9 @@ def iterate(spectra, correctedFirsIteration, residualsFirstIteration, wavenumber nkks = np.zeros(len(wavenumbers)) # For the first iteration, make basic EMSC model - M_basic = make_basic_emsc_mod(ref_X) # Consider to make the M_basic in the init since this one does not change. + M_basic = make_basic_emsc_mod( + ref_X + ) # Consider to make the M_basic in the init since this one does not change. # Calculate scattering curves for ME-EMSC Qext = calculate_Qext_curves(nprs, nkks, self.alpha0, self.gamma, wavenumbers) @@ -265,35 +343,65 @@ def iterate(spectra, correctedFirsIteration, residualsFirstIteration, wavenumber # Correcting all spectra at once for the first iteration newspectra, res = cal_emsc(M, X) - if self.fixedNiter==1 or self.maxNiter==1: + if self.fixedNiter == 1 or self.maxNiter == 1: res = np.array(res) numberOfIterations = np.ones([1, newspectra.shape[0]]) - RMSEall = [round(np.sqrt((1/res.shape[1])*np.sum(res[specNum, :]**2)), 4) for specNum in range(newspectra.shape[0])] # ADD RESIDUALS!!!!! FIXME - newspectra = np.hstack((newspectra, numberOfIterations.reshape(-1, 1), np.array(RMSEall).reshape(-1,1))) + RMSEall = [ + round(np.sqrt((1 / res.shape[1]) * np.sum(res[specNum, :] ** 2)), 4) + for specNum in range(newspectra.shape[0]) + ] # ADD RESIDUALS!!!!! FIXME + newspectra = np.hstack( + ( + newspectra, + numberOfIterations.reshape(-1, 1), + np.array(RMSEall).reshape(-1, 1), + ) + ) return newspectra # Iterate - newspectra, RMSEall, numberOfIterations = iterate(X, newspectra, res, wavenumbers, M_basic, self.alpha0, self.gamma) - newspectra = np.hstack((newspectra, numberOfIterations.reshape(-1, 1),RMSEall.reshape(-1, 1))) + newspectra, RMSEall, numberOfIterations = iterate( + X, newspectra, res, wavenumbers, M_basic, self.alpha0, self.gamma + ) + newspectra = np.hstack( + (newspectra, numberOfIterations.reshape(-1, 1), RMSEall.reshape(-1, 1)) + ) return newspectra def __eq__(self, other): - return CommonDomainRef.__eq__(self, other) \ - and self.ncomp == other.ncomp \ - and np.array_equal(self.alpha0, other.alpha0) \ - and np.array_equal(self.gamma, other.gamma) \ - and self.maxNiter == other.maxNiter \ - and self.fixedNiter == other.fixedNiter \ - and self.positiveRef == other.positiveRef \ - and (self.weights == other.weights - if not isinstance(self.weights, Table) - else table_eq_x(self.weights, other.weights)) + return ( + CommonDomainRef.__eq__(self, other) + and self.ncomp == other.ncomp + and np.array_equal(self.alpha0, other.alpha0) + and np.array_equal(self.gamma, other.gamma) + and self.maxNiter == other.maxNiter + and self.fixedNiter == other.fixedNiter + and self.positiveRef == other.positiveRef + and ( + self.weights == other.weights + if not isinstance(self.weights, Table) + else table_eq_x(self.weights, other.weights) + ) + ) def __hash__(self): - weights = self.weights if not isinstance(self.weights, Table) \ + weights = ( + self.weights + if not isinstance(self.weights, Table) else subset_for_hash(self.weights.X) - return hash((CommonDomainRef.__hash__(self), weights, self.ncomp, tuple(self.alpha0), - tuple(self.gamma), self.maxNiter, self.fixedNiter, self.positiveRef)) + ) + return hash( + ( + CommonDomainRef.__hash__(self), + weights, + self.ncomp, + tuple(self.alpha0), + tuple(self.gamma), + self.maxNiter, + self.fixedNiter, + self.positiveRef, + ) + ) class MissingReferenceException(Exception): @@ -301,9 +409,20 @@ class MissingReferenceException(Exception): class ME_EMSC(Preprocess): - - def __init__(self, reference=None, weights=None, ncomp=False, n0=np.linspace(1.1, 1.4, 10), a=np.linspace(2, 7.1, 10), h=0.25, - max_iter=30, fixed_iter=False, positive_reference=True, output_model=False, ranges=None): + def __init__( + self, + reference=None, + weights=None, + ncomp=False, + n0=np.linspace(1.1, 1.4, 10), # noqa: B008 + a=np.linspace(2, 7.1, 10), # noqa: B008 + h=0.25, + max_iter=30, + fixed_iter=False, + positive_reference=True, + output_model=False, + ranges=None, + ): # the first non-kwarg can not be a data table (Preprocess limitations) # ranges could be a list like this [[800, 1000], [1300, 1500]] if reference is None: @@ -325,54 +444,84 @@ def __init__(self, reference=None, weights=None, ncomp=False, n0=np.linspace(1.1 self.h = h self.alpha0 = (4 * np.pi * self.a * (self.n0 - 1)) * 1e-6 - self.gamma = self.h * np.log(10) / (4 * np.pi * 0.5 * np.pi * (self.n0 - 1) * self.a * 1e-6) + self.gamma = ( + self.h + * np.log(10) + / (4 * np.pi * 0.5 * np.pi * (self.n0 - 1) * self.a * 1e-6) + ) if not self.ncomp: wavenumbers_ref = np.array(sorted(getx(self.reference))) ref_X = interpolate_extend_to(self.reference, wavenumbers_ref)[0] - self.ncomp = cal_ncomp(ref_X, wavenumbers_ref, explainedVariance, self.alpha0, self.gamma) + self.ncomp = cal_ncomp( + ref_X, wavenumbers_ref, explainedVariance, self.alpha0, self.gamma + ) else: self.explainedVariance = False def __call__(self, data): # creates function for transforming data - common = _ME_EMSC(reference=self.reference, weights=self.weights, ncomp=self.ncomp, alpha0=self.alpha0, - gamma=self.gamma, maxNiter=self.maxNiter, fixedNiter=self.fixedNiter, positiveRef=self.positiveRef, domain=data.domain) + common = _ME_EMSC( + reference=self.reference, + weights=self.weights, + ncomp=self.ncomp, + alpha0=self.alpha0, + gamma=self.gamma, + maxNiter=self.maxNiter, + fixedNiter=self.fixedNiter, + positiveRef=self.positiveRef, + domain=data.domain, + ) # takes care of domain column-wise, by above transformation function - atts = [a.copy(compute_value=ME_EMSCFeature(i, common)) - for i, a in enumerate(data.domain.attributes)] + atts = [ + a.copy(compute_value=ME_EMSCFeature(i, common)) + for i, a in enumerate(data.domain.attributes) + ] model_metas = [] n_badspec = self.ncomp # Check if function knows about bad spectra - used_names = set([var.name for var in data.domain.variables + data.domain.metas]) + used_names = set( + [var.name for var in data.domain.variables + data.domain.metas] + ) if self.output_model: i = len(data.domain.attributes) for o in range(1): n = get_unique_names(used_names, "EMSC parameter " + str(o)) model_metas.append( - Orange.data.ContinuousVariable(name=n, - compute_value=ME_EMSCModel(i, common))) + Orange.data.ContinuousVariable( + name=n, compute_value=ME_EMSCModel(i, common) + ) + ) i += 1 for o in range(n_badspec): n = get_unique_names(used_names, "EMSC parameter bad spec " + str(o)) model_metas.append( - Orange.data.ContinuousVariable(name=n, - compute_value=ME_EMSCModel(i, common))) + Orange.data.ContinuousVariable( + name=n, compute_value=ME_EMSCModel(i, common) + ) + ) i += 1 n = get_unique_names(used_names, "EMSC scaling parameter") model_metas.append( - Orange.data.ContinuousVariable(name=n, - compute_value=ME_EMSCModel(i, common))) + Orange.data.ContinuousVariable( + name=n, compute_value=ME_EMSCModel(i, common) + ) + ) i += 1 n = get_unique_names(used_names, "Number of iterations") model_metas.append( - Orange.data.ContinuousVariable(name=n, - compute_value=ME_EMSCModel(i, common))) + Orange.data.ContinuousVariable( + name=n, compute_value=ME_EMSCModel(i, common) + ) + ) i += 1 n = get_unique_names(used_names, "RMSE") model_metas.append( - Orange.data.ContinuousVariable(name=n, - compute_value=ME_EMSCModel(i, common))) - domain = Orange.data.Domain(atts, data.domain.class_vars, - data.domain.metas + tuple(model_metas)) + Orange.data.ContinuousVariable( + name=n, compute_value=ME_EMSCModel(i, common) + ) + ) + domain = Orange.data.Domain( + atts, data.domain.class_vars, data.domain.metas + tuple(model_metas) + ) return data.from_table(domain, data) diff --git a/orangecontrib/spectroscopy/preprocess/npfunc.py b/orangecontrib/spectroscopy/preprocess/npfunc.py index 3bf29cbac..73473cf72 100644 --- a/orangecontrib/spectroscopy/preprocess/npfunc.py +++ b/orangecontrib/spectroscopy/preprocess/npfunc.py @@ -2,7 +2,6 @@ class Function: - def __init__(self, fn): self.fn = fn @@ -10,33 +9,29 @@ def __call__(self, x): return self.fn(x) def __eq__(self, other): - return type(self) is type(other) \ - and self.fn == other.fn + return type(self) is type(other) and self.fn == other.fn def __hash__(self): return hash((type(self), self.fn)) class Constant(Function): - def __init__(self, c): super().__init__(None) self.c = c def __call__(self, x): x = np.asarray(x) - return np.ones(x.shape)*self.c + return np.ones(x.shape) * self.c def __eq__(self, other): - return super().__eq__(other) \ - and self.c == other.c + return super().__eq__(other) and self.c == other.c def __hash__(self): return hash((super().__hash__(), self.c)) class Identity(Function): - def __init__(self): super().__init__(None) @@ -65,15 +60,13 @@ def __call__(self, x): return output def __eq__(self, other): - return super().__eq__(other) \ - and self.segments == other.segments + return super().__eq__(other) and self.segments == other.segments def __hash__(self): return hash((super().__hash__(), self.segments)) class Sum(Function): - def __init__(self, *elements): super().__init__(None) self.elements = elements @@ -89,8 +82,7 @@ def __call__(self, x): return acc def __eq__(self, other): - return super().__eq__(other) \ - and self.segments == other.elements + return super().__eq__(other) and self.segments == other.elements def __hash__(self): return hash((super().__hash__(), self.elements)) diff --git a/orangecontrib/spectroscopy/preprocess/transform.py b/orangecontrib/spectroscopy/preprocess/transform.py index 89ba8c212..a0eaed2cc 100644 --- a/orangecontrib/spectroscopy/preprocess/transform.py +++ b/orangecontrib/spectroscopy/preprocess/transform.py @@ -5,14 +5,19 @@ from Orange.preprocess.preprocess import Preprocess from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.preprocess.utils import SelectColumn, CommonDomainRef,\ - WrongReferenceException, replace_infs +from orangecontrib.spectroscopy.preprocess.utils import ( + SelectColumn, + CommonDomainRef, + WrongReferenceException, + replace_infs, +) class SpecTypes(Enum): """ Spectral types possibly supported by spectral transforms """ + ABSORBANCE = "Absorbance" TRANSMITTANCE = "Transmittance" SINGLECHANNEL = "Single Channel" @@ -23,7 +28,6 @@ class AbsorbanceFeature(SelectColumn): class _AbsorbanceCommon(CommonDomainRef): - def transformed(self, data): if self.reference is not None: # Calculate from single-channel data @@ -50,7 +54,6 @@ def __hash__(self): class TransformOptionalReference(Preprocess): - def __init__(self, reference=None): if reference is not None and len(reference) != 1: raise WrongReferenceException("Reference data should have length 1") @@ -58,11 +61,13 @@ def __init__(self, reference=None): def __call__(self, data): common = self._cl_common(self.reference, data.domain) - newattrs = [Orange.data.ContinuousVariable( - name=var.name, compute_value=self._cl_feature(i, common)) - for i, var in enumerate(data.domain.attributes)] - domain = Orange.data.Domain( - newattrs, data.domain.class_vars, data.domain.metas) + newattrs = [ + Orange.data.ContinuousVariable( + name=var.name, compute_value=self._cl_feature(i, common) + ) + for i, var in enumerate(data.domain.attributes) + ] + domain = Orange.data.Domain(newattrs, data.domain.class_vars, data.domain.metas) return data.from_table(domain, data) @@ -88,7 +93,6 @@ class TransmittanceFeature(SelectColumn): class _TransmittanceCommon(CommonDomainRef): - def transformed(self, data): if self.reference is not None: # Calculate from single-channel data diff --git a/orangecontrib/spectroscopy/preprocess/utils.py b/orangecontrib/spectroscopy/preprocess/utils.py index 14d4ec02e..49dc5b4f4 100644 --- a/orangecontrib/spectroscopy/preprocess/utils.py +++ b/orangecontrib/spectroscopy/preprocess/utils.py @@ -30,7 +30,6 @@ def full_like_type(orig, shape, val): class PreprocessException(Exception): - def message(self): if self.args: return self.args[0] @@ -47,7 +46,6 @@ class WrongReferenceException(PreprocessException): class SelectColumn(SharedComputeValue): - def __init__(self, feature, commonfn): super().__init__(commonfn) self.feature = feature @@ -56,8 +54,7 @@ def compute(self, data, common): return common[:, self.feature] def __eq__(self, other): - return super().__eq__(other) \ - and self.feature == other.feature + return super().__eq__(other) and self.feature == other.feature def __hash__(self): return hash((super().__hash__(), self.feature)) @@ -68,6 +65,7 @@ class CommonDomain: SharedComputeValue features. It does the domain transformation (input domain needs to be the same as it was with training data). """ + def __init__(self, domain: Domain): self.domain = domain @@ -84,8 +82,7 @@ def transformed(self, data): raise NotImplementedError def __eq__(self, other): - return type(self) is type(other) \ - and self.domain == other.domain + return type(self) is type(other) and self.domain == other.domain def __hash__(self): return hash((type(self), self.domain)) @@ -93,6 +90,7 @@ def __hash__(self): class CommonDomainRef(CommonDomain): """CommonDomain which also ensures reference domain transformation""" + def __init__(self, reference: Table, domain: Domain): super().__init__(domain) self.reference = reference @@ -101,8 +99,7 @@ def interpolate_extend_to(self, interpolate: Table, wavenumbers): return interpolate_extend_to(interpolate, wavenumbers) def __eq__(self, other): - return super().__eq__(other) \ - and table_eq_x(self.reference, other.reference) + return super().__eq__(other) and table_eq_x(self.reference, other.reference) def __hash__(self): domain = self.reference.domain if self.reference is not None else None @@ -111,8 +108,8 @@ def __hash__(self): class CommonDomainOrder(CommonDomain): - """CommonDomain + it also handles wavenumber order. - """ + """CommonDomain + it also handles wavenumber order.""" + def __call__(self, data): data = self.transform_domain(data) @@ -147,6 +144,7 @@ class CommonDomainOrderUnknowns(CommonDomainOrder): """CommonDomainOrder + it also handles unknown values: it interpolates values before computation and afterwards sets them back to unknown. """ + def __call__(self, data): data = self.transform_domain(data) @@ -161,8 +159,10 @@ def __call__(self, data): # with some values so that the function does not crash. # Results are going to be discarded later. remaining_nans = np.isnan(X) - if np.any(remaining_nans): # if there were no nans X is a view, so do not modify - X[remaining_nans] = 1. + if np.any( + remaining_nans + ): # if there were no nans X is a view, so do not modify + X[remaining_nans] = 1.0 # do the transformation X = self.transformed(X, xs[xsind]) @@ -193,8 +193,9 @@ def table_eq_x(first: Optional[Table], second: Optional[Table]): elif first is None or second is None: return False else: - return first.domain.attributes == second.domain.attributes \ - and np.array_equal(first.X, second.X) + return first.domain.attributes == second.domain.attributes and np.array_equal( + first.X, second.X + ) def subset_for_hash(array, size=10): @@ -246,7 +247,7 @@ def fill_edges_1d(l): return l else: l[:fi] = l[fi] - l[li + 1:] = l[li] + l[li + 1 :] = l[li] return l @@ -298,8 +299,15 @@ def interp1d_with_unknowns_scipy(x, ys, points, kind="linear"): xt = x[~nan] yt = y[~nan] if len(xt): # check if all values are removed - out[i] = interp1d(xt, yt, fill_value=np.nan, assume_sorted=True, - bounds_error=False, kind=kind, copy=False)(points) + out[i] = interp1d( + xt, + yt, + fill_value=np.nan, + assume_sorted=True, + bounds_error=False, + kind=kind, + copy=False, + )(points) return out @@ -316,11 +324,13 @@ def linear_baseline(x, y, zero_points=None): if len(x) == 0: return 0 values_zero_points = interp1d(x, y, axis=1, fill_value="extrapolate")(zero_points) - return interp1d(zero_points, values_zero_points, axis=1, fill_value="extrapolate")(x) + return interp1d(zero_points, values_zero_points, axis=1, fill_value="extrapolate")( + x + ) def replace_infs(array): - """ Replaces inf and -inf with nan. + """Replaces inf and -inf with nan. This should be used anywhere a divide-by-zero can happen (/, np.log10, etc)""" array[np.isinf(array)] = np.nan return array @@ -328,7 +338,10 @@ def replace_infs(array): def replacex(data: Table, replacement: list): assert len(data.domain.attributes) == len(replacement) - natts = [at.renamed(str(n)) for n, at in zip(replacement, data.domain.attributes)] + natts = [ + at.renamed(str(n)) + for n, at in zip(replacement, data.domain.attributes, strict=True) + ] ndom = Domain(natts, data.domain.class_vars, data.domain.metas) return data.transform(ndom) diff --git a/orangecontrib/spectroscopy/tests/__init__.py b/orangecontrib/spectroscopy/tests/__init__.py index d9c30c76b..ab628d121 100644 --- a/orangecontrib/spectroscopy/tests/__init__.py +++ b/orangecontrib/spectroscopy/tests/__init__.py @@ -1,6 +1,7 @@ import os import unittest + def suite(loader=None, pattern='test*.py'): test_dir = os.path.dirname(__file__) if loader is None: diff --git a/orangecontrib/spectroscopy/tests/bigdata.py b/orangecontrib/spectroscopy/tests/bigdata.py index 4282bb87a..47169a787 100644 --- a/orangecontrib/spectroscopy/tests/bigdata.py +++ b/orangecontrib/spectroscopy/tests/bigdata.py @@ -3,17 +3,19 @@ import serverfiles from Orange.misc.environ import data_dir -import orangecontrib.spectroscopy # loads file readers +import orangecontrib.spectroscopy # loads file readers # noqa: F401 server = serverfiles.ServerFiles("http://193.2.72.57/infrared-data/") localfiles = serverfiles.LocalFiles( - os.path.join(data_dir(), "orange-infrared"), serverfiles=server) + os.path.join(data_dir(), "orange-infrared"), serverfiles=server +) def spectra20nea(): return localfiles.localpath_download("spectra20.nea") + def dust(): localfiles.localpath_download("20160831_06_Paris_25x_highmag.dat") - return localfiles.localpath_download("20160831_06_Paris_25x_highmag.hdr") \ No newline at end of file + return localfiles.localpath_download("20160831_06_Paris_25x_highmag.hdr") diff --git a/orangecontrib/spectroscopy/tests/example_connect_settings.py b/orangecontrib/spectroscopy/tests/example_connect_settings.py index 2495f50fe..a42fa0576 100644 --- a/orangecontrib/spectroscopy/tests/example_connect_settings.py +++ b/orangecontrib/spectroscopy/tests/example_connect_settings.py @@ -11,13 +11,17 @@ import Orange from Orange.widgets.widget import OWWidget -from orangecontrib.spectroscopy.widgets.gui import connect_settings, lineEditFloatRange, \ - ValueTransform, connect_line, MovableVline +from orangecontrib.spectroscopy.widgets.gui import ( + connect_settings, + lineEditFloatRange, + ValueTransform, + connect_line, + MovableVline, +) from orangecontrib.spectroscopy.widgets.owspectra import CurvePlot class PlusAdd(ValueTransform): - def __init__(self, contains_add): self.contains_add = contains_add @@ -29,7 +33,6 @@ def inverse(self, v): class TestConnected(OWWidget): - name = "Test" want_main_area = True @@ -76,6 +79,7 @@ def refresh(): def main(): from AnyQt.QtWidgets import QApplication + app = QApplication(sys.argv) ow = TestConnected() ow.show() diff --git a/orangecontrib/spectroscopy/tests/spectral_preprocess.py b/orangecontrib/spectroscopy/tests/spectral_preprocess.py index 930de08ee..05bae04a9 100644 --- a/orangecontrib/spectroscopy/tests/spectral_preprocess.py +++ b/orangecontrib/spectroscopy/tests/spectral_preprocess.py @@ -18,7 +18,6 @@ def wait_for_preview(widget, timeout=5000): class WarningEditor(BaseEditorOrange): - class Warning(BaseEditorOrange.Warning): some_warning = Msg("Warn.") @@ -51,7 +50,6 @@ def createinstance(params): class TestWarning(WidgetTest): - widget_cls = None def setUp(self): diff --git a/orangecontrib/spectroscopy/tests/test_als.py b/orangecontrib/spectroscopy/tests/test_als.py index 5d7ba7389..25992b923 100644 --- a/orangecontrib/spectroscopy/tests/test_als.py +++ b/orangecontrib/spectroscopy/tests/test_als.py @@ -4,43 +4,45 @@ from Orange.data import Table from orangecontrib.spectroscopy.preprocess.als import ALSP, ARPLS, AIRPLS -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALLER_COLLAGEN +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALLER_COLLAGEN, +) class TestAls(unittest.TestCase, TestCommonIndpSamplesMixin): - preprocessors = [ - ALSP(lam=100E+6, itermax=5, p=0.5), - ARPLS(lam=100E+5, itermax=5, ratio=0.5), + ALSP(lam=100e6, itermax=5, p=0.5), + ARPLS(lam=100e5, itermax=5, ratio=0.5), AIRPLS(lam=100, itermax=5, porder=1), ] data = SMALLER_COLLAGEN def test_als_Basic(self): - data = Table.from_numpy(None, [[1.0, 2.0, 10.0, 5.0], - [3.0, 5.0, 9.0, 4.0]]) - method = ALSP(lam=100E+6, p=0.5, itermax=100) + data = Table.from_numpy(None, [[1.0, 2.0, 10.0, 5.0], [3.0, 5.0, 9.0, 4.0]]) + method = ALSP(lam=100e6, p=0.5, itermax=100) process = method(data) - check = Table.from_numpy(None, [[-0.5, -1.5, 4.5, -2.5], - [-1.2, 0.1, 3.4, -2.3]]) + check = Table.from_numpy( + None, [[-0.5, -1.5, 4.5, -2.5], [-1.2, 0.1, 3.4, -2.3]] + ) process = np.array(process) np.testing.assert_almost_equal(check, process, 2) def test_arpls_basic(self): - data = Table.from_numpy(None, [[1.0, 2.0, 10.0, 5.0], - [3.0, 5.0, 9.0, 4.0]]) - method = ARPLS(lam=100E+5, ratio=0.5, itermax=100) + data = Table.from_numpy(None, [[1.0, 2.0, 10.0, 5.0], [3.0, 5.0, 9.0, 4.0]]) + method = ARPLS(lam=100e5, ratio=0.5, itermax=100) process = method(data) - check = Table.from_numpy(None, [[-0.5, -1.5, 4.5, -2.5], - [-1.2, 0.0999999, 3.4, -2.3]]) + check = Table.from_numpy( + None, [[-0.5, -1.5, 4.5, -2.5], [-1.2, 0.0999999, 3.4, -2.3]] + ) np.testing.assert_almost_equal(np.array(check), np.array(process)) def test_airpls_basic(self): - data = Table.from_numpy(None, [[1.0, 2.0, 10.0, 5.0], - [3.0, 5.0, 9.0, 4.0]]) + data = Table.from_numpy(None, [[1.0, 2.0, 10.0, 5.0], [3.0, 5.0, 9.0, 4.0]]) method = AIRPLS(lam=100, itermax=10, porder=1) process = method(data) - check = Table.from_numpy(None, [[-1.15248, -0.155994, 7.83538, 2.82675], - [-0.499999, 1.5, 5.5, 0.499999]]) + check = Table.from_numpy( + None, + [[-1.15248, -0.155994, 7.83538, 2.82675], [-0.499999, 1.5, 5.5, 0.499999]], + ) np.testing.assert_almost_equal(np.array(check), np.array(process), 2) diff --git a/orangecontrib/spectroscopy/tests/test_atm_corr.py b/orangecontrib/spectroscopy/tests/test_atm_corr.py index 8fd0fcced..509dbc96a 100755 --- a/orangecontrib/spectroscopy/tests/test_atm_corr.py +++ b/orangecontrib/spectroscopy/tests/test_atm_corr.py @@ -4,39 +4,50 @@ from orangecontrib.spectroscopy.preprocess.atm_corr import AtmCorr from orangecontrib.spectroscopy.tests.util import spectra_table -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALL_COLLAGEN, add_edge_case_data_parameter +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALL_COLLAGEN, + add_edge_case_data_parameter, +) -class TestAtmCorr(unittest.TestCase, TestCommonIndpSamplesMixin): +class TestAtmCorr(unittest.TestCase, TestCommonIndpSamplesMixin): preprocessors = list( - add_edge_case_data_parameter(AtmCorr, "reference", SMALL_COLLAGEN[0:1], - correct_ranges=[(1300, 2100)], smooth_win=5)) + add_edge_case_data_parameter( + AtmCorr, + "reference", + SMALL_COLLAGEN[0:1], + correct_ranges=[(1300, 2100)], + smooth_win=5, + ) + ) data = SMALL_COLLAGEN def test_atm_corr(self): # Fake atmospheric spectrum def atm(wn): - return np.sin(wn*.0373)**2 * \ - ((np.abs(wn-1700)<300) + (np.abs(wn-3630)<150)) + return np.sin(wn * 0.0373) ** 2 * ( + (np.abs(wn - 1700) < 300) + (np.abs(wn - 3630) < 150) + ) + # Make some fake data with different resolution than the atm spectrum wn = np.arange(800, 4000, 3) awn = np.arange(800, 4000, 1) # The CO2 region happens to be nearly straight with this function - sp = np.sin(wn*.005)**2 * (1-((wn-2400)/1600)**2) - data = spectra_table(wn, [sp + .3 * atm(wn)]) + sp = np.sin(wn * 0.005) ** 2 * (1 - ((wn - 2400) / 1600) ** 2) + data = spectra_table(wn, [sp + 0.3 * atm(wn)]) ref = spectra_table(awn, [atm(awn)]) method = AtmCorr(reference=ref, smooth_win=9) process = method(data) - delta = ((data.X[0] - process.X[0])**2).sum() + delta = ((data.X[0] - process.X[0]) ** 2).sum() assert 10 < delta < 11 method = AtmCorr(reference=ref, correct_ranges=[], spline_ranges=[]) process = method(data) - delta = ((data.X[0] - process.X[0])**2).sum() + delta = ((data.X[0] - process.X[0]) ** 2).sum() assert delta == 0 # Test with multiple references ref = spectra_table(awn, 3 * [atm(awn)]) method = AtmCorr(reference=ref, smooth_win=9, mean_reference=False) process = method(data) - delta = ((data.X[0] - process.X[0])**2).sum() + delta = ((data.X[0] - process.X[0]) ** 2).sum() assert 10 < delta < 11 diff --git a/orangecontrib/spectroscopy/tests/test_conversion.py b/orangecontrib/spectroscopy/tests/test_conversion.py index 992b5cd43..77e12802e 100644 --- a/orangecontrib/spectroscopy/tests/test_conversion.py +++ b/orangecontrib/spectroscopy/tests/test_conversion.py @@ -10,8 +10,11 @@ from Orange.evaluation.scoring import AUC from Orange.data.table import DomainTransformationError -from orangecontrib.spectroscopy.preprocess import Interpolate, \ - Cut, SavitzkyGolayFiltering +from orangecontrib.spectroscopy.preprocess import ( + Interpolate, + Cut, + SavitzkyGolayFiltering, +) from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.tests.util import smaller_data @@ -23,15 +26,16 @@ def separate_learn_test(data): - sf = ms.ShuffleSplit(n_splits=1, test_size=0.2, random_state=np.random.RandomState(0)) - (traini, testi), = sf.split(y=data.Y, X=data.X) + sf = ms.ShuffleSplit( + n_splits=1, test_size=0.2, random_state=np.random.RandomState(0) + ) + ((traini, testi),) = sf.split(y=data.Y, X=data.X) return data[traini], data[testi] def slightly_change_wavenumbers(data, change): natts = [ContinuousVariable(float(a.name) + change) for a in data.domain.attributes] - ndomain = Orange.data.Domain(natts, data.domain.class_vars, - metas=data.domain.metas) + ndomain = Orange.data.Domain(natts, data.domain.class_vars, metas=data.domain.metas) ndata = data.transform(ndomain) with ndata.unlocked(): ndata.X = data.X @@ -39,14 +43,12 @@ def slightly_change_wavenumbers(data, change): def odd_attr(data): - natts = [a for i, a in enumerate(data.domain.attributes) if i%2 == 0] - ndomain = Orange.data.Domain(natts, data.domain.class_vars, - metas=data.domain.metas) + natts = [a for i, a in enumerate(data.domain.attributes) if i % 2 == 0] + ndomain = Orange.data.Domain(natts, data.domain.class_vars, metas=data.domain.metas) return data.transform(ndomain) class TestConversion(unittest.TestCase): - @classmethod def setUpClass(cls): cls.collagen = SMALL_COLLAGEN @@ -54,21 +56,25 @@ def setUpClass(cls): def test_predict_same_domain(self): train, test = separate_learn_test(self.collagen) auc = AUC(TestOnTestData()(train, test, [learner])) - self.assertGreater(auc, 0.9) # easy dataset + self.assertGreater(auc, 0.9) # easy dataset def test_predict_different_domain(self): train, test = separate_learn_test(self.collagen) - test = Interpolate(points=getx(test) - 1)(test) # other test domain + test = Interpolate(points=getx(test) - 1)(test) # other test domain with self.assertRaises(DomainTransformationError): learner(train)(test) def test_predict_different_domain_interpolation(self): train, test = separate_learn_test(self.collagen) aucorig = AUC(TestOnTestData()(train, test, [learner])) - test = Interpolate(points=getx(test) - 1.)(test) # other test domain - train = Interpolate(points=getx(train))(train) # make train capable of interpolation + test = Interpolate(points=getx(test) - 1.0)(test) # other test domain + train = Interpolate(points=getx(train))( + train + ) # make train capable of interpolation aucshift = AUC(TestOnTestData()(train, test, [learner])) - self.assertAlmostEqual(aucorig, aucshift, delta=0.01) # shift can decrease AUC slightly + self.assertAlmostEqual( + aucorig, aucshift, delta=0.01 + ) # shift can decrease AUC slightly test = Cut(1000, 1700)(test) auccut1 = AUC(TestOnTestData()(train, test, [learner])) test = Cut(1100, 1600)(test) diff --git a/orangecontrib/spectroscopy/tests/test_cut.py b/orangecontrib/spectroscopy/tests/test_cut.py index 3756cd6fd..2cad88c97 100644 --- a/orangecontrib/spectroscopy/tests/test_cut.py +++ b/orangecontrib/spectroscopy/tests/test_cut.py @@ -3,12 +3,13 @@ import unittest from orangecontrib.spectroscopy.preprocess import Cut from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALL_COLLAGEN +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALL_COLLAGEN, +) class TestCut(unittest.TestCase, TestCommonIndpSamplesMixin): - preprocessors = [Cut(lowlim=1000, highlim=1800)] data = SMALL_COLLAGEN diff --git a/orangecontrib/spectroscopy/tests/test_despike.py b/orangecontrib/spectroscopy/tests/test_despike.py index eefb2ccf2..731f01bfa 100644 --- a/orangecontrib/spectroscopy/tests/test_despike.py +++ b/orangecontrib/spectroscopy/tests/test_despike.py @@ -2,35 +2,102 @@ import numpy as np from Orange.data import Table from orangecontrib.spectroscopy.preprocess import Despike -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALL_COLLAGEN +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALL_COLLAGEN, +) class TestSpikeRemoval(unittest.TestCase, TestCommonIndpSamplesMixin): - preprocessors = [Despike(threshold=5, cutoff=60, dis=5)] data = SMALL_COLLAGEN def test_spikes(self): - data = Table.from_numpy(None, [[1000, 1, 1, 1, 1, 10, 1, 1, 1000, 1000, 1000, 1, 1000, - 1, 1, 1, 1000, 1000, 1000, 1000], - [1000, 1, 1, 1, 1, 10, 1, 1, 1000, 1000, 1000, - 1, 1000, 1, 1, 1, 1000, 1000, 1000, 1000], - [1000, 1000, 2, 1, 1, 10, 1, 2, 1000, 1000, 1000, - 1, 1000, 1, 1, 1, 3, 1000, 1000, 1000]]) + data = Table.from_numpy( + None, + [ + [ + 1000, + 1, + 1, + 1, + 1, + 10, + 1, + 1, + 1000, + 1000, + 1000, + 1, + 1000, + 1, + 1, + 1, + 1000, + 1000, + 1000, + 1000, + ], + [ + 1000, + 1, + 1, + 1, + 1, + 10, + 1, + 1, + 1000, + 1000, + 1000, + 1, + 1000, + 1, + 1, + 1, + 1000, + 1000, + 1000, + 1000, + ], + [ + 1000, + 1000, + 2, + 1, + 1, + 10, + 1, + 2, + 1000, + 1000, + 1000, + 1, + 1000, + 1, + 1, + 1, + 3, + 1000, + 1000, + 1000, + ], + ], + ) method = Despike(threshold=7, cutoff=100, dis=1) process = method(data).X - check = Table.from_numpy(None, ([1, 1, 1, 1, 1, 10, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [1, 1, 1, 1, 1, 10, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - [2, 2, 2, 1, 1, 10, 1, 2, 2, 1.5, - 1, 1, 1, 1, 1, 1, 3, 3, 3, 3])) + check = Table.from_numpy( + None, + ( + [1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + [2, 2, 2, 1, 1, 10, 1, 2, 2, 1.5, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3], + ), + ) np.testing.assert_almost_equal(check, process) def test_nospike(self): - data = Table.from_numpy(None, [[1, 2, 10, 15], - [2, 3, 6, 10]]) + data = Table.from_numpy(None, [[1, 2, 10, 15], [2, 3, 6, 10]]) method = Despike(threshold=7, cutoff=100, dis=5) changed = method(data) check = np.array(data) diff --git a/orangecontrib/spectroscopy/tests/test_editor_als.py b/orangecontrib/spectroscopy/tests/test_editor_als.py index d6a88c989..49c252d76 100644 --- a/orangecontrib/spectroscopy/tests/test_editor_als.py +++ b/orangecontrib/spectroscopy/tests/test_editor_als.py @@ -5,10 +5,13 @@ from orangecontrib.spectroscopy.widgets.preprocessors.als import ALSEditor import unittest -class A(unittest.TestCase): pass -class TestALSEditor(PreprocessorEditorTest): +class A(unittest.TestCase): + pass # noqa: E701 + + +class TestALSEditor(PreprocessorEditorTest): def setUp(self): self.widget = self.create_widget(OWPreprocess) self.editor = self.add_editor(ALSEditor, self.widget) diff --git a/orangecontrib/spectroscopy/tests/test_editor_atm_corr.py b/orangecontrib/spectroscopy/tests/test_editor_atm_corr.py index cfa91a49b..d25aa547a 100755 --- a/orangecontrib/spectroscopy/tests/test_editor_atm_corr.py +++ b/orangecontrib/spectroscopy/tests/test_editor_atm_corr.py @@ -7,7 +7,6 @@ class TestAtmCorrEditor(PreprocessorEditorTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) self.editor = self.add_editor(AtmCorrEditor, self.widget) diff --git a/orangecontrib/spectroscopy/tests/test_editor_baseline.py b/orangecontrib/spectroscopy/tests/test_editor_baseline.py index 51441dcda..30e90555b 100644 --- a/orangecontrib/spectroscopy/tests/test_editor_baseline.py +++ b/orangecontrib/spectroscopy/tests/test_editor_baseline.py @@ -9,7 +9,6 @@ class TestBaselineEditor(PreprocessorEditorTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) self.editor = self.add_editor(BaselineEditor, self.widget) @@ -40,7 +39,7 @@ def test_add_limit(self): # the second addition adds one limit self.editor.range_button.click() p = self.commit_get_preprocessor() - self.assertEqual(p.zero_points, [dmin, dmax, (dmin + dmax)/2]) + self.assertEqual(p.zero_points, [dmin, dmax, (dmin + dmax) / 2]) def test_remove_limit(self): dmin, dmax = min(getx(self.data)), max(getx(self.data)) @@ -50,7 +49,7 @@ def test_remove_limit(self): button = list(layout_widgets(second))[1] button.click() p = self.commit_get_preprocessor() - self.assertEqual(p.zero_points, [dmin, (dmin + dmax)/2]) + self.assertEqual(p.zero_points, [dmin, (dmin + dmax) / 2]) # if there are two entries, remove both second = list(layout_widgets(self.editor.ranges_box))[1] button = list(layout_widgets(second))[1] diff --git a/orangecontrib/spectroscopy/tests/test_editor_cut.py b/orangecontrib/spectroscopy/tests/test_editor_cut.py index d0b857102..958e1da87 100644 --- a/orangecontrib/spectroscopy/tests/test_editor_cut.py +++ b/orangecontrib/spectroscopy/tests/test_editor_cut.py @@ -6,7 +6,6 @@ class TestCutEditor(PreprocessorEditorTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) self.editor = self.add_editor(CutEditor, self.widget) diff --git a/orangecontrib/spectroscopy/tests/test_editor_emsc.py b/orangecontrib/spectroscopy/tests/test_editor_emsc.py index 366747566..607884d41 100644 --- a/orangecontrib/spectroscopy/tests/test_editor_emsc.py +++ b/orangecontrib/spectroscopy/tests/test_editor_emsc.py @@ -9,7 +9,6 @@ class TestEMSCEditor(PreprocessorEditorTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) self.editor = self.add_editor(EMSCEditor, self.widget) # type: EMSCEditor @@ -41,8 +40,11 @@ def test_activate_options(self): def test_migrate_smoothing(self): name = "orangecontrib.spectroscopy.preprocess.emsc" - settings = {"storedsettings": {"preprocessors": [(name, {"ranges": [[0, 1, 2]]})]}} + settings = { + "storedsettings": {"preprocessors": [(name, {"ranges": [[0, 1, 2]]})]} + } OWPreprocess.migrate_settings(settings, 6) self.assertEqual( settings["storedsettings"]["preprocessors"][0], - (name, {"ranges": [[0, 1, 2, 0]]})) + (name, {"ranges": [[0, 1, 2, 0]]}), + ) diff --git a/orangecontrib/spectroscopy/tests/test_editor_gaussian.py b/orangecontrib/spectroscopy/tests/test_editor_gaussian.py index abf7fb0a8..bac0df9c5 100644 --- a/orangecontrib/spectroscopy/tests/test_editor_gaussian.py +++ b/orangecontrib/spectroscopy/tests/test_editor_gaussian.py @@ -2,16 +2,16 @@ from orangecontrib.spectroscopy.tests.test_owpreprocess import PreprocessorEditorTest from orangecontrib.spectroscopy.tests.test_preprocess import SMALL_COLLAGEN -from orangecontrib.spectroscopy.widgets.preprocessors.misc import GaussianSmoothingEditor +from orangecontrib.spectroscopy.widgets.preprocessors.misc import ( + GaussianSmoothingEditor, +) from orangecontrib.spectroscopy.widgets.owpreprocess import OWPreprocess class TestGaussianEditor(PreprocessorEditorTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) - self.editor = self.add_editor(GaussianSmoothingEditor, - self.widget) # type: GaussianSmoothingEditor + self.editor = self.add_editor(GaussianSmoothingEditor, self.widget) # type: GaussianSmoothingEditor data = SMALL_COLLAGEN self.send_signal(self.widget.Inputs.data, data) @@ -29,5 +29,5 @@ def test_zero(self): def test_basic(self): self.editor.sd = 1.5 self.editor.edited.emit() - p = self.commit_get_preprocessor() # type: GaussianSmoothing + p = self.commit_get_preprocessor() # type: GaussianSmoothing self.assertEqual(1.5, p.sd) diff --git a/orangecontrib/spectroscopy/tests/test_editor_me_emsc.py b/orangecontrib/spectroscopy/tests/test_editor_me_emsc.py index 2594d316d..fdb8bc03d 100644 --- a/orangecontrib/spectroscopy/tests/test_editor_me_emsc.py +++ b/orangecontrib/spectroscopy/tests/test_editor_me_emsc.py @@ -9,7 +9,6 @@ class TestMeEMSCEditor(PreprocessorEditorTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) self.editor = self.add_editor(MeEMSCEditor, self.widget) # type: MeEMSCEditor @@ -88,8 +87,11 @@ def test_iterations(self): def test_migrate_smoothing(self): name = "orangecontrib.spectroscopy.preprocess.me_emsc.me_emsc" - settings = {"storedsettings": {"preprocessors": [(name, {"ranges": [[0, 1, 2]]})]}} + settings = { + "storedsettings": {"preprocessors": [(name, {"ranges": [[0, 1, 2]]})]} + } OWPreprocess.migrate_settings(settings, 6) self.assertEqual( settings["storedsettings"]["preprocessors"][0], - (name, {"ranges": [[0, 1, 2, 0]]})) + (name, {"ranges": [[0, 1, 2, 0]]}), + ) diff --git a/orangecontrib/spectroscopy/tests/test_editor_normalize.py b/orangecontrib/spectroscopy/tests/test_editor_normalize.py index 1a3fd8752..ef45069fe 100644 --- a/orangecontrib/spectroscopy/tests/test_editor_normalize.py +++ b/orangecontrib/spectroscopy/tests/test_editor_normalize.py @@ -5,10 +5,12 @@ from orangecontrib.spectroscopy.widgets.owpreprocess import OWPreprocess from orangecontrib.spectroscopy.widgets.preprocessors.normalize import NormalizeEditor -NORMALIZE_REFERENCE_INDEX = NormalizeEditor.Normalizers.index(("Normalize by Reference", 42)) +NORMALIZE_REFERENCE_INDEX = NormalizeEditor.Normalizers.index( + ("Normalize by Reference", 42) +) -class TestNormalizeEditor(PreprocessorEditorTest): +class TestNormalizeEditor(PreprocessorEditorTest): def setUp(self): self.widget = self.create_widget(OWPreprocess) self.editor = self.add_editor(NormalizeEditor, self.widget) diff --git a/orangecontrib/spectroscopy/tests/test_editor_spikeremoval.py b/orangecontrib/spectroscopy/tests/test_editor_spikeremoval.py index e4beb62fe..8929438e4 100644 --- a/orangecontrib/spectroscopy/tests/test_editor_spikeremoval.py +++ b/orangecontrib/spectroscopy/tests/test_editor_spikeremoval.py @@ -1,12 +1,13 @@ from orangecontrib.spectroscopy.tests.test_owpreprocess import PreprocessorEditorTest from orangecontrib.spectroscopy.widgets.owpreprocess import OWPreprocess from orangecontrib.spectroscopy.tests.test_preprocess import SMALL_COLLAGEN -from orangecontrib.spectroscopy.widgets.preprocessors.spikeremoval import SpikeRemovalEditor +from orangecontrib.spectroscopy.widgets.preprocessors.spikeremoval import ( + SpikeRemovalEditor, +) from orangecontrib.spectroscopy.preprocess import Despike class TestSpikeRemovalEditor(PreprocessorEditorTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) self.editor = self.add_editor(SpikeRemovalEditor, self.widget) @@ -39,4 +40,4 @@ def test_none(self): p = self.commit_get_preprocessor() self.assertEqual(p.dis, 6) self.assertEqual(p.cutoff, 100) - self.assertEqual(p.threshold, 7) \ No newline at end of file + self.assertEqual(p.threshold, 7) diff --git a/orangecontrib/spectroscopy/tests/test_emsc.py b/orangecontrib/spectroscopy/tests/test_emsc.py index e8a6fc3cc..8f74c8249 100644 --- a/orangecontrib/spectroscopy/tests/test_emsc.py +++ b/orangecontrib/spectroscopy/tests/test_emsc.py @@ -4,51 +4,56 @@ import Orange from Orange.data import Table -from orangecontrib.spectroscopy.preprocess.emsc import EMSC, MissingReferenceException, \ - SelectionFunction, SmoothedSelectionFunction +from orangecontrib.spectroscopy.preprocess.emsc import ( + EMSC, + MissingReferenceException, + SelectionFunction, + SmoothedSelectionFunction, +) from orangecontrib.spectroscopy.preprocess.npfunc import Sum from orangecontrib.spectroscopy.tests.util import spectra_table -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALL_COLLAGEN, add_edge_case_data_parameter +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALL_COLLAGEN, + add_edge_case_data_parameter, +) class TestEMSC(unittest.TestCase, TestCommonIndpSamplesMixin): - different_reference = list( - add_edge_case_data_parameter(EMSC, "reference", SMALL_COLLAGEN[0:1])) + add_edge_case_data_parameter(EMSC, "reference", SMALL_COLLAGEN[0:1]) + ) different_badspectra = list( - add_edge_case_data_parameter(EMSC, "badspectra", SMALL_COLLAGEN[0:2], - reference=SMALL_COLLAGEN[-1:])) + add_edge_case_data_parameter( + EMSC, "badspectra", SMALL_COLLAGEN[0:2], reference=SMALL_COLLAGEN[-1:] + ) + ) preprocessors = different_reference + different_badspectra data = SMALL_COLLAGEN def test_ab(self): - data = Table.from_numpy(None, [[1.0, 2.0, 1.0, 1.0], - [3.0, 5.0, 3.0, 3.0]]) + data = Table.from_numpy(None, [[1.0, 2.0, 1.0, 1.0], [3.0, 5.0, 3.0, 3.0]]) f = EMSC(reference=data[0:1], order=0, output_model=True) fdata = f(data) - np.testing.assert_almost_equal(fdata.X, - [[1.0, 2.0, 1.0, 1.0], - [1.0, 2.0, 1.0, 1.0]]) - np.testing.assert_almost_equal(fdata.metas, - [[0.0, 1.0], - [1.0, 2.0]]) + np.testing.assert_almost_equal( + fdata.X, [[1.0, 2.0, 1.0, 1.0], [1.0, 2.0, 1.0, 1.0]] + ) + np.testing.assert_almost_equal(fdata.metas, [[0.0, 1.0], [1.0, 2.0]]) self.assertEqual(fdata.domain.metas[0].name, "EMSC parameter 0") self.assertEqual(fdata.domain.metas[1].name, "EMSC scaling parameter") def test_abde(self): # TODO Find test values - data = Table.from_numpy(None, [[1.0, 2.0, 1.0, 1.0], - [3.0, 5.0, 3.0, 3.0]]) + data = Table.from_numpy(None, [[1.0, 2.0, 1.0, 1.0], [3.0, 5.0, 3.0, 3.0]]) f = EMSC(reference=data[0:1], order=2, output_model=True) fdata = f(data) - np.testing.assert_almost_equal(fdata.X, - [[1.0, 2.0, 1.0, 1.0], - [1.0, 2.0, 1.0, 1.0]]) + np.testing.assert_almost_equal( + fdata.X, [[1.0, 2.0, 1.0, 1.0], [1.0, 2.0, 1.0, 1.0]] + ) - np.testing.assert_almost_equal(fdata.metas, - [[0.0, 0.0, 0.0, 1.0], - [1.0, 0.0, 0.0, 2.0]]) + np.testing.assert_almost_equal( + fdata.metas, [[0.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 2.0]] + ) self.assertEqual(fdata.domain.metas[0].name, "EMSC parameter 0") self.assertEqual(fdata.domain.metas[1].name, "EMSC parameter 1") self.assertEqual(fdata.domain.metas[2].name, "EMSC parameter 2") @@ -56,117 +61,155 @@ def test_abde(self): def test_no_reference(self): # average from the data will be used - data = Table.from_numpy(None, [[1.0, 2.0, 1.0, 1.0], - [3.0, 5.0, 3.0, 3.0]]) + data = Table.from_numpy(None, [[1.0, 2.0, 1.0, 1.0], [3.0, 5.0, 3.0, 3.0]]) with self.assertRaises(MissingReferenceException): _ = EMSC()(data) def test_select_all(self): - data = spectra_table([0, 1, 2, 3], - [[1.0, 2.0, 1.0, 1.0], - [3.0, 5.0, 3.0, 3.0]]) + data = spectra_table([0, 1, 2, 3], [[1.0, 2.0, 1.0, 1.0], [3.0, 5.0, 3.0, 3.0]]) f = EMSC(reference=data[0:1], order=2, output_model=True) noweights = f(data) # table obtained by now-removed ranges_to_weight_table([(0, 3, 1)]) - weight_table = spectra_table([-5e-324, 0.0, 3.0, 3.0000000000000004], - [[0, 1, 1, 0]]) + weight_table = spectra_table( + [-5e-324, 0.0, 3.0, 3.0000000000000004], [[0, 1, 1, 0]] + ) f = EMSC(reference=data[0:1], order=2, output_model=True, weights=weight_table) weights = f(data) np.testing.assert_equal(weights.X, noweights.X) - f = EMSC(reference=data[0:1], order=2, output_model=True, - weights=SelectionFunction(0, 3, 1)) + f = EMSC( + reference=data[0:1], + order=2, + output_model=True, + weights=SelectionFunction(0, 3, 1), + ) weights = f(data) np.testing.assert_equal(weights.X, noweights.X) def test_interpolate_wavenumbers(self): - domain_ref = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in [1.0, 2.0, 3.0, 4.0]]) + domain_ref = Orange.data.Domain( + [Orange.data.ContinuousVariable(str(w)) for w in [1.0, 2.0, 3.0, 4.0]] + ) data_ref = Table.from_numpy(domain_ref, X=[[1.0, 3.0, 2.0, 3.0]]) - domain = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]]) - data = Table.from_numpy(domain, [[2.0, 4.0, 6.0, 5.0, 4.0, 5.0, 6.0], - [1.5, 2.0, 2.5, 2.25, 2.0, 2.25, 2.5]]) + domain = Orange.data.Domain( + [ + Orange.data.ContinuousVariable(str(w)) + for w in [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0] + ] + ) + data = Table.from_numpy( + domain, + [ + [2.0, 4.0, 6.0, 5.0, 4.0, 5.0, 6.0], + [1.5, 2.0, 2.5, 2.25, 2.0, 2.25, 2.5], + ], + ) f = EMSC(reference=data_ref[0:1], order=0, output_model=True) fdata = f(data) - np.testing.assert_almost_equal(fdata.X, - [[1.0, 2.0, 3.0, 2.5, 2.0, 2.5, 3.0], - [1.0, 2.0, 3.0, 2.5, 2.0, 2.5, 3.0]]) - np.testing.assert_almost_equal(fdata.metas, - [[0.0, 2.0], - [1.0, 0.5]]) + np.testing.assert_almost_equal( + fdata.X, + [[1.0, 2.0, 3.0, 2.5, 2.0, 2.5, 3.0], [1.0, 2.0, 3.0, 2.5, 2.0, 2.5, 3.0]], + ) + np.testing.assert_almost_equal(fdata.metas, [[0.0, 2.0], [1.0, 0.5]]) def test_order_wavenumbers(self): - domain_ref = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in [4.0, 3.0, 2.0, 1.0]]) + domain_ref = Orange.data.Domain( + [Orange.data.ContinuousVariable(str(w)) for w in [4.0, 3.0, 2.0, 1.0]] + ) data_ref = data = Table.from_numpy(domain_ref, [[3.0, 2.0, 3.0, 1.0]]) domain = Orange.data.Domain( - [Orange.data.ContinuousVariable(str(w)) - for w in [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]]) - data = data = Table.from_numpy(domain, [[2.0, 4.0, 6.0, 5.0, 4.0, 5.0, 6.0], - [1.5, 2.0, 2.5, 2.25, 2.0, 2.25, 2.5]]) + [ + Orange.data.ContinuousVariable(str(w)) + for w in [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0] + ] + ) + data = data = Table.from_numpy( + domain, + [ + [2.0, 4.0, 6.0, 5.0, 4.0, 5.0, 6.0], + [1.5, 2.0, 2.5, 2.25, 2.0, 2.25, 2.5], + ], + ) f = EMSC(reference=data_ref[0:1], order=0, output_model=True) fdata = f(data) - np.testing.assert_almost_equal(fdata.X, - [[1.0, 2.0, 3.0, 2.5, 2.0, 2.5, 3.0], - [1.0, 2.0, 3.0, 2.5, 2.0, 2.5, 3.0]]) - np.testing.assert_almost_equal(fdata.metas, - [[0.0, 2.0], - [1.0, 0.5]]) + np.testing.assert_almost_equal( + fdata.X, + [[1.0, 2.0, 3.0, 2.5, 2.0, 2.5, 3.0], [1.0, 2.0, 3.0, 2.5, 2.0, 2.5, 3.0]], + ) + np.testing.assert_almost_equal(fdata.metas, [[0.0, 2.0], [1.0, 0.5]]) def test_badspectra(self): - data = Table.from_numpy(None, [[0, 0.25, 4.5, 4.75, 1.0, 1.25, - 7.5, 7.75, 2.0, 5.25, 5.5, 2.75]]) + data = Table.from_numpy( + None, [[0, 0.25, 4.5, 4.75, 1.0, 1.25, 7.5, 7.75, 2.0, 5.25, 5.5, 2.75]] + ) data_ref = Table.from_numpy(None, [[0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 0, 0]]) badspec = Table.from_numpy(None, [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0]]) - f = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True) + f = EMSC( + reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True + ) fdata = f(data) np.testing.assert_almost_equal( - fdata.X, - [[0.0, 0.0, 2.0, 2.0, 0.0, 0.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0]]) - np.testing.assert_almost_equal( - fdata.metas, - [[1.375, 1.375, 3.0, 2.0]]) + fdata.X, [[0.0, 0.0, 2.0, 2.0, 0.0, 0.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0]] + ) + np.testing.assert_almost_equal(fdata.metas, [[1.375, 1.375, 3.0, 2.0]]) def test_multiple_badspectra(self): - data = Table.from_numpy(None, [[0, 0.25, 4.5, 4.75, 1.0, 1.25, - 7.5, 7.75, 2.0, 5.25, 5.5, 2.75]]) + data = Table.from_numpy( + None, [[0, 0.25, 4.5, 4.75, 1.0, 1.25, 7.5, 7.75, 2.0, 5.25, 5.5, 2.75]] + ) data_ref = Table.from_numpy(None, [[0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0]]) - badspec = Table.from_numpy(None, [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0]]) - - f = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True) + badspec = Table.from_numpy( + None, + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0], + ], + ) + + f = EMSC( + reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True + ) fdata = f(data) np.testing.assert_almost_equal( - fdata.X, - [[0.0, 0.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]) - np.testing.assert_almost_equal( - fdata.metas, - [[1.375, 1.375, 3.0, 6.0, 2.0]]) + fdata.X, [[0.0, 0.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] + ) + np.testing.assert_almost_equal(fdata.metas, [[1.375, 1.375, 3.0, 6.0, 2.0]]) def test_eq(self): - data = Table.from_numpy(None, [[0, 0.25, 4.5, 4.75, 1.0, 1.25, - 7.5, 7.75, 2.0, 5.25, 5.5, 2.75]]) + data = Table.from_numpy( + None, [[0, 0.25, 4.5, 4.75, 1.0, 1.25, 7.5, 7.75, 2.0, 5.25, 5.5, 2.75]] + ) data_ref = Table.from_numpy(None, [[0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 0, 0]]) badspec = Table.from_numpy(None, [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0]]) - d1 = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True)(data) - d2 = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True)(data) + d1 = EMSC( + reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True + )(data) + d2 = EMSC( + reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True + )(data) self.assertEqual(d1.domain, d2.domain) self.assertEqual(hash(d1.domain), hash(d2.domain)) - d2 = EMSC(reference=Table.from_numpy(None, [[1, 0, 2, 2, 0, 0, 3, 3, 0, 0, 0, 0]]), - badspectra=badspec, order=1, output_model=True)(data) + d2 = EMSC( + reference=Table.from_numpy(None, [[1, 0, 2, 2, 0, 0, 3, 3, 0, 0, 0, 0]]), + badspectra=badspec, + order=1, + output_model=True, + )(data) self.assertNotEqual(d1.domain, d2.domain) self.assertNotEqual(hash(d1.domain), hash(d2.domain)) - d2 = EMSC(reference=data_ref[0:1], - badspectra=Table.from_numpy(None, [[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0]]), - order=1, output_model=True)(data) + d2 = EMSC( + reference=data_ref[0:1], + badspectra=Table.from_numpy(None, [[1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0]]), + order=1, + output_model=True, + )(data) self.assertNotEqual(d1.domain, d2.domain) self.assertNotEqual(hash(d1.domain), hash(d2.domain)) @@ -174,52 +217,81 @@ def test_eq(self): self.assertNotEqual(d1.domain, d2.domain) self.assertNotEqual(hash(d1.domain), hash(d2.domain)) - weight_table = spectra_table([-5e-324, 0.0, 3.0, 3.0000000000000004], - [[0, 1, 1, 0]]) - d2 = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True, - weights=weight_table)(data) + weight_table = spectra_table( + [-5e-324, 0.0, 3.0, 3.0000000000000004], [[0, 1, 1, 0]] + ) + d2 = EMSC( + reference=data_ref[0:1], + badspectra=badspec, + order=1, + output_model=True, + weights=weight_table, + )(data) self.assertNotEqual(d1.domain, d2.domain) self.assertNotEqual(hash(d1.domain), hash(d2.domain)) - d3 = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True, - weights=weight_table)(data) + d3 = EMSC( + reference=data_ref[0:1], + badspectra=badspec, + order=1, + output_model=True, + weights=weight_table, + )(data) self.assertEqual(d3.domain, d2.domain) self.assertEqual(hash(d3.domain), hash(d2.domain)) - d2 = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True, - weights=SelectionFunction(0, 3, 1))(data) + d2 = EMSC( + reference=data_ref[0:1], + badspectra=badspec, + order=1, + output_model=True, + weights=SelectionFunction(0, 3, 1), + )(data) self.assertNotEqual(d1.domain, d2.domain) self.assertNotEqual(hash(d1.domain), hash(d2.domain)) - d3 = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True, - weights=SelectionFunction(0, 3, 1))(data) + d3 = EMSC( + reference=data_ref[0:1], + badspectra=badspec, + order=1, + output_model=True, + weights=SelectionFunction(0, 3, 1), + )(data) self.assertEqual(d3.domain, d2.domain) self.assertEqual(hash(d3.domain), hash(d2.domain)) - d3 = EMSC(reference=data_ref[0:1], badspectra=badspec, order=1, output_model=True, - weights=SmoothedSelectionFunction(0, 3, 1, 0.5))(data) + d3 = EMSC( + reference=data_ref[0:1], + badspectra=badspec, + order=1, + output_model=True, + weights=SmoothedSelectionFunction(0, 3, 1, 0.5), + )(data) self.assertNotEqual(d3.domain, d2.domain) self.assertNotEqual(hash(d3.domain), hash(d2.domain)) class TestSelectionFuctions(unittest.TestCase): - def test_no_smoothing(self): fn = SelectionFunction(1, 2, 1) np.testing.assert_equal(fn(np.arange(0, 4, 1)), [0, 1, 1, 0]) np.testing.assert_equal(fn(np.arange(0, 4, 0.5)), [0, 0, 1, 1, 1, 0, 0, 0]) def test_overlap(self): - fn = Sum(SelectionFunction(1, 2, 1), SelectionFunction(3, 4, 1)) # non-overlapping regions + fn = Sum( + SelectionFunction(1, 2, 1), SelectionFunction(3, 4, 1) + ) # non-overlapping regions a = fn([0.5, 1, 2, 2.5, 3.5, 5]) np.testing.assert_equal(a, [0, 1, 1, 0, 1, 0]) - fn = Sum(SelectionFunction(1, 2, 1), SelectionFunction(1, 3, 1.3)) # overlapping + fn = Sum( + SelectionFunction(1, 2, 1), SelectionFunction(1, 3, 1.3) + ) # overlapping a = fn([0.5, 1, 1.5, 2, 2.0001, 2.5, 2.999, 3.01, 3.5]) np.testing.assert_equal(a, [0, 2.3, 2.3, 2.3, 1.3, 1.3, 1.3, 0, 0]) def test_smoothing(self): fn = SmoothedSelectionFunction(0, 10, 3, 4) tx = np.array([-5, -0.1, 0, 0.1, 1, 5]) - np.testing.assert_almost_equal(fn(tx), (np.tanh(tx/3)+1)/2 * 4) - np.testing.assert_almost_equal(fn(tx+10), (-np.tanh(tx/3)+1)/2 * 4) + np.testing.assert_almost_equal(fn(tx), (np.tanh(tx / 3) + 1) / 2 * 4) + np.testing.assert_almost_equal(fn(tx + 10), (-np.tanh(tx / 3) + 1) / 2 * 4) diff --git a/orangecontrib/spectroscopy/tests/test_integrate.py b/orangecontrib/spectroscopy/tests/test_integrate.py index 0fbcd8fed..e470913e7 100644 --- a/orangecontrib/spectroscopy/tests/test_integrate.py +++ b/orangecontrib/spectroscopy/tests/test_integrate.py @@ -4,12 +4,13 @@ import numpy as np from orangecontrib.spectroscopy.preprocess import Integrate -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALL_COLLAGEN +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALL_COLLAGEN, +) class TestIntegrate(unittest.TestCase, TestCommonIndpSamplesMixin): - preprocessors = [ Integrate(limits=[[900, 100], [1100, 1200], [1200, 1300]]), Integrate(methods=Integrate.Simple, limits=[[1100, 1200]]), @@ -19,15 +20,14 @@ class TestIntegrate(unittest.TestCase, TestCommonIndpSamplesMixin): Integrate(methods=Integrate.PeakAt, limits=[[1100]]), Integrate(methods=Integrate.PeakX, limits=[[1100, 1200]]), Integrate(methods=Integrate.PeakXBaseline, limits=[[1100, 1200]]), - Integrate(methods=Integrate.BaselineAbsolute, limits=[[900, 1200]]) + Integrate(methods=Integrate.BaselineAbsolute, limits=[[900, 1200]]), ] data = SMALL_COLLAGEN - def test_simple(self): - data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1], - [1, 2, 3, 1, np.nan, 1], - [1, 2, 3, 1, 1, np.nan]]) + data = Table.from_numpy( + None, [[1, 2, 3, 1, 1, 1], [1, 2, 3, 1, np.nan, 1], [1, 2, 3, 1, 1, np.nan]] + ) i = Integrate(methods=Integrate.Simple, limits=[[0, 5]])(data) self.assertEqual(i[0][0], 8) self.assertEqual(i[1][0], 8) @@ -35,9 +35,9 @@ def test_simple(self): np.testing.assert_equal(i.domain[0].compute_value.baseline(data)[1], 0) def test_baseline(self): - data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1], - [1, 2, 3, 1, np.nan, 1], - [1, 2, 3, 1, 1, np.nan]]) + data = Table.from_numpy( + None, [[1, 2, 3, 1, 1, 1], [1, 2, 3, 1, np.nan, 1], [1, 2, 3, 1, 1, np.nan]] + ) i = Integrate(methods=Integrate.Baseline, limits=[[0, 5]])(data) self.assertEqual(i[0][0], 3) self.assertEqual(i[1][0], 3) @@ -56,15 +56,15 @@ def test_peakbaseline(self): data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1]]) i = Integrate(methods=Integrate.PeakBaseline, limits=[[0, 5]])(data) self.assertEqual(i[0][0], 2) - np.testing.assert_equal(i.domain[0].compute_value.baseline(data)[1], - [[1, 1, 1, 1, 1, 1]]) + np.testing.assert_equal( + i.domain[0].compute_value.baseline(data)[1], [[1, 1, 1, 1, 1, 1]] + ) def test_peakat(self): data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1]]) i = Integrate(methods=Integrate.PeakAt, limits=[[0, 5]])(data) self.assertEqual(i[0][0], 1) - np.testing.assert_equal(i.domain[0].compute_value.baseline(data)[1], - 0) + np.testing.assert_equal(i.domain[0].compute_value.baseline(data)[1], 0) i = Integrate(methods=Integrate.PeakAt, limits=[[1.4, None]])(data) self.assertEqual(i[0][0], 2) i = Integrate(methods=Integrate.PeakAt, limits=[[1.6, None]])(data) @@ -82,13 +82,14 @@ def test_peakxbaseline(self): data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1]]) i = Integrate(methods=Integrate.PeakXBaseline, limits=[[0, 5]])(data) self.assertEqual(i[0][0], 2) - np.testing.assert_equal(i.domain[0].compute_value.baseline(data)[1], - [[1, 1, 1, 1, 1, 1]]) + np.testing.assert_equal( + i.domain[0].compute_value.baseline(data)[1], [[1, 1, 1, 1, 1, 1]] + ) def test_separate_baseline(self): - data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1], - [1, 2, 3, 1, np.nan, 1], - [1, 2, 3, 1, 1, np.nan]]) + data = Table.from_numpy( + None, [[1, 2, 3, 1, 1, 1], [1, 2, 3, 1, np.nan, 1], [1, 2, 3, 1, 1, np.nan]] + ) # baseline spans the whole region i = Integrate(methods=Integrate.Separate, limits=[[0, 5, 0, 5]])(data) @@ -115,12 +116,17 @@ def test_separate_baseline(self): np.testing.assert_equal(by, [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]) def test_absolute_integral(self): - data_neg = Table.from_numpy(None, [[-1, -2, -3, -1, -1, -1], - [-1, -2, -3, -1, np.nan, -1], - [-1, -2, -3, -1, -1, np.nan]]) - data_pos = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1], - [1, 2, 3, 1, np.nan, 1], - [1, 2, 3, 1, 1, np.nan]]) + data_neg = Table.from_numpy( + None, + [ + [-1, -2, -3, -1, -1, -1], + [-1, -2, -3, -1, np.nan, -1], + [-1, -2, -3, -1, -1, np.nan], + ], + ) + data_pos = Table.from_numpy( + None, [[1, 2, 3, 1, 1, 1], [1, 2, 3, 1, np.nan, 1], [1, 2, 3, 1, 1, np.nan]] + ) i = Integrate(methods=Integrate.BaselineAbsolute, limits=[[0, 5]])(data_neg) j = Integrate(methods=Integrate.BaselineAbsolute, limits=[[0, 5]])(data_pos) self.assertEqual(i[0][0], j[0][0]) @@ -151,35 +157,47 @@ def test_empty_interval(self): def test_different_integrals(self): data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1]]) - i = Integrate(methods=[Integrate.Simple, Integrate.Baseline], - limits=[[0, 5], [0, 5]])(data) + i = Integrate( + methods=[Integrate.Simple, Integrate.Baseline], limits=[[0, 5], [0, 5]] + )(data) self.assertEqual(i[0][0], 8) np.testing.assert_equal(i.domain[0].compute_value.baseline(data)[1], 0) np.testing.assert_equal(i.domain[1].compute_value.baseline(data)[1], 1) def test_names(self): data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1]]) - i = Integrate(methods=[Integrate.Simple, Integrate.Baseline, Integrate.Separate], - limits=[[0, 5], [0, 6], [1, 2, 0, 6]])(data) + i = Integrate( + methods=[Integrate.Simple, Integrate.Baseline, Integrate.Separate], + limits=[[0, 5], [0, 6], [1, 2, 0, 6]], + )(data) self.assertEqual(i.domain[0].name, "0 - 5") self.assertEqual(i.domain[1].name, "0 - 6") self.assertEqual(i.domain[2].name, "1 - 2 [baseline 0 - 6]") - i = Integrate(methods=[Integrate.Simple, Integrate.Baseline], - limits=[[0, 5], [0, 6]], names=["simple", "baseline"])(data) + i = Integrate( + methods=[Integrate.Simple, Integrate.Baseline], + limits=[[0, 5], [0, 6]], + names=["simple", "baseline"], + )(data) self.assertEqual(i.domain[0].name, "simple") self.assertEqual(i.domain[1].name, "baseline") def test_repeated(self): data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1]]) - i = Integrate(methods=[Integrate.Simple, Integrate.Baseline], - limits=[[0, 5], [0, 6]], names=["int", "int"])(data) + i = Integrate( + methods=[Integrate.Simple, Integrate.Baseline], + limits=[[0, 5], [0, 6]], + names=["int", "int"], + )(data) self.assertEqual(i.domain[0].name, "int") self.assertEqual(i.domain[1].name, "int (1)") def test_metas_output(self): data = Table.from_numpy(None, [[1, 2, 3, 1, 1, 1]]) - i = Integrate(methods=[Integrate.Simple, Integrate.Baseline], - limits=[[0, 5], [0, 6]], metas=True)(data) + i = Integrate( + methods=[Integrate.Simple, Integrate.Baseline], + limits=[[0, 5], [0, 6]], + metas=True, + )(data) metavars = [a.name for a in i.domain.metas] self.assertTrue("0 - 5" in metavars and "0 - 6" in metavars) self.assertEqual(i[0]["0 - 5"], 8) diff --git a/orangecontrib/spectroscopy/tests/test_interpolate.py b/orangecontrib/spectroscopy/tests/test_interpolate.py index 6a0f9755a..e97e3dd1b 100644 --- a/orangecontrib/spectroscopy/tests/test_interpolate.py +++ b/orangecontrib/spectroscopy/tests/test_interpolate.py @@ -10,18 +10,24 @@ import Orange -from orangecontrib.spectroscopy.preprocess import Interpolate, \ - interp1d_with_unknowns_numpy, interp1d_with_unknowns_scipy, \ - interp1d_wo_unknowns_scipy, InterpolateToDomain, NotAllContinuousException, \ - nan_extend_edges_and_interpolate +from orangecontrib.spectroscopy.preprocess import ( + Interpolate, + interp1d_with_unknowns_numpy, + interp1d_with_unknowns_scipy, + interp1d_wo_unknowns_scipy, + InterpolateToDomain, + NotAllContinuousException, + nan_extend_edges_and_interpolate, +) from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.tests.util import spectra_table -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALL_COLLAGEN +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALL_COLLAGEN, +) class TestInterpolate(unittest.TestCase, TestCommonIndpSamplesMixin): - preprocessors = [Interpolate(np.linspace(1000, 1700, 100))] data = SMALL_COLLAGEN @@ -46,13 +52,15 @@ def test_nofloatname(self): def test_floatname(self): data = self.collagen f1, f2 = 20, 21 - c1, c2 = float(data.domain.attributes[f1].name), \ - float(data.domain.attributes[f2].name) - avg = (c1 + c2)/2 + c1, c2 = ( + float(data.domain.attributes[f1].name), + float(data.domain.attributes[f2].name), + ) + avg = (c1 + c2) / 2 interpolated = Interpolate([avg])(data) self.assertIsInstance(interpolated, type(data)) av1 = interpolated.X.ravel() - av2 = data.X[:, [20,21]].mean(axis=1) + av2 = data.X[:, [20, 21]].mean(axis=1) np.testing.assert_allclose(av1, av2) def test_domain_conversion(self): @@ -74,22 +82,23 @@ def test_permute(self): rs = np.random.RandomState(0) data = self.iris oldX = data.X - #permute data + # permute data p = rs.permutation(range(len(data.domain.attributes))) - nattr = [Orange.data.ContinuousVariable(str(p[i])) - for i, a in enumerate(data.domain.attributes)] - data = Orange.data.Table.from_numpy(Orange.data.Domain(nattr), - X=data.X[:, p]) + nattr = [ + Orange.data.ContinuousVariable(str(p[i])) + for i, a in enumerate(data.domain.attributes) + ] + data = Orange.data.Table.from_numpy(Orange.data.Domain(nattr), X=data.X[:, p]) interpolated = Interpolate(range(len(data.domain.attributes)))(data) np.testing.assert_allclose(interpolated.X, oldX) - #also permute output + # also permute output p1 = rs.permutation(range(len(data.domain.attributes))) interpolated = Interpolate(p1)(data) np.testing.assert_allclose(interpolated.X, oldX[:, p1]) def test_out_of_band(self): data = self.iris - interpolated = Interpolate(range(-1, len(data.domain.attributes)+1))(data) + interpolated = Interpolate(range(-1, len(data.domain.attributes) + 1))(data) np.testing.assert_allclose(interpolated.X[:, 1:5], data.X) np.testing.assert_equal(np.asarray(interpolated.X[:, [0, -1]]), np.nan) @@ -154,16 +163,24 @@ def test_nan_extend_edges_and_interpolate(self): interp, unknowns = nan_extend_edges_and_interpolate(xs, data.X) self.assertIsInstance(interp, type(data.X)) nan = float("nan") - res = np.array([[nan, nan, nan, nan], - [4.9, 3.15, 1.4, 0.2], - [3.2, 3.2, 1.3, 0.2], - [4.6, 3.1, 1.5, 1.5], - [5., 3.6, 1.4, 0.2]]) - resu = np.array([[True, True, True, True], - [False, True, False, False], - [True, False, False, False], - [False, False, False, True], - [False, False, False, False]]) + res = np.array( + [ + [nan, nan, nan, nan], + [4.9, 3.15, 1.4, 0.2], + [3.2, 3.2, 1.3, 0.2], + [4.6, 3.1, 1.5, 1.5], + [5.0, 3.6, 1.4, 0.2], + ] + ) + resu = np.array( + [ + [True, True, True, True], + [False, True, False, False], + [True, False, False, False], + [False, False, False, True], + [False, False, False, False], + ] + ) np.testing.assert_allclose(interp, res) np.testing.assert_allclose(unknowns, resu) @@ -201,7 +218,6 @@ def test_eq(self): class TestInterpolateToDomain(unittest.TestCase): - @classmethod def setUpClass(cls): super().setUpClass() diff --git a/orangecontrib/spectroscopy/tests/test_irfft.py b/orangecontrib/spectroscopy/tests/test_irfft.py index 18b759e97..4aea7dfff 100644 --- a/orangecontrib/spectroscopy/tests/test_irfft.py +++ b/orangecontrib/spectroscopy/tests/test_irfft.py @@ -5,16 +5,20 @@ from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.irfft import (IRFFT, zero_fill, PhaseCorrection, - find_zpd, PeakSearch, ApodFunc, - MultiIRFFT, - ) +from orangecontrib.spectroscopy.irfft import ( + IRFFT, + zero_fill, + PhaseCorrection, + find_zpd, + PeakSearch, + ApodFunc, + MultiIRFFT, +) dx = 1.0 / 15797.337544 / 2.0 class TestIRFFT(unittest.TestCase): - def setUp(self): self.ifg_single = Orange.data.Table("IFG_single.dpt") self.ifg_seq_ref = Orange.data.Table("agilent/background_agg256.seq") @@ -59,18 +63,20 @@ def test_peak_search(self): def test_agilent_fft_sc(self): ifg = self.ifg_seq_ref.X[0] # dat = self.sc_dat_ref.X[0] # TODO scaling diffrences fail - dx_ag = (1 / 1.57980039e+04 / 2) * 4 - fft = IRFFT(dx=dx_ag, - apod_func=ApodFunc.BLACKMAN_HARRIS_4, - zff=1, - phase_res=None, - phase_corr=PhaseCorrection.MERTZ, - peak_search=PeakSearch.MINIMUM) + dx_ag = (1 / 1.57980039e04 / 2) * 4 + fft = IRFFT( + dx=dx_ag, + apod_func=ApodFunc.BLACKMAN_HARRIS_4, + zff=1, + phase_res=None, + phase_corr=PhaseCorrection.MERTZ, + peak_search=PeakSearch.MINIMUM, + ) fft(ifg) self.assertEqual(fft.zpd, 69) dat_x = getx(self.sc_dat_ref) limits = np.searchsorted(fft.wavenumbers, [dat_x[0] - 1, dat_x[-1]]) - np.testing.assert_allclose(fft.wavenumbers[limits[0]:limits[1]], dat_x) + np.testing.assert_allclose(fft.wavenumbers[limits[0] : limits[1]], dat_x) # TODO This fails due to scaling differences # np.testing.assert_allclose(fft.spectrum[limits[0]:limits[1]], dat) @@ -79,35 +85,39 @@ def test_agilent_fft_ab(self): ifg_sam = Orange.data.Table("agilent/4_noimage_agg256.seq").X[0] dat_T = Orange.data.Table("agilent/4_noimage_agg256.dat") dat = dat_T.X[0] - dx_ag = (1 / 1.57980039e+04 / 2) * 4 - fft = IRFFT(dx=dx_ag, - apod_func=ApodFunc.BLACKMAN_HARRIS_4, - zff=1, - phase_res=None, - phase_corr=PhaseCorrection.MERTZ, - peak_search=PeakSearch.MINIMUM) + dx_ag = (1 / 1.57980039e04 / 2) * 4 + fft = IRFFT( + dx=dx_ag, + apod_func=ApodFunc.BLACKMAN_HARRIS_4, + zff=1, + phase_res=None, + phase_corr=PhaseCorrection.MERTZ, + peak_search=PeakSearch.MINIMUM, + ) fft(ifg_ref) rsc = fft.spectrum fft(ifg_sam) ssc = fft.spectrum dat_x = getx(dat_T) limits = np.searchsorted(fft.wavenumbers, [dat_x[0] - 1, dat_x[-1]]) - np.testing.assert_allclose(fft.wavenumbers[limits[0]:limits[1]], dat_x) + np.testing.assert_allclose(fft.wavenumbers[limits[0] : limits[1]], dat_x) # Calculate absorbance from ssc and rsc ab = np.log10(rsc / ssc) # Compare to agilent absorbance # NB 4 mAbs error - np.testing.assert_allclose(ab[limits[0]:limits[1]], dat, atol=0.004) + np.testing.assert_allclose(ab[limits[0] : limits[1]], dat, atol=0.004) def test_multi(self): - dx_ag = (1 / 1.57980039e+04 / 2) * 4 - fft = MultiIRFFT(dx=dx_ag, - apod_func=ApodFunc.BLACKMAN_HARRIS_4, - zff=1, - phase_res=None, - phase_corr=PhaseCorrection.MERTZ, - peak_search=PeakSearch.MINIMUM) - zpd = 69 # from test_agilent_fft_sc(), TODO replace with value read from file + dx_ag = (1 / 1.57980039e04 / 2) * 4 + fft = MultiIRFFT( + dx=dx_ag, + apod_func=ApodFunc.BLACKMAN_HARRIS_4, + zff=1, + phase_res=None, + phase_corr=PhaseCorrection.MERTZ, + peak_search=PeakSearch.MINIMUM, + ) + zpd = 69 # from test_agilent_fft_sc(), TODO replace with value read from file fft(self.ifg_seq_ref.X, zpd) def test_multi_ab(self): @@ -115,13 +125,15 @@ def test_multi_ab(self): ifg_sam = Orange.data.Table("agilent/4_noimage_agg256.seq").X dat_T = Orange.data.Table("agilent/4_noimage_agg256.dat") dat = dat_T.X - dx_ag = (1 / 1.57980039e+04 / 2) * 4 - fft = MultiIRFFT(dx=dx_ag, - apod_func=ApodFunc.BLACKMAN_HARRIS_4, - zff=1, - phase_res=None, - phase_corr=PhaseCorrection.MERTZ, - peak_search=PeakSearch.MINIMUM) + dx_ag = (1 / 1.57980039e04 / 2) * 4 + fft = MultiIRFFT( + dx=dx_ag, + apod_func=ApodFunc.BLACKMAN_HARRIS_4, + zff=1, + phase_res=None, + phase_corr=PhaseCorrection.MERTZ, + peak_search=PeakSearch.MINIMUM, + ) zpd = 69 # from test_agilent_fft_sc(), TODO replace with value read from file fft(ifg_ref, zpd) rsc = fft.spectrum @@ -129,9 +141,9 @@ def test_multi_ab(self): ssc = fft.spectrum dat_x = getx(dat_T) limits = np.searchsorted(fft.wavenumbers, [dat_x[0] - 1, dat_x[-1]]) - np.testing.assert_allclose(fft.wavenumbers[limits[0]:limits[1]], dat_x) + np.testing.assert_allclose(fft.wavenumbers[limits[0] : limits[1]], dat_x) # Calculate absorbance from ssc and rsc ab = np.log10(rsc / ssc) # Compare to agilent absorbance # NB 4 mAbs error - np.testing.assert_allclose(ab[:, limits[0]:limits[1]], dat, atol=0.004) + np.testing.assert_allclose(ab[:, limits[0] : limits[1]], dat, atol=0.004) diff --git a/orangecontrib/spectroscopy/tests/test_me_emsc.py b/orangecontrib/spectroscopy/tests/test_me_emsc.py index 8e649cb4b..3f740b989 100644 --- a/orangecontrib/spectroscopy/tests/test_me_emsc.py +++ b/orangecontrib/spectroscopy/tests/test_me_emsc.py @@ -6,14 +6,17 @@ from Orange.data import FileFormat, dataset_dirs from orangecontrib.spectroscopy.preprocess.me_emsc import ME_EMSC -from orangecontrib.spectroscopy.preprocess.emsc import SelectionFunction, SmoothedSelectionFunction +from orangecontrib.spectroscopy.preprocess.emsc import SmoothedSelectionFunction from orangecontrib.spectroscopy.preprocess.npfunc import Sum -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALLER_COLLAGEN, add_edge_case_data_parameter +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALLER_COLLAGEN, + add_edge_case_data_parameter, +) def weights_from_inflection_points_legacy(points, kappa, wavenumbers): - """ As ported from the original Matlab code. + """As ported from the original Matlab code. BUG: results are shifted for 1 wavenumber positions-wise @@ -32,7 +35,7 @@ def weights_from_inflection_points_legacy(points, kappa, wavenumbers): wn < 1900 go towards 1, but for (4 points) they go towards 0 when wn < 1000. """ # Hyperbolic tangent function - hypTan = lambda x_range, kap: 0.5 * (np.tanh(kap * x_range) + 1) + hypTan = lambda x_range, kap: 0.5 * (np.tanh(kap * x_range) + 1) # noqa: E731 # Calculate position of inflection points p1 = np.argmin(np.abs(wavenumbers - points[2])) @@ -47,12 +50,16 @@ def weights_from_inflection_points_legacy(points, kappa, wavenumbers): xp1 = np.hstack([x1, x2]) - x3 = np.linspace(-(np.ceil((p2 - p1) / 2) - 1) * dx, 0, int((np.ceil((p2 - p1) / 2)))) + x3 = np.linspace( + -(np.ceil((p2 - p1) / 2) - 1) * dx, 0, int((np.ceil((p2 - p1) / 2))) + ) x4 = np.linspace(dx, np.floor((p3 - p2) / 2) * dx, int(np.floor((p3 - p2) / 2))) xp2 = np.hstack([x3, x4]) - x5 = np.linspace(-(np.ceil((p3 - p2) / 2) - 1) * dx, 0, int((np.ceil((p3 - p2) / 2)))) + x5 = np.linspace( + -(np.ceil((p3 - p2) / 2) - 1) * dx, 0, int((np.ceil((p3 - p2) / 2))) + ) x6 = np.linspace(dx, (len(wavenumbers) - p3) * dx, int(len(wavenumbers) - p3)) xp3 = np.hstack([x5, x6]) @@ -67,8 +74,12 @@ def weights_from_inflection_points_legacy(points, kappa, wavenumbers): p0 = np.argmin(np.abs(wavenumbers - points[3])) x1a = np.linspace(-(p0 - 1) * dx, 0, p0) - x2a = np.linspace(dx, np.floor((p1 - p0) / 2) * dx, int(np.floor((p1 - p0) / 2))) - x3a = np.linspace(-(np.ceil((p1 - p0) / 2) - 1) * dx, 0, int((np.ceil((p1 - p0) / 2)))) + x2a = np.linspace( + dx, np.floor((p1 - p0) / 2) * dx, int(np.floor((p1 - p0) / 2)) + ) + x3a = np.linspace( + -(np.ceil((p1 - p0) / 2) - 1) * dx, 0, int((np.ceil((p1 - p0) / 2))) + ) xp0 = np.hstack([x1a, x2a]) xp1 = np.hstack([x3a, x2]) @@ -79,15 +90,18 @@ def weights_from_inflection_points_legacy(points, kappa, wavenumbers): wei_X = wei_X.reshape(1, -1) dom = Orange.data.Domain( - [Orange.data.ContinuousVariable(name=str(float(a))) for a in wavenumbers]) + [Orange.data.ContinuousVariable(name=str(float(a))) for a in wavenumbers] + ) data = Orange.data.Table.from_numpy(dom, wei_X) return data class TestME_EMSC(unittest.TestCase, TestCommonIndpSamplesMixin): - preprocessors = list( - add_edge_case_data_parameter(ME_EMSC, "reference", SMALLER_COLLAGEN[0:1], max_iter=4)) + add_edge_case_data_parameter( + ME_EMSC, "reference", SMALLER_COLLAGEN[0:1], max_iter=4 + ) + ) data = SMALLER_COLLAGEN @classmethod @@ -128,16 +142,22 @@ def locate_dataset(fn): cls.res_14ncomp_20th_elem = v[1] cls.res_fixed_iter3_20th_elem = v[2] - cls.numiter_std = np.loadtxt(path2data6, usecols=(1,), delimiter=",", dtype="int64") + cls.numiter_std = np.loadtxt( + path2data6, usecols=(1,), delimiter=",", dtype="int64" + ) - cls.RMSE_std = np.loadtxt(path2data7, usecols=(1,), delimiter=",", dtype="float") + cls.RMSE_std = np.loadtxt( + path2data7, usecols=(1,), delimiter=",", dtype="float" + ) - domain_reference = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in cls.wnM]) + domain_reference = Orange.data.Domain( + [Orange.data.ContinuousVariable(str(w)) for w in cls.wnM] + ) cls.reference = Orange.data.Table(domain_reference, cls.Matrigel) - domain_spectra = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in cls.wnS]) + domain_spectra = Orange.data.Domain( + [Orange.data.ContinuousVariable(str(w)) for w in cls.wnS] + ) cls.spectra = Orange.data.Table(domain_spectra, cls.Spectra) # inflPoints = [3700, 2550, 1900, 0] @@ -145,13 +165,23 @@ def locate_dataset(fn): # # weights = weights_from_inflection_points(inflPoints, kappa, self.wnS) - f = ME_EMSC(reference=cls.reference, ncomp=False, weights=False, max_iter=45, output_model=True) + f = ME_EMSC( + reference=cls.reference, + ncomp=False, + weights=False, + max_iter=45, + output_model=True, + ) cls.f1data = f(cls.spectra) - f2 = ME_EMSC(reference=cls.reference, ncomp=14, output_model=True) # With weights + f2 = ME_EMSC( + reference=cls.reference, ncomp=14, output_model=True + ) # With weights cls.f2data = f2(cls.spectra) - f3 = ME_EMSC(reference=cls.reference, ncomp=False, fixed_iter=1, output_model=True) + f3 = ME_EMSC( + reference=cls.reference, ncomp=False, fixed_iter=1, output_model=True + ) cls.f3data = f3(cls.spectra) def disabled_test_plotting(self): @@ -161,7 +191,11 @@ def disabled_test_plotting(self): plt.figure() plt.plot(self.wnS[0::20], self.f1data[0, 0::20].X.T, label='python') plt.plot(self.wnS[0::20], self.corr_default_20th_elem, label='matlab') - plt.plot(self.wnS[0::20], self.f1data[0, 0::20].X.T[:, 0] - self.corr_default_20th_elem, label='diff') + plt.plot( + self.wnS[0::20], + self.f1data[0, 0::20].X.T[:, 0] - self.corr_default_20th_elem, + label='diff', + ) plt.legend() plt.title('Comparison Matlab/Python - default parameters') @@ -169,7 +203,11 @@ def disabled_test_plotting(self): plt.figure() plt.plot(self.wnS[0::20], self.f2data[0, 0::20].X.T, label='python') plt.plot(self.wnS[0::20], self.corr_14ncomp_20th_elem, label='matlab') - plt.plot(self.wnS[0::20], self.f2data[0, 0::20].X.T[:, 0]-self.corr_14ncomp_20th_elem, label='diff') + plt.plot( + self.wnS[0::20], + self.f2data[0, 0::20].X.T[:, 0] - self.corr_14ncomp_20th_elem, + label='diff', + ) plt.legend() plt.title('Comparison Matlab/Python - 14 principal components') @@ -177,27 +215,55 @@ def disabled_test_plotting(self): plt.figure() plt.plot(self.wnS[0::20], self.f3data[0, 0::20].X.T, label='python') plt.plot(self.wnS[0::20], self.corr_fixed_iter3_20th_elem, label='matlab') - plt.plot(self.wnS[0::20], self.f3data[0, 0::20].X.T[:, 0]-self.corr_fixed_iter3_20th_elem, label='diff') + plt.plot( + self.wnS[0::20], + self.f3data[0, 0::20].X.T[:, 0] - self.corr_fixed_iter3_20th_elem, + label='diff', + ) plt.legend() plt.title('Comparison Matlab/Python - fixed iterations 3') plt.show() def test_correction_output(self): - np.testing.assert_almost_equal(self.corr_default_20th_elem, self.f1data[0, 0::20].X.T[:, 0]) - np.testing.assert_almost_equal(self.corr_14ncomp_20th_elem, self.f2data[0, 0::20].X.T[:, 0]) - np.testing.assert_almost_equal(self.corr_fixed_iter3_20th_elem, self.f3data[0, 0::20].X.T[:, 0]) + np.testing.assert_almost_equal( + self.corr_default_20th_elem, self.f1data[0, 0::20].X.T[:, 0] + ) + np.testing.assert_almost_equal( + self.corr_14ncomp_20th_elem, self.f2data[0, 0::20].X.T[:, 0] + ) + np.testing.assert_almost_equal( + self.corr_fixed_iter3_20th_elem, self.f3data[0, 0::20].X.T[:, 0] + ) def test_EMSC_parameters(self): - np.testing.assert_almost_equal(abs(self.f1data.metas[0, :-2]), abs(self.param_default_20th_elem)) - np.testing.assert_almost_equal(abs(self.f2data.metas[0, :-2]), abs(self.param_14ncomp_20th_elem)) - np.testing.assert_almost_equal(abs(self.f3data.metas[0, :-2]), abs(self.param_fixed_iter3_20th_elem)) + np.testing.assert_almost_equal( + abs(self.f1data.metas[0, :-2]), abs(self.param_default_20th_elem) + ) + np.testing.assert_almost_equal( + abs(self.f2data.metas[0, :-2]), abs(self.param_14ncomp_20th_elem) + ) + np.testing.assert_almost_equal( + abs(self.f3data.metas[0, :-2]), abs(self.param_fixed_iter3_20th_elem) + ) def test_number_iterations(self): - numiter = np.array([self.f1data.metas[0, -2], self.f2data.metas[0, -2], self.f3data.metas[0, -2]]) + numiter = np.array( + [ + self.f1data.metas[0, -2], + self.f2data.metas[0, -2], + self.f3data.metas[0, -2], + ] + ) np.testing.assert_equal(numiter, self.numiter_std) def test_RMSE(self): - RMSE = np.array([self.f1data.metas[0, -1], self.f2data.metas[0, -1], self.f3data.metas[0, -1]]) + RMSE = np.array( + [ + self.f1data.metas[0, -1], + self.f2data.metas[0, -1], + self.f3data.metas[0, -1], + ] + ) np.testing.assert_equal(RMSE, self.RMSE_std) def test_same_data_reference(self): @@ -208,8 +274,9 @@ def test_short_reference(self): wnMshort = self.wnM[0::30] Matrigelshort = self.Matrigel[0, 0::30] Matrigelshort = Matrigelshort.reshape(-1, 1).T - domain_reference = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in wnMshort]) + domain_reference = Orange.data.Domain( + [Orange.data.ContinuousVariable(str(w)) for w in wnMshort] + ) reference = Orange.data.Table(domain_reference, Matrigelshort) # it was crashing before ME_EMSC(reference=reference)(self.spectra) @@ -226,9 +293,14 @@ def test_eq(self): self.assertNotEqual(d1.domain, d2.domain) self.assertNotEqual(hash(d1.domain), hash(d2.domain)) - d2 = ME_EMSC(reference=ref, ncomp=False, weights=False, max_iter=1, - n0=np.linspace(1.1, 1.4, 11), - a=np.linspace(2, 7.1, 11))(spectra) + d2 = ME_EMSC( + reference=ref, + ncomp=False, + weights=False, + max_iter=1, + n0=np.linspace(1.1, 1.4, 11), + a=np.linspace(2, 7.1, 11), + )(spectra) self.assertNotEqual(d1.domain, d2.domain) self.assertNotEqual(hash(d1.domain), hash(d2.domain)) @@ -249,7 +321,6 @@ def test_eq(self): class TestInflectionPointWeighting(unittest.TestCase): - def test_weights_from_inflection_points_old_use(self): ws = 5 # spacing between wavenumbers dx = 0.094 # hardcoded constant from weights_from_inflection_points_legacy @@ -257,19 +328,27 @@ def test_weights_from_inflection_points_old_use(self): wns = np.arange(0, 5000, ws) # at > 3700 towards 0, at > 1900 towards 1 - weights = weights_from_inflection_points_legacy([3700, 2800, 1900, 0], [1, 1, 1, 1], wns) + weights = weights_from_inflection_points_legacy( + [3700, 2800, 1900, 0], [1, 1, 1, 1], wns + ) oldx = wns + ws # shift due to a bug in weights_from_inflection_points_legacy oldy = weights.X[0] - new = Sum(SmoothedSelectionFunction(2800, 3700, ws / dx, 1), - SmoothedSelectionFunction(-1000, 1900, ws / dx, 1)) + new = Sum( + SmoothedSelectionFunction(2800, 3700, ws / dx, 1), + SmoothedSelectionFunction(-1000, 1900, ws / dx, 1), + ) newy = new(oldx) - self.assertLess(np.max(np.abs(newy-oldy)), 1e-7) + self.assertLess(np.max(np.abs(newy - oldy)), 1e-7) # at > 3700 towards 0, at > 1000 towards 0 - weights = weights_from_inflection_points_legacy([3700, 2800, 1900, 1000], [1, 1, 1, 1], wns) + weights = weights_from_inflection_points_legacy( + [3700, 2800, 1900, 1000], [1, 1, 1, 1], wns + ) oldx = wns + ws # shift due to a bug in weights_from_inflection_points_legacy oldy = weights.X[0] - new = Sum(SmoothedSelectionFunction(2800, 3700, ws / dx, 1), - SmoothedSelectionFunction(1000, 1900, ws / dx, 1)) + new = Sum( + SmoothedSelectionFunction(2800, 3700, ws / dx, 1), + SmoothedSelectionFunction(1000, 1900, ws / dx, 1), + ) newy = new(oldx) - self.assertLess(np.max(np.abs(newy-oldy)), 1e-7) + self.assertLess(np.max(np.abs(newy - oldy)), 1e-7) diff --git a/orangecontrib/spectroscopy/tests/test_npfunc.py b/orangecontrib/spectroscopy/tests/test_npfunc.py index bf0dcfa6e..5bbf269c7 100644 --- a/orangecontrib/spectroscopy/tests/test_npfunc.py +++ b/orangecontrib/spectroscopy/tests/test_npfunc.py @@ -2,12 +2,16 @@ import numpy as np -from orangecontrib.spectroscopy.preprocess.npfunc import Function, Constant, Identity,\ - Segments, Sum +from orangecontrib.spectroscopy.preprocess.npfunc import ( + Function, + Constant, + Identity, + Segments, + Sum, +) class TestInflectionPointWeighting(unittest.TestCase): - def test_constant(self): a = np.zeros((2, 3)) constant = Constant(3) @@ -17,16 +21,19 @@ def test_constant(self): def test_cond(self): x = np.arange(6) - segm = Segments((lambda x: x < 3, Identity()), - (lambda x: x >= 3, Function(lambda x: -x))) + segm = Segments( + (lambda x: x < 3, Identity()), (lambda x: x >= 3, Function(lambda x: -x)) + ) np.testing.assert_equal(segm(x), [0, 1, 2, -3, -4, -5]) - segm = Segments((lambda x: True, Identity()), - (lambda x: x >= 3, Function(lambda x: -x))) + segm = Segments( + (lambda x: True, Identity()), (lambda x: x >= 3, Function(lambda x: -x)) + ) np.testing.assert_equal(segm(x), [0, 1, 2, -3, -4, -5]) - segm = Segments((lambda x: True, Identity()), - (lambda x: True, Function(lambda x: -x))) + segm = Segments( + (lambda x: True, Identity()), (lambda x: True, Function(lambda x: -x)) + ) np.testing.assert_equal(segm(x), [0, -1, -2, -3, -4, -5]) def test_sum(self): diff --git a/orangecontrib/spectroscopy/tests/test_owalignstack.py b/orangecontrib/spectroscopy/tests/test_owalignstack.py index 7f576b7c3..9205e0291 100644 --- a/orangecontrib/spectroscopy/tests/test_owalignstack.py +++ b/orangecontrib/spectroscopy/tests/test_owalignstack.py @@ -9,38 +9,36 @@ from orangecontrib.spectroscopy.data import build_spec_table from orangecontrib.spectroscopy.io.util import _spectra_from_image -from orangecontrib.spectroscopy.widgets.owstackalign import \ - alignstack, RegisterTranslation, shift_fill, OWStackAlign, process_stack +from orangecontrib.spectroscopy.widgets.owstackalign import ( + alignstack, + RegisterTranslation, + shift_fill, + OWStackAlign, + process_stack, +) def test_image(): - return np.hstack((np.zeros((5, 1)), - np.diag([1, 5., 3., 1, 1]), - np.ones((5, 1)))) + return np.hstack((np.zeros((5, 1)), np.diag([1, 5.0, 3.0, 1, 1]), np.ones((5, 1)))) def _up(im, fill=0): - return np.vstack((im[1:], - np.ones_like(im[0]) * fill)) + return np.vstack((im[1:], np.ones_like(im[0]) * fill)) def _down(im, fill=0): - return np.vstack((np.ones_like(im[0]) * fill, - im[:-1])) + return np.vstack((np.ones_like(im[0]) * fill, im[:-1])) def _right(im, fill=0): - return np.hstack((np.ones_like(im[:, :1]) * fill, - im[:, :-1])) + return np.hstack((np.ones_like(im[:, :1]) * fill, im[:, :-1])) def _left(im, fill=0): - return np.hstack((im[:, 1:], - np.ones_like(im[:, :1]) * fill)) + return np.hstack((im[:, 1:], np.ones_like(im[:, :1]) * fill)) class TestUtils(unittest.TestCase): - def test_image_shift(self): im = test_image() calculate_shift = RegisterTranslation() @@ -57,8 +55,9 @@ def test_image_shift(self): def test_alignstack(self): im = test_image() - _, aligned = alignstack([im, _up(im), _down(im), _right(im)], - shiftfn=RegisterTranslation()) + _, aligned = alignstack( + [im, _up(im), _down(im), _right(im)], shiftfn=RegisterTranslation() + ) self.assertEqual(aligned.shape, (4, 5, 7)) def test_alignstack_calls_filterfn(self): @@ -67,9 +66,7 @@ def test_alignstack_calls_filterfn(self): im = test_image() up = _up(im) down = _down(im) - alignstack([im, up, down], - shiftfn=RegisterTranslation(), - filterfn=filterfn) + alignstack([im, up, down], shiftfn=RegisterTranslation(), filterfn=filterfn) for i, t in enumerate([im, up, down]): self.assertIs(filterfn.call_args_list[i][0][0], t) @@ -110,36 +107,45 @@ def test_shift_fill(self): def diamond(): - return np.array([ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], - [0, 0, 0, 1, 1, 6, 1, 1, 0, 0, 0], - [0, 0, 1, 1, 5, 1, 7, 1, 1, 0, 0], - [0, 0, 0, 1, 1, 8, 1, 1, 0, 0, 0], - [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=float) + return np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 6, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 5, 1, 7, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 8, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + dtype=float, + ) + def rectangle(): - return np.array([ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=float) + return np.array( + [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ], + dtype=float, + ) def fake_stxm_from_image(image): @@ -151,6 +157,7 @@ def fake_stxm_from_image(image): spectral[:, :, 4] = _down(_right(_down(diamond()))) return spectral + def fake_stxm_from_rectangle(image): spectral = np.zeros(image.shape + (5,)) spectral[:, :, 0] = rectangle() @@ -160,7 +167,8 @@ def fake_stxm_from_rectangle(image): spectral[:, :, 4] = _down(_right(_down(rectangle()))) return spectral -class SideEffect(): + +class SideEffect: def __init__(self, fn): self.fn = fn self.return_value = None @@ -177,17 +185,21 @@ def lineedit_type(le, text): def orange_table_from_3d(image3d): - info = _spectra_from_image(image3d, - range(image3d.shape[2]), - range(image3d[:, :, 0].shape[1]), - range(image3d[:, :, 0].shape[0])) + info = _spectra_from_image( + image3d, + range(image3d.shape[2]), + range(image3d[:, :, 0].shape[1]), + range(image3d[:, :, 0].shape[0]), + ) data = build_spec_table(*info) return data stxm_diamond = orange_table_from_3d(fake_stxm_from_image(diamond())) stxm_rectangle = orange_table_from_3d(fake_stxm_from_rectangle(rectangle())) -stxm_rectangle_short = orange_table_from_3d(fake_stxm_from_rectangle(rectangle())[:, :, :3]) +stxm_rectangle_short = orange_table_from_3d( + fake_stxm_from_rectangle(rectangle())[:, :, :3] +) def orange_table_to_3d(data): @@ -196,7 +208,7 @@ def orange_table_to_3d(data): miny = int(min(data.metas[:, 1])) maxx = int(max(data.metas[:, 0])) maxy = int(max(data.metas[:, 1])) - image3d = np.ones((maxy-miny+1, maxx-minx+1, nz)) * np.nan + image3d = np.ones((maxy - miny + 1, maxx - minx + 1, nz)) * np.nan for d in data: x, y = int(d.metas[0]), int(d.metas[1]) image3d[y - miny, x - minx, :] = d.x @@ -204,7 +216,6 @@ def orange_table_to_3d(data): class TestOWStackAlign(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWStackAlign) # type: OWStackAlign @@ -222,7 +233,7 @@ def test_output_aligned(self): image3d = orange_table_to_3d(out) for z in range(1, image3d.shape[2]): np.testing.assert_almost_equal(image3d[:, :, 0], image3d[:, :, z]) - + def test_output_aligned_with_ref(self): self.send_signal(self.widget.Inputs.data, stxm_diamond) self.send_signal(self.widget.Inputs.refdata, stxm_rectangle) @@ -245,8 +256,10 @@ def test_output_cropped(self): np.testing.assert_almost_equal(image3d[:, :, 0], diamond()[1:-2, :-1]) def test_sobel_called(self): - with patch("orangecontrib.spectroscopy.widgets.owstackalign.sobel", - Mock(side_effect=sobel)) as mock: + with patch( + "orangecontrib.spectroscopy.widgets.owstackalign.sobel", + Mock(side_effect=sobel), + ) as mock: self.send_signal(self.widget.Inputs.data, stxm_diamond) _ = self.get_output(self.widget.Outputs.newstack) self.assertFalse(mock.called) @@ -271,7 +284,9 @@ def test_with_class_columns(self): data = stxm_diamond cv = DiscreteVariable(name="class", values=["a", "b"]) z = ContinuousVariable(name="z") - domain = Domain(data.domain.attributes, class_vars=[cv], metas=data.domain.metas + (z,)) + domain = Domain( + data.domain.attributes, class_vars=[cv], metas=data.domain.metas + (z,) + ) data = data.transform(domain) self.send_signal(self.widget.Inputs.data, data) out = self.get_output(self.widget.Outputs.newstack) @@ -288,7 +303,7 @@ def test_invalid_axis(self): self.assertTrue(self.widget.Error.invalid_axis.is_shown()) self.send_signal(self.widget.Inputs.data, None) self.assertFalse(self.widget.Error.invalid_axis.is_shown()) - + def test_reference_warning(self): data = stxm_diamond # only Data is set, reference input not used = No warning @@ -316,12 +331,16 @@ def test_missing_metas(self): self.send_signal(self.widget.Inputs.data, data) def test_no_wavenumbers(self): - domain = Domain(stxm_diamond.domain.attributes[:0], metas=stxm_diamond.domain.metas) + domain = Domain( + stxm_diamond.domain.attributes[:0], metas=stxm_diamond.domain.metas + ) data = stxm_diamond.transform(domain) self.send_signal(self.widget.Inputs.data, data) def test_single_wavenumber(self): - domain = Domain(stxm_diamond.domain.attributes[:1], metas=stxm_diamond.domain.metas) + domain = Domain( + stxm_diamond.domain.attributes[:1], metas=stxm_diamond.domain.metas + ) data = stxm_diamond.transform(domain) self.send_signal(self.widget.Inputs.data, data) out = self.get_output(self.widget.Outputs.newstack) @@ -351,8 +370,10 @@ def test_frame_limits(self): def test_frame_shifts(self): se = SideEffect(process_stack) - with patch("orangecontrib.spectroscopy.widgets.owstackalign.process_stack", - Mock(side_effect=se)) as mock: + with patch( + "orangecontrib.spectroscopy.widgets.owstackalign.process_stack", + Mock(side_effect=se), + ) as mock: self.send_signal(self.widget.Inputs.data, stxm_diamond) lineedit_type(self.widget.controls.ref_frame_num, "1") self.assertEqual(2, mock.call_count) diff --git a/orangecontrib/spectroscopy/tests/test_owaverage.py b/orangecontrib/spectroscopy/tests/test_owaverage.py index 8a7b7f0de..6a67ba405 100644 --- a/orangecontrib/spectroscopy/tests/test_owaverage.py +++ b/orangecontrib/spectroscopy/tests/test_owaverage.py @@ -5,7 +5,6 @@ class TestOWAverage(WidgetTest): - @classmethod def setUpClass(cls): super().setUpClass() @@ -59,9 +58,11 @@ def test_average_by_group_metas(self): c_domain = self.collagen.domain str_var = Orange.data.StringVariable.make(name="stringtest") time_var = Orange.data.TimeVariable.make(name="timetest") - n_domain = Orange.data.Domain(c_domain.attributes, - c_domain.class_vars, - [Orange.data.ContinuousVariable("con"), str_var, time_var]) + n_domain = Orange.data.Domain( + c_domain.attributes, + c_domain.class_vars, + [Orange.data.ContinuousVariable("con"), str_var, time_var], + ) collagen = self.collagen.transform(n_domain) with collagen.unlocked(collagen.metas): collagen.metas[:, 0] = np.atleast_2d(collagen.X[:, 0]) @@ -118,9 +119,9 @@ def test_average_by_group_objectvar(self): gvar = self.collagen.domain.class_var c_domain = self.collagen.domain str_var = Orange.data.StringVariable.make(name="stringtest") - n_domain = Orange.data.Domain(c_domain.attributes, - None, - [c_domain.class_var, str_var]) + n_domain = Orange.data.Domain( + c_domain.attributes, None, [c_domain.class_var, str_var] + ) collagen = self.collagen.transform(n_domain) # collagen.metas[:, 1] = np.atleast_2d(self.collagen.Y) self.send_signal("Data", collagen) diff --git a/orangecontrib/spectroscopy/tests/test_owbin.py b/orangecontrib/spectroscopy/tests/test_owbin.py index 19e1331e9..80fe9b867 100644 --- a/orangecontrib/spectroscopy/tests/test_owbin.py +++ b/orangecontrib/spectroscopy/tests/test_owbin.py @@ -6,7 +6,6 @@ class TestOWBin(WidgetTest): - @classmethod def setUpClass(cls): super().setUpClass() @@ -21,7 +20,7 @@ def test_load_unload(self): def test_bin(self): self.widget.bin_shape = (2, 2) - self.widget._init_bins + self.widget._init_bins() self.send_signal(self.widget.Inputs.data, self.mosaic) m = self.get_output(self.widget.Outputs.bindata) np.testing.assert_equal(len(m.X), len(self.mosaic.X) / 2**2) @@ -29,8 +28,14 @@ def test_bin(self): x_coords_binned = np.array([x_coords[0:2].mean(), x_coords[2:4].mean()]) np.testing.assert_equal(m[:, "map_x"].metas[::4, 0], x_coords_binned) y_coords = self.mosaic[:, "map_y"].metas[:, 0] - y_coords_binned = np.array([y_coords[0:8].mean(), y_coords[8:16].mean(), - y_coords[16:24].mean(), y_coords[24:32].mean()]) + y_coords_binned = np.array( + [ + y_coords[0:8].mean(), + y_coords[8:16].mean(), + y_coords[16:24].mean(), + y_coords[24:32].mean(), + ] + ) np.testing.assert_equal(m[:, "map_y"].metas[0:4, 0], y_coords_binned) def test_bin_changed(self): diff --git a/orangecontrib/spectroscopy/tests/test_owcos.py b/orangecontrib/spectroscopy/tests/test_owcos.py index 4a9e8ad50..e9a76b5ab 100644 --- a/orangecontrib/spectroscopy/tests/test_owcos.py +++ b/orangecontrib/spectroscopy/tests/test_owcos.py @@ -21,23 +21,26 @@ def setUp(self): def test_calc_cos(self): cos = calc_cos(self.DATA1, self.DATA2) - numpy.testing.assert_array_equal(cos[0], [[6., 6., 6.], - [6., 8., 4.], - [6., 4., 8.]]) + numpy.testing.assert_array_equal( + cos[0], [[6.0, 6.0, 6.0], [6.0, 8.0, 4.0], [6.0, 4.0, 8.0]] + ) - numpy.testing.assert_array_almost_equal(cos[1], [[0., -0.95492966, 0.95492966], - [1.27323954, 0.31830989, 2.2281692], - [-1.27323954, -2.2281692, -0.31830989]]) + numpy.testing.assert_array_almost_equal( + cos[1], + [ + [0.0, -0.95492966, 0.95492966], + [1.27323954, 0.31830989, 2.2281692], + [-1.27323954, -2.2281692, -0.31830989], + ], + ) - numpy.testing.assert_array_equal(cos[2], [[-1.5, -0.5, -2.5], - [1.5, 2.5, 0.5]]) + numpy.testing.assert_array_equal(cos[2], [[-1.5, -0.5, -2.5], [1.5, 2.5, 0.5]]) - numpy.testing.assert_array_equal(cos[3], [[-2., -1., -3.], - [2., 3., 1.]]) + numpy.testing.assert_array_equal(cos[3], [[-2.0, -1.0, -3.0], [2.0, 3.0, 1.0]]) - numpy.testing.assert_array_equal(cos[4], [1., 2., 3.]) + numpy.testing.assert_array_equal(cos[4], [1.0, 2.0, 3.0]) - numpy.testing.assert_array_equal(cos[5], [1., 2., 3.]) + numpy.testing.assert_array_equal(cos[5], [1.0, 2.0, 3.0]) def test_sort_data(self): sorted_data = sort_data(self.DATA1) diff --git a/orangecontrib/spectroscopy/tests/test_owfft.py b/orangecontrib/spectroscopy/tests/test_owfft.py index c65dcbd3e..2a9b8c62e 100644 --- a/orangecontrib/spectroscopy/tests/test_owfft.py +++ b/orangecontrib/spectroscopy/tests/test_owfft.py @@ -11,7 +11,6 @@ class TestOWFFT(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWFFT) self.ifg_single = Orange.data.Table("IFG_single.dpt") @@ -25,14 +24,14 @@ def test_load_unload(self): self.send_signal("Interferogram", None) def test_laser_metadata(self): - """ Test dx in presence/absence of laser metadata """ + """Test dx in presence/absence of laser metadata""" self.send_signal("Interferogram", self.ifg_seq) - self.assertEqual(self.widget.dx, (1 / 1.57980039e+04 / 2) * 4) + self.assertEqual(self.widget.dx, (1 / 1.57980039e04 / 2) * 4) self.send_signal("Interferogram", self.ifg_single) self.assertEqual(self.widget.dx, (1 / DEFAULT_HENE / 2)) def test_respect_custom_dx(self): - """ Setting new data should not overwrite custom dx value """ + """Setting new data should not overwrite custom dx value""" self.send_signal("Interferogram", self.ifg_single) self.widget.dx_auto = False self.widget.dx = 5 @@ -48,7 +47,7 @@ def test_respect_custom_dx(self): def test_auto_dx(self): self.send_signal("Interferogram", self.ifg_seq) - self.assertEqual(self.widget.dx, (1 / 1.57980039e+04 / 2) * 4) + self.assertEqual(self.widget.dx, (1 / 1.57980039e04 / 2) * 4) self.send_signal("Interferogram", self.ifg_gsf) self.assertEqual(self.widget.dx, (0.00019550342130987293)) @@ -59,10 +58,10 @@ def test_keep_metas(self): spectra = self.get_output(self.widget.Outputs.spectra) phases = self.get_output(self.widget.Outputs.phases) np.testing.assert_equal(input.metas, spectra.metas) - np.testing.assert_equal(input.metas, phases.metas[:, :input.metas.shape[1]]) + np.testing.assert_equal(input.metas, phases.metas[:, : input.metas.shape[1]]) def test_custom_zpd(self): - """ Test setting custom zpd value""" + """Test setting custom zpd value""" custom_zpd = 1844 self.send_signal(self.widget.Inputs.data, self.ifg_single) self.widget.peak_search_enable = False @@ -73,26 +72,26 @@ def test_custom_zpd(self): self.assertEqual(phases[0, "zpd_fwd"], custom_zpd) def test_chunk_one(self): - """ Test batching when len(data) < chunk_size """ + """Test batching when len(data) < chunk_size""" self.assertLess(len(self.ifg_seq), CHUNK_SIZE) self.send_signal(self.widget.Inputs.data, self.ifg_seq) self.widget.peak_search_enable = False - self.widget.zpd1 = 69 # TODO replace with value read from file + self.widget.zpd1 = 69 # TODO replace with value read from file self.widget.peak_search_changed() self.commit_and_wait() def test_chunk_many(self): - """ Test batching when len(data) >> chunk_size """ + """Test batching when len(data) >> chunk_size""" data = Orange.data.table.Table.concatenate(5 * (self.ifg_seq,)) self.assertGreater(len(data), CHUNK_SIZE) self.send_signal(self.widget.Inputs.data, data) self.widget.peak_search_enable = False - self.widget.zpd1 = 69 # TODO replace with value read from file + self.widget.zpd1 = 69 # TODO replace with value read from file self.widget.peak_search_changed() self.commit_and_wait() def test_calculation(self): - """" Test calculation with custom settings and batching """ + """ " Test calculation with custom settings and batching""" ifg_ref = Orange.data.Table("agilent/background_agg256.seq") abs = Orange.data.Table("agilent/4_noimage_agg256.dat") @@ -102,7 +101,7 @@ def test_calculation(self): self.widget.phase_corr = irfft.PhaseCorrection.MERTZ self.widget.setting_changed() self.widget.peak_search_enable = False - self.widget.zpd1 = 69 # TODO replace with value read from file + self.widget.zpd1 = 69 # TODO replace with value read from file self.widget.peak_search_changed() self.send_signal(self.widget.Inputs.data, ifg_ref) @@ -119,24 +118,28 @@ def test_calculation(self): abs_x = getx(abs) calc_x = getx(ssc) limits = np.searchsorted(calc_x, [abs_x[0] - 1, abs_x[-1]]) - np.testing.assert_allclose(calc_x[limits[0]:limits[1]], abs_x) + np.testing.assert_allclose(calc_x[limits[0] : limits[1]], abs_x) # Compare to agilent absorbance # NB 4 mAbs error - np.testing.assert_allclose(calc_abs[:, limits[0]:limits[1]], abs.X, atol=0.004) + np.testing.assert_allclose( + calc_abs[:, limits[0] : limits[1]], abs.X, atol=0.004 + ) def test_complex_calculation(self): - """" Test calculation Complex FFT """ + """ " Test calculation Complex FFT""" self.widget.zff = 2 # 2**2 = 4 self.widget.limit_output = False - self.widget.peak_search = 1 # MINIMUM - self.widget.apod_func = 0 # boxcar + self.widget.peak_search = 1 # MINIMUM + self.widget.apod_func = 0 # boxcar self.send_signal(self.widget.Inputs.data, self.ifg_gsf) self.commit_and_wait() # testing info panel text associated with the input file metadata widget_text = self.widget.info_dx.text() - self.assertIn('Using Calculated Datapoint Spacing (Δx) from metadata', widget_text) + self.assertIn( + 'Using Calculated Datapoint Spacing (Δx) from metadata', widget_text + ) self.assertTrue(self.widget.use_interleaved_data) self.assertTrue(self.widget.complexfft) @@ -144,8 +147,12 @@ def test_complex_calculation(self): phases = self.get_output(self.widget.Outputs.phases) np.testing.assert_allclose(spectra.X.size, (2049)) np.testing.assert_allclose(phases.X.size, (2049)) - np.testing.assert_allclose(spectra.X[0, 396:399], (23.67618359, 25.02051088, 25.82566789)) - np.testing.assert_allclose(phases.X[0, 396:399], (2.61539453, 2.65495979, 2.72814989)) + np.testing.assert_allclose( + spectra.X[0, 396:399], (23.67618359, 25.02051088, 25.82566789) + ) + np.testing.assert_allclose( + phases.X[0, 396:399], (2.61539453, 2.65495979, 2.72814989) + ) def test_migrate_HeNe(self): settings = {"dx_HeNe": False} diff --git a/orangecontrib/spectroscopy/tests/test_owhyper.py b/orangecontrib/spectroscopy/tests/test_owhyper.py index 4ff0b63d7..1ea9c5083 100644 --- a/orangecontrib/spectroscopy/tests/test_owhyper.py +++ b/orangecontrib/spectroscopy/tests/test_owhyper.py @@ -22,16 +22,22 @@ from orangecontrib.spectroscopy.data import _spectra_from_image, build_spec_table from orangecontrib.spectroscopy.io.util import VisibleImage -from orangecontrib.spectroscopy.preprocess.integrate import IntegrateFeaturePeakSimple, \ - Integrate, IntegrateFeatureSimple +from orangecontrib.spectroscopy.preprocess.integrate import ( + IntegrateFeaturePeakSimple, + Integrate, + IntegrateFeatureSimple, +) from orangecontrib.spectroscopy.widgets import owhyper -from orangecontrib.spectroscopy.widgets.owhyper import \ - OWHyper +from orangecontrib.spectroscopy.widgets.owhyper import OWHyper from orangecontrib.spectroscopy.preprocess import Interpolate from orangecontrib.spectroscopy.widgets.line_geometry import in_polygon, is_left from orangecontrib.spectroscopy.tests.util import hold_modifiers, set_png_graph_save -from orangecontrib.spectroscopy.utils import values_to_linspace, \ - index_values, location_values, index_values_nan +from orangecontrib.spectroscopy.utils import ( + values_to_linspace, + index_values, + location_values, + index_values_nan, +) NAN = float("nan") @@ -42,7 +48,6 @@ def wait_for_image(widget, timeout=5000): class TestReadCoordinates(unittest.TestCase): - def test_linspace(self): v = values_to_linspace(np.array([1, 2, 3])) np.testing.assert_equal(np.linspace(*v), [1, 2, 3]) @@ -81,7 +86,6 @@ def test_location(self): class TestPolygonSelection(unittest.TestCase): - def test_is_left(self): self.assertGreater(is_left(0, 0, 0, 1, -1, 0), 0) self.assertLess(is_left(0, 0, 0, -1, -1, 0), 0) @@ -106,7 +110,6 @@ def test_order(self): class TestOWHyper(WidgetTest): - @classmethod def setUpClass(cls): super().setUpClass() @@ -125,7 +128,8 @@ def setUpClass(cls): irisunknown = Interpolate(np.arange(20))(cls.iris)[:20] # dataset without any attributes, but XY whitelight0 = cls.whitelight.transform( - Orange.data.Domain([], None, metas=cls.whitelight.domain.metas))[:100] + Orange.data.Domain([], None, metas=cls.whitelight.domain.metas) + )[:100] unknowns = cls.iris[::10].copy() with unknowns.unlocked(): unknowns.X[:, :] = float("nan") @@ -133,8 +137,17 @@ def setUpClass(cls): unknown_pixel = single_pixel.copy() with unknown_pixel.unlocked(): unknown_pixel.Y[:] = float("nan") - cls.strange_data = [None, cls.iris1, iris0, empty, irisunknown, whitelight0, - unknowns, single_pixel, unknown_pixel] + cls.strange_data = [ + None, + cls.iris1, + iris0, + empty, + irisunknown, + whitelight0, + unknowns, + single_pixel, + unknown_pixel, + ] def setUp(self): self.widget = self.create_widget(OWHyper) # type: OWHyper @@ -167,7 +180,9 @@ def test_integral_lines(self): correct = [True, True, False, True, True] else: correct = [True, True, False, False, False] - visible = [a.isVisible() for a in [w.line1, w.line2, w.line3, w.line4, w.line5]] + visible = [ + a.isVisible() for a in [w.line1, w.line2, w.line3, w.line4, w.line5] + ] self.assertEqual(visible, correct) def try_big_selection(self): @@ -235,13 +250,16 @@ def test_select_all(self): self.assertIsNone(out, None) # select specific points - self.widget.imageplot.select_square(QPointF(53.20, 30.0), QPointF(53.205, 30.02)) + self.widget.imageplot.select_square( + QPointF(53.20, 30.0), QPointF(53.205, 30.02) + ) out = self.get_output("Selection") - np.testing.assert_almost_equal(out.metas, - [[53.2043, 30.0185], [53.2043, 30.0085]], - decimal=3) - np.testing.assert_equal([o[out.domain["Group"]].value for o in out], - ["G1", "G1"]) + np.testing.assert_almost_equal( + out.metas, [[53.2043, 30.0185], [53.2043, 30.0085]], decimal=3 + ) + np.testing.assert_equal( + [o[out.domain["Group"]].value for o in out], ["G1", "G1"] + ) def test_select_polygon_as_rectangle(self): # rectangle and a polygon need to give the same results @@ -249,8 +267,15 @@ def test_select_polygon_as_rectangle(self): wait_for_image(self.widget) self.widget.imageplot.select_square(QPointF(53, 30), QPointF(54, 31)) out = self.get_output("Selection") - self.widget.imageplot.select_polygon([QPointF(53, 30), QPointF(53, 31), QPointF(54, 31), - QPointF(54, 30), QPointF(53, 30)]) + self.widget.imageplot.select_polygon( + [ + QPointF(53, 30), + QPointF(53, 31), + QPointF(54, 31), + QPointF(54, 30), + QPointF(53, 30), + ] + ) outpoly = self.get_output("Selection") self.assertEqual(list(out), list(outpoly)) @@ -290,24 +315,35 @@ def test_select_click_multiple_groups(self): oldvars = data.domain.variables + data.domain.metas group_at = [a for a in newvars if a not in oldvars][0] unselected = group_at.to_val("Unselected") - out = out[np.asarray(np.flatnonzero( - out.transform(Orange.data.Domain([group_at])).X != unselected))] + out = out[ + np.asarray( + np.flatnonzero( + out.transform(Orange.data.Domain([group_at])).X != unselected + ) + ) + ] self.assertEqual(len(out), 4) - np.testing.assert_almost_equal([o["map_x"].value for o in out], - [53.1993, 53.3993, 53.5993, 53.7993], - decimal=3) - np.testing.assert_equal([o[group_at].value for o in out], ["G1", "G2", "G3", "G3"]) + np.testing.assert_almost_equal( + [o["map_x"].value for o in out], + [53.1993, 53.3993, 53.5993, 53.7993], + decimal=3, + ) + np.testing.assert_equal( + [o[group_at].value for o in out], ["G1", "G2", "G3", "G3"] + ) out = self.get_output(self.widget.Outputs.selected_data) - np.testing.assert_equal([o[out.domain["Group"]].value for o in out], - ["G1", "G2", "G3", "G3"]) + np.testing.assert_equal( + [o[out.domain["Group"]].value for o in out], ["G1", "G2", "G3", "G3"] + ) # remove one element with hold_modifiers(self.widget, Qt.AltModifier): self.widget.imageplot.select_by_click(QPointF(53.2, 30)) out = self.get_output(self.widget.Outputs.selected_data) np.testing.assert_equal(len(out), 3) - np.testing.assert_equal([o[out.domain["Group"]].value for o in out], - ["G2", "G3", "G3"]) + np.testing.assert_equal( + [o[out.domain["Group"]].value for o in out], ["G2", "G3", "G3"] + ) def test_select_a_curve(self): self.send_signal("Data", self.iris) @@ -323,29 +359,38 @@ def test_settings_curves(self): def test_set_variable_color(self): data = self.iris - ndom = Orange.data.Domain(data.domain.attributes[:-1], data.domain.class_var, - metas=[data.domain.attributes[-1]]) + ndom = Orange.data.Domain( + data.domain.attributes[:-1], + data.domain.class_var, + metas=[data.domain.attributes[-1]], + ) data = data.transform(ndom) self.send_signal("Data", data) self.widget.controls.value_type.buttons[1].click() - with patch("orangecontrib.spectroscopy.widgets.owhyper.ImageItemNan.setLookupTable") as p: + with patch( + "orangecontrib.spectroscopy.widgets.owhyper.ImageItemNan.setLookupTable" + ) as p: # a discrete variable self.widget.attr_value = data.domain["iris"] self.widget.imageplot.update_color_schema() self.widget.update_feature_value() wait_for_image(self.widget) - np.testing.assert_equal(len(p.call_args[0][0]), 3) # just 3 colors for 3 values + np.testing.assert_equal( + len(p.call_args[0][0]), 3 + ) # just 3 colors for 3 values # a continuous variable self.widget.attr_value = data.domain["petal width"] self.widget.imageplot.update_color_schema() self.widget.update_feature_value() wait_for_image(self.widget) - np.testing.assert_equal(len(p.call_args[0][0]), 256) # 256 for a continuous variable + np.testing.assert_equal( + len(p.call_args[0][0]), 256 + ) # 256 for a continuous variable def test_color_variable_levels(self): class_values = ["a"], ["a", "b", "c"] correct_levels = [0, 0], [0, 2] - for values, correct in zip(class_values, correct_levels): + for values, correct in zip(class_values, correct_levels, strict=True): domain = Domain([], DiscreteVariable("c", values=values)) data = Table.from_numpy(domain, X=[[]], Y=[[0]]) self.send_signal("Data", data) @@ -356,7 +401,9 @@ def test_color_variable_levels(self): np.testing.assert_equal(self.widget.imageplot.img.levels, correct) def test_single_update_view(self): - with patch("orangecontrib.spectroscopy.widgets.owhyper.ImagePlot.update_view") as p: + with patch( + "orangecontrib.spectroscopy.widgets.owhyper.ImagePlot.update_view" + ) as p: self.send_signal("Data", self.iris) self.assertEqual(p.call_count, 1) @@ -370,10 +417,12 @@ def test_correct_legend(self): def test_migrate_selection(self): c = QPointF() # some we set an attribute to - setattr(c, "selection", [False, True, True, False]) + setattr(c, "selection", [False, True, True, False]) # noqa: B010 settings = {"context_settings": [c]} OWHyper.migrate_settings(settings, 2) - self.assertEqual(settings["imageplot"]["selection_group_saved"], [(1, 1), (2, 1)]) + self.assertEqual( + settings["imageplot"]["selection_group_saved"], [(1, 1), (2, 1)] + ) def test_color_no_data(self): self.send_signal("Data", None) @@ -407,34 +456,39 @@ def test_unknown_values_axes(self): def test_migrate_context_feature_color(self): # avoid class_vars in tests, because the setting does not allow them iris = self.iris.transform( - Domain(self.iris.domain.attributes, None, self.iris.domain.class_vars)) - c = self.widget.settingsHandler.new_context(iris.domain, None, iris.domain.metas) + Domain(self.iris.domain.attributes, None, self.iris.domain.class_vars) + ) + c = self.widget.settingsHandler.new_context( + iris.domain, None, iris.domain.metas + ) c.values["curveplot"] = {"feature_color": ("iris", 1)} - self.widget = self.create_widget(OWHyper, - stored_settings={"context_settings": [c]}) + self.widget = self.create_widget( + OWHyper, stored_settings={"context_settings": [c]} + ) self.send_signal("Data", iris) self.assertIsInstance(self.widget.curveplot.feature_color, DiscreteVariable) def test_image_computation(self): - spectra = [[[0, 0, 2, 0], - [0, 0, 1, 0]], - [[1, 2, 2, 0], - [0, 1, 1, 0]]] + spectra = [[[0, 0, 2, 0], [0, 0, 1, 0]], [[1, 2, 2, 0], [0, 1, 1, 0]]] wns = [0, 1, 2, 3] x_locs = [0, 1] y_locs = [0, 1] data = build_spec_table(*_spectra_from_image(spectra, wns, x_locs, y_locs)) def last_called_array(m): - arrays = [a[0][0] for a in m.call_args_list - if a and a[0] and isinstance(a[0][0], np.ndarray)] + arrays = [ + a[0][0] + for a in m.call_args_list + if a and a[0] and isinstance(a[0][0], np.ndarray) + ] return arrays[-1] wrap = self.widget.imageplot.img # integral from zero - self.widget.integration_method = \ - self.widget.integration_methods.index(IntegrateFeatureSimple) + self.widget.integration_method = self.widget.integration_methods.index( + IntegrateFeatureSimple + ) self.send_signal("Data", data) with patch.object(wrap, 'setImage', wraps=wrap.setImage) as m: wait_for_image(self.widget) @@ -443,8 +497,9 @@ def last_called_array(m): np.testing.assert_equal(called.squeeze(), target) # peak from zero - self.widget.integration_method = \ - self.widget.integration_methods.index(IntegrateFeaturePeakSimple) + self.widget.integration_method = self.widget.integration_methods.index( + IntegrateFeaturePeakSimple + ) self.widget._change_integral_type() with patch.object(wrap, 'setImage', wraps=wrap.setImage) as m: wait_for_image(self.widget) @@ -476,10 +531,7 @@ def last_called_array(m): np.testing.assert_equal(called, target) def test_scatterplot_computation(self): - spectra = [[[0, 0, 2, 0], - [0, 0, 1, 0]], - [[1, 2, 2, 0], - [0, 1, 1, 0]]] + spectra = [[[0, 0, 2, 0], [0, 0, 1, 0]], [[1, 2, 2, 0], [0, 1, 1, 0]]] wns = [0, 1, 2, 3] x_locs = [0, 1] y_locs = [0, 1] @@ -497,8 +549,9 @@ def colors_from_brush(brushes): self.widget.imageplot.controls.draw_as_scatterplot.click() # integral from zero - self.widget.integration_method = \ - self.widget.integration_methods.index(IntegrateFeatureSimple) + self.widget.integration_method = self.widget.integration_methods.index( + IntegrateFeatureSimple + ) self.send_signal("Data", data) with patch.object(wrap, 'setData', wraps=wrap.setData) as m: wait_for_image(self.widget) @@ -507,8 +560,10 @@ def colors_from_brush(brushes): np.testing.assert_equal(call.kwargs['x'], [0, 1, 0, 1]) np.testing.assert_equal(call.kwargs['y'], [0, 0, 1, 1]) # the current state hardcoded - np.testing.assert_equal(colors_from_brush(call.kwargs['brush']), - [(0, 177, 80), (0, 124, 12), (255, 35, 241), (0, 177, 80)]) + np.testing.assert_equal( + colors_from_brush(call.kwargs['brush']), + [(0, 177, 80), (0, 124, 12), (255, 35, 241), (0, 177, 80)], + ) # single wavenumber (feature) self.widget.controls.value_type.buttons[1].click() @@ -521,8 +576,10 @@ def colors_from_brush(brushes): np.testing.assert_equal(call.kwargs['x'], [0, 1, 0, 1]) np.testing.assert_equal(call.kwargs['y'], [0, 0, 1, 1]) # the current state hardcoded - np.testing.assert_equal(colors_from_brush(call.kwargs['brush']), - [(0, 124, 12), (0, 124, 12), (255, 35, 241), (27, 97, 142)]) + np.testing.assert_equal( + colors_from_brush(call.kwargs['brush']), + [(0, 124, 12), (0, 124, 12), (255, 35, 241), (27, 97, 142)], + ) # RGB self.widget.controls.value_type.buttons[2].click() @@ -536,20 +593,28 @@ def colors_from_brush(brushes): call = m.call_args_list[-1] np.testing.assert_equal(call.kwargs['x'], [0, 1, 0, 1]) np.testing.assert_equal(call.kwargs['y'], [0, 0, 1, 1]) - np.testing.assert_equal(colors_from_brush(call.kwargs['brush']), - [(0, 255, 0), (0, 0, 0), (255, 255, 255), (0, 0, 128)]) + np.testing.assert_equal( + colors_from_brush(call.kwargs['brush']), + [(0, 255, 0), (0, 0, 0), (255, 255, 255), (0, 0, 128)], + ) def test_migrate_visual_setttings(self): - settings = {"curveplot": - {"label_title": "title", - "label_xaxis": "x", - "label_yaxis": "y"} - } + settings = { + "curveplot": { + "label_title": "title", + "label_xaxis": "x", + "label_yaxis": "y", + } + } OWHyper.migrate_settings(settings, 6) - self.assertEqual(settings["visual_settings"], - {('Annotations', 'Title', 'Title'): 'title', - ('Annotations', 'x-axis title', 'Title'): 'x', - ('Annotations', 'y-axis title', 'Title'): 'y'}) + self.assertEqual( + settings["visual_settings"], + { + ('Annotations', 'Title', 'Title'): 'title', + ('Annotations', 'x-axis title', 'Title'): 'x', + ('Annotations', 'y-axis title', 'Title'): 'y', + }, + ) settings = {} OWHyper.migrate_settings(settings, 6) self.assertNotIn("visual_settings", settings) @@ -568,7 +633,6 @@ def test_compat_no_group(self): self.assertTrue(self.widget.compat_no_group) - @unittest.skipUnless(dask, "installed Orange does not support dask") class TestOWHyperWithDask(TestOWHyper): @classmethod @@ -578,12 +642,13 @@ def setUpClass(cls): cls.whitelight = temp_dasktable("whitelight.gsf") cls.whitelight_unknown = temp_dasktable(cls.whitelight_unknown) cls.iris1 = temp_dasktable(cls.iris1) - cls.strange_data = [temp_dasktable(d) if d is not None else None - for d in cls.strange_data] + cls.strange_data = [ + temp_dasktable(d) if d is not None else None for d in cls.strange_data + ] class _VisibleImageStream(VisibleImage): - """ Do not use this class in practice because too many things + """Do not use this class in practice because too many things will get copied when transforming tables.""" def __init__(self, name, pos_x, pos_y, size_x, size_y, stream): @@ -596,17 +661,24 @@ def image(self): class TestVisibleImage(WidgetTest): - @classmethod def mock_visible_image_data_oldformat(cls): - red_img = io.BytesIO(b64decode("iVBORw0KGgoAAAANSUhEUgAAAA" - "oAAAAKCAYAAACNMs+9AAAAFUlE" - "QVR42mP8z8AARIQB46hC+ioEAG" - "X8E/cKr6qsAAAAAElFTkSuQmCC")) - black_img = io.BytesIO(b64decode("iVBORw0KGgoAAAANSUhEUgAAA" - "AoAAAAKCAQAAAAnOwc2AAAAEU" - "lEQVR42mNk+M+AARiHsiAAcCI" - "KAYwFoQ8AAAAASUVORK5CYII=")) + red_img = io.BytesIO( + b64decode( + "iVBORw0KGgoAAAANSUhEUgAAAA" + "oAAAAKCAYAAACNMs+9AAAAFUlE" + "QVR42mP8z8AARIQB46hC+ioEAG" + "X8E/cKr6qsAAAAAElFTkSuQmCC" + ) + ) + black_img = io.BytesIO( + b64decode( + "iVBORw0KGgoAAAANSUhEUgAAA" + "AoAAAAKCAQAAAAnOwc2AAAAEU" + "lEQVR42mNk+M+AARiHsiAAcCI" + "KAYwFoQ8AAAAASUVORK5CYII=" + ) + ) return [ { @@ -615,7 +687,7 @@ def mock_visible_image_data_oldformat(cls): "pos_x": 100, "pos_y": 100, "pixel_size_x": 1.7, - "pixel_size_y": 2.3 + "pixel_size_y": 2.3, }, { "name": "Image 02", @@ -623,7 +695,7 @@ def mock_visible_image_data_oldformat(cls): "pos_x": 0.5, "pos_y": 0.5, "pixel_size_x": 1, - "pixel_size_y": 0.3 + "pixel_size_y": 0.3, }, { "name": "Image 03", @@ -631,35 +703,42 @@ def mock_visible_image_data_oldformat(cls): "pos_x": 100, "pos_y": 100, "img_size_x": 17.0, - "img_size_y": 23.0 + "img_size_y": 23.0, }, ] @classmethod def mock_visible_image_data(cls): - red_img = io.BytesIO(b64decode("iVBORw0KGgoAAAANSUhEUgAAAA" - "oAAAAKCAYAAACNMs+9AAAAFUlE" - "QVR42mP8z8AARIQB46hC+ioEAG" - "X8E/cKr6qsAAAAAElFTkSuQmCC")) - black_img = io.BytesIO(b64decode("iVBORw0KGgoAAAANSUhEUgAAA" - "AoAAAAKCAQAAAAnOwc2AAAAEU" - "lEQVR42mNk+M+AARiHsiAAcCI" - "KAYwFoQ8AAAAASUVORK5CYII=")) + red_img = io.BytesIO( + b64decode( + "iVBORw0KGgoAAAANSUhEUgAAAA" + "oAAAAKCAYAAACNMs+9AAAAFUlE" + "QVR42mP8z8AARIQB46hC+ioEAG" + "X8E/cKr6qsAAAAAElFTkSuQmCC" + ) + ) + black_img = io.BytesIO( + b64decode( + "iVBORw0KGgoAAAANSUhEUgAAA" + "AoAAAAKCAQAAAAnOwc2AAAAEU" + "lEQVR42mNk+M+AARiHsiAAcCI" + "KAYwFoQ8AAAAASUVORK5CYII=" + ) + ) return [ - _VisibleImageStream("Image 01", 100, 100, 17., 23., red_img), + _VisibleImageStream("Image 01", 100, 100, 17.0, 23.0, red_img), _VisibleImageStream("Image 02", 0.5, 0.5, 10, 3, black_img), - _VisibleImageStream("Image 03", 100, 100, 17., 23., red_img), + _VisibleImageStream("Image 03", 100, 100, 17.0, 23.0, red_img), ] @classmethod def setUpClass(cls): super().setUpClass() - cls.data_with_visible_images = Orange.data.Table( - "agilent/4_noimage_agg256.dat" - ) - cls.data_with_visible_images.attributes["visible_images"] = \ + cls.data_with_visible_images = Orange.data.Table("agilent/4_noimage_agg256.dat") + cls.data_with_visible_images.attributes["visible_images"] = ( cls.mock_visible_image_data() + ) def setUp(self): self.widget = self.create_widget(OWHyper) # type: OWHyper @@ -667,8 +746,7 @@ def setUp(self): def assert_same_visible_image(self, img_info, vis_img, mock_rect): img = img_info.image.convert('RGBA') img = np.array(img)[::-1] - rect = QRectF(img_info.pos_x, img_info.pos_y, - img_info.size_x, img_info.size_y) + rect = QRectF(img_info.pos_x, img_info.pos_y, img_info.size_x, img_info.size_y) self.assertTrue((vis_img.image == img).all()) mock_rect.assert_called_with(rect) @@ -693,9 +771,11 @@ def test_controls_enabled_by_show_chkbox(self): self.assertTrue(w.controls.show_visible_image.isEnabled()) self.assertFalse(w.show_visible_image) - controls = [w.controls.visible_image, - w.controls.visible_image_composition, - w.controls.visible_image_opacity] + controls = [ + w.controls.visible_image, + w.controls.visible_image_composition, + w.controls.visible_image_opacity, + ] for control in controls: self.assertFalse(control.isEnabled()) @@ -712,15 +792,16 @@ def test_first_visible_image_selected_in_combobox_by_default(self): wait_for_image(w) w.controls.show_visible_image.setChecked(True) - self.assertEqual(len(w.visible_image_model), - len(data.attributes["visible_images"])) + self.assertEqual( + len(w.visible_image_model), len(data.attributes["visible_images"]) + ) self.assertEqual(w.visible_image, data.attributes["visible_images"][0]) self.assertEqual(w.controls.visible_image.currentIndex(), 0) self.assertEqual(w.controls.visible_image.currentText(), "Image 01") - self.assert_same_visible_image(data.attributes["visible_images"][0], - w.imageplot.vis_img, - mock_rect) + self.assert_same_visible_image( + data.attributes["visible_images"][0], w.imageplot.vis_img, mock_rect + ) def test_visible_image_displayed(self): w = self.widget @@ -764,9 +845,9 @@ def test_select_another_visible_image(self): # we need to trigger it by hand here. w.controls.visible_image.activated.emit(1) - self.assert_same_visible_image(data.attributes["visible_images"][1], - w.imageplot.vis_img, - mock_rect) + self.assert_same_visible_image( + data.attributes["visible_images"][1], w.imageplot.vis_img, mock_rect + ) def test_visible_image_opacity(self): w = self.widget @@ -811,16 +892,13 @@ def test_visible_image_img_size(self): # we need to trigger it by hand here. w.controls.visible_image.activated.emit(2) - self.assert_same_visible_image(data.attributes["visible_images"][0], - w.imageplot.vis_img, - mock_rect) + self.assert_same_visible_image( + data.attributes["visible_images"][0], w.imageplot.vis_img, mock_rect + ) def test_oldformat(self): - data = Orange.data.Table( - "agilent/4_noimage_agg256.dat" - ) - data.attributes["visible_images"] = \ - self.mock_visible_image_data_oldformat() + data = Orange.data.Table("agilent/4_noimage_agg256.dat") + data.attributes["visible_images"] = self.mock_visible_image_data_oldformat() w = self.widget with self.assertWarns(OrangeDeprecationWarning): @@ -837,7 +915,6 @@ def test_oldformat(self): class TestVectorPlot(WidgetTest): - @classmethod def setUpClass(cls): super().setUpClass() @@ -914,7 +991,7 @@ def test_legend(self): self.assertFalse(self.widget.imageplot.vect_legend.isVisible()) self.widget.controls.imageplot.show_vector_plot.setChecked(False) self.widget.imageplot.enable_vector() - + def test_vect_color(self): feat = self.iris.get_column(self.iris.domain.attributes[0]) self.send_signal(self.widget.Inputs.data, self.iris) @@ -926,7 +1003,9 @@ def test_vect_color(self): self.assertEqual(len(self.widget.imageplot.get_vector_color(feat)), 4) self.widget.imageplot.vector_color_index = 8 self.widget.imageplot._update_vector() - self.assertEqual(self.widget.imageplot.get_vector_color(feat)[0].shape, (feat.shape[0], 4)) + self.assertEqual( + self.widget.imageplot.get_vector_color(feat)[0].shape, (feat.shape[0], 4) + ) self.widget.controls.imageplot.show_vector_plot.setChecked(False) self.widget.imageplot.enable_vector() @@ -943,10 +1022,14 @@ def test_vect_bin(self): self.widget.imageplot.update_view() wait_for_image(self.widget) print(self.widget.imageplot.vector_plot.params[0].shape) - self.assertEqual(self.widget.imageplot.vector_plot.params[0].shape[0], - self.iris.X.shape[0]*2) - self.assertEqual(self.widget.imageplot.vector_plot.params[1].shape[0], - self.iris.X.shape[0]*2) + self.assertEqual( + self.widget.imageplot.vector_plot.params[0].shape[0], + self.iris.X.shape[0] * 2, + ) + self.assertEqual( + self.widget.imageplot.vector_plot.params[1].shape[0], + self.iris.X.shape[0] * 2, + ) self.widget.imageplot.v_bin = 1 self.widget.imageplot._update_binsize() @@ -961,7 +1044,7 @@ def test_vect_bin(self): wait_for_image(self.widget) self.assertEqual(self.widget.imageplot.vector_plot.params[0].shape[0], 2) self.assertEqual(self.widget.imageplot.vector_plot.params[1].shape[0], 2) - + self.widget.imageplot.v_bin = 3 self.widget.imageplot._update_binsize() self.widget.imageplot.update_view() diff --git a/orangecontrib/spectroscopy/tests/test_owintegrate.py b/orangecontrib/spectroscopy/tests/test_owintegrate.py index c10f88081..6ea019a8d 100644 --- a/orangecontrib/spectroscopy/tests/test_owintegrate.py +++ b/orangecontrib/spectroscopy/tests/test_owintegrate.py @@ -1,13 +1,18 @@ import Orange from Orange.widgets.tests.base import WidgetTest -from orangecontrib.spectroscopy.tests.spectral_preprocess import pack_editor, wait_for_preview -from orangecontrib.spectroscopy.widgets.owintegrate import OWIntegrate, PREPROCESSORS,\ - IntegrateSimpleEditor +from orangecontrib.spectroscopy.tests.spectral_preprocess import ( + pack_editor, + wait_for_preview, +) +from orangecontrib.spectroscopy.widgets.owintegrate import ( + OWIntegrate, + PREPROCESSORS, + IntegrateSimpleEditor, +) from orangecontrib.spectroscopy.tests import spectral_preprocess class TestOWIntegrate(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWIntegrate) @@ -35,9 +40,10 @@ def test_allint_indv_empty(self): # no attributes data = Orange.data.Table("peach_juice.dpt") data = data.transform( - Orange.data.Domain([], - class_vars=data.domain.class_vars, - metas=data.domain.metas)) + Orange.data.Domain( + [], class_vars=data.domain.class_vars, metas=data.domain.metas + ) + ) for p in PREPROCESSORS: self.widget = self.create_widget(OWIntegrate) self.send_signal("Data", data) @@ -73,7 +79,9 @@ def test_output_as_metas(self): self.assertEqual(1, len(out_data.domain.metas)) preprocessor = self.get_output(self.widget.Outputs.preprocessor) preprocessed = preprocessor(data) - self.assertEqual(len(data.domain.attributes), len(preprocessed.domain.attributes)) + self.assertEqual( + len(data.domain.attributes), len(preprocessed.domain.attributes) + ) self.assertEqual(1, len(out_data.domain.metas)) def test_output_as_non_metas(self): diff --git a/orangecontrib/spectroscopy/tests/test_owinterpolate.py b/orangecontrib/spectroscopy/tests/test_owinterpolate.py index 2fe89189c..ab4c475d0 100644 --- a/orangecontrib/spectroscopy/tests/test_owinterpolate.py +++ b/orangecontrib/spectroscopy/tests/test_owinterpolate.py @@ -6,7 +6,6 @@ class TestOWInterpolate(WidgetTest): - @classmethod def setUpClass(cls): super().setUpClass() diff --git a/orangecontrib/spectroscopy/tests/test_owmultifile.py b/orangecontrib/spectroscopy/tests/test_owmultifile.py index 545c4e415..dc40501ea 100644 --- a/orangecontrib/spectroscopy/tests/test_owmultifile.py +++ b/orangecontrib/spectroscopy/tests/test_owmultifile.py @@ -18,8 +18,12 @@ from orangecontrib.spectroscopy.io import SPAReader from orangecontrib.spectroscopy.io.ascii import AsciiColReader from orangecontrib.spectroscopy.io.util import SpectralFileFormat -from orangecontrib.spectroscopy.widgets.owmultifile import OWMultifile, numpy_union_keep_order, \ - wns_to_unique_str, decimals_neeeded_for_unique_str +from orangecontrib.spectroscopy.widgets.owmultifile import ( + OWMultifile, + numpy_union_keep_order, + wns_to_unique_str, + decimals_neeeded_for_unique_str, +) class ReadImaginaryFile(SPAReader): @@ -33,7 +37,6 @@ def read_spectra(self): class TestOWFilesAuxiliary(unittest.TestCase): - def test_numpy_union(self): A = np.array([2, 1, 3]) B = np.array([1, 3]) @@ -62,17 +65,32 @@ def test_decimals_neeeded_for_unique_str(self): def test_wns_to_unique_str(self): names = wns_to_unique_str([1, 2, 2 + 1e-10, 3]) - self.assertEqual(names, ['1.000000', '2.000000000000', '2.000000000100', '3.000000']) + self.assertEqual( + names, ['1.000000', '2.000000000000', '2.000000000100', '3.000000'] + ) names = wns_to_unique_str([1, 2, 2 + 0.99e-10, 3]) - self.assertEqual(names, ['1.000000', '2.000000000000', '2.000000000099', '3.000000']) + self.assertEqual( + names, ['1.000000', '2.000000000000', '2.000000000099', '3.000000'] + ) names = wns_to_unique_str([4, 1, 2, 2 + 0.99e-10, 3]) - self.assertEqual(names, ['4.000000', '1.000000', '2.000000000000', '2.000000000099', '3.000000']) - names = wns_to_unique_str([2+2e-10, 1, 2, 2 + 1e-10, 3]) - self.assertEqual(names, ['2.000000000200', '1.000000', '2.000000000000', '2.000000000100', '3.000000']) + self.assertEqual( + names, + ['4.000000', '1.000000', '2.000000000000', '2.000000000099', '3.000000'], + ) + names = wns_to_unique_str([2 + 2e-10, 1, 2, 2 + 1e-10, 3]) + self.assertEqual( + names, + [ + '2.000000000200', + '1.000000', + '2.000000000000', + '2.000000000100', + '3.000000', + ], + ) class TestOWMultifile(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWMultifile) # type: OWMultifile @@ -94,7 +112,11 @@ def open_with_no_specific_format(a, b, c, filters, e): def open_with_specific_format(a, b, c, filters, e): return files, format_filter(reader) - patchfn = open_with_no_specific_format if reader is None else open_with_specific_format + patchfn = ( + open_with_no_specific_format + if reader is None + else open_with_specific_format + ) # pretend that files were chosen in the open dialog with patch("AnyQt.QtWidgets.QFileDialog.getOpenFileNames", patchfn): @@ -107,24 +129,28 @@ def test_load_files(self): titanic = Table("titanic") for a in list(iris.domain.variables) + list(titanic.domain.variables): self.assertIn(a, out.domain) - self.assertEqual(set(out.domain.class_vars), - set(iris.domain.class_vars) | set(titanic.domain.class_vars)) + self.assertEqual( + set(out.domain.class_vars), + set(iris.domain.class_vars) | set(titanic.domain.class_vars), + ) self.assertEqual(len(out), len(iris) + len(titanic)) def test_load_files_reader(self): self.load_files("iris") self.assertIs(self.widget.recent_paths[0].file_format, None) self.load_files("iris", reader=TabReader) - self.assertEqual(self.widget.recent_paths[1].file_format, "Orange.data.io.TabReader") + self.assertEqual( + self.widget.recent_paths[1].file_format, "Orange.data.io.TabReader" + ) def test_filename(self): self.load_files("iris", "titanic") out = self.get_output("Data") - iris = out[:len(Table("iris"))] + iris = out[: len(Table("iris"))] fns = set([e["Filename"].value for e in iris]) self.assertTrue(len(fns), 1) self.assertIn("iris", fns.pop().lower()) - titanic = out[len(Table("iris")):] + titanic = out[len(Table("iris")) :] fns = set([e["Filename"].value for e in titanic]) self.assertTrue(len(fns), 1) self.assertIn("titanic", fns.pop().lower()) @@ -155,7 +181,9 @@ def test_saving_setting(self): self.assertEqual(self.widget.recent_paths[0].basename, "iris.tab") self.assertEqual(self.widget.recent_paths[0].file_format, None) self.assertEqual(self.widget.recent_paths[1].basename, "iris.tab") - self.assertEqual(self.widget.recent_paths[1].file_format, "Orange.data.io.TabReader") + self.assertEqual( + self.widget.recent_paths[1].file_format, "Orange.data.io.TabReader" + ) def test_files_relocated_on_saved_workflow(self): tempdir = tempfile.mkdtemp() @@ -163,8 +191,10 @@ def test_files_relocated_on_saved_workflow(self): oiris = FileFormat.locate("iris.tab", dataset_dirs) ciris = os.path.join(tempdir, "iris.tab") shutil.copy(oiris, ciris) - with patch("Orange.widgets.widget.OWWidget.workflowEnv", - Mock(return_value={"basedir": tempdir})): + with patch( + "Orange.widgets.widget.OWWidget.workflowEnv", + Mock(return_value={"basedir": tempdir}), + ): self.load_files(ciris) self.assertEqual(self.widget.recent_paths[0].relpath, "iris.tab") finally: @@ -178,9 +208,13 @@ def test_files_relocated_after_workflow_save(self): shutil.copy(oiris, ciris) self.load_files(ciris) self.assertEqual(self.widget.recent_paths[0].relpath, None) - with patch("Orange.widgets.widget.OWWidget.workflowEnv", - Mock(return_value={"basedir": tempdir})): - self.widget.workflowEnvChanged("basedir", tempdir, None) # run to propagate changes + with patch( + "Orange.widgets.widget.OWWidget.workflowEnv", + Mock(return_value={"basedir": tempdir}), + ): + self.widget.workflowEnvChanged( + "basedir", tempdir, None + ) # run to propagate changes self.assertEqual(self.widget.recent_paths[0].relpath, "iris.tab") finally: shutil.rmtree(tempdir) @@ -211,7 +245,6 @@ def test_reset_domain_edit(self): self.assertEqual(4, len(data.domain.attributes)) def test_special_spectral_reading(self): - class CountTabReader(TabReader): read_count = 0 @@ -232,8 +265,11 @@ def read_spectra(self): return super().read_spectra() # can not patch readers directly as they are already in registry - with patch.object(FileFormat, "registry", {"TabReader": CountTabReader, - "SPAReader": CountSPAReader}): + with patch.object( + FileFormat, + "registry", + {"TabReader": CountTabReader, "SPAReader": CountSPAReader}, + ): # clear LRU cache so that new classes get use FileFormat._ext_to_attr_if_attr2.cache_clear() self.load_files("titanic.tab", "sample1.spa") @@ -242,16 +278,16 @@ def read_spectra(self): self.assertEqual(CountTabReader.read_count, 1) def test_spectra_almost_same_wavenumbers(self): - with patch.object(FileFormat, "registry", {"SPAReader": ReadImaginaryFile}): # clear LRU cache so that new classes get use FileFormat._ext_to_attr_if_attr2.cache_clear() self.load_files("sample1.spa", "sample1.spa") out = self.get_output("Data") - np.testing.assert_equal(out.X, [[42, 41, np.nan], - [43, np.nan, 44]]) - self.assertEqual([a.name for a in out.domain.attributes], - ['1.000000', '2.000000000000', '2.000000000100']) + np.testing.assert_equal(out.X, [[42, 41, np.nan], [43, np.nan, 44]]) + self.assertEqual( + [a.name for a in out.domain.attributes], + ['1.000000', '2.000000000000', '2.000000000100'], + ) def test_report_on_empty(self): self.widget.send_report() @@ -281,8 +317,10 @@ def test_missing_files_do_not_disappear(self): def test_reader_not_found_error(self): self.load_files("iris") self.assertIsNotNone(self.get_output(self.widget.Outputs.data)) - with patch("orangecontrib.spectroscopy.widgets.owmultifile._get_reader", - side_effect=Exception()): + with patch( + "orangecontrib.spectroscopy.widgets.owmultifile._get_reader", + side_effect=Exception(), + ): self.widget.load_data() self.assertTrue(self.widget.Error.missing_reader.is_shown()) self.assertIsNone(self.get_output(self.widget.Outputs.data)) @@ -292,8 +330,7 @@ def test_reader_not_found_error(self): def test_unknown_reader_error(self): self.load_files("iris") self.assertIsNotNone(self.get_output(self.widget.Outputs.data)) - with patch("Orange.data.io.TabReader.read", - side_effect=Exception("test")): + with patch("Orange.data.io.TabReader.read", side_effect=Exception("test")): self.widget.load_data() self.assertTrue(self.widget.Error.read_error.is_shown()) self.assertIsNone(self.get_output(self.widget.Outputs.data)) @@ -345,15 +382,19 @@ def test_special_and_normal(self): 101\t3.05 104\t3.20 """ - n = """\ + n = """\ 100.000000\t200.000000\t300.000000\tclass 4\t5\t6\td """ nan = float("nan") - concat = np.array([[3., 3.1, 3.2, nan, nan, nan, nan], - [3., nan, 3.2, 3.05, nan, nan, nan], - [4., nan, nan, nan, 5., 6., 0.]]) + concat = np.array( + [ + [3.0, 3.1, 3.2, nan, nan, nan, nan], + [3.0, nan, 3.2, 3.05, nan, nan, nan], + [4.0, nan, nan, nan, 5.0, 6.0, 0.0], + ] + ) self.assertTrue(issubclass(AsciiColReader, SpectralFileFormat)) with named_file(s1, suffix=".xy") as fn1: @@ -367,9 +408,18 @@ def test_special_and_normal(self): self.load_files(fn) out = self.get_output(self.widget.Outputs.data) atts = [a.name for a in out.domain.attributes] - self.assertEqual(atts, ['100.000000', '102.000000', '104.000000', - '101.000000', '200.000000', '300.000000', - 'class']) + self.assertEqual( + atts, + [ + '100.000000', + '102.000000', + '104.000000', + '101.000000', + '200.000000', + '300.000000', + 'class', + ], + ) out = self.get_output(self.widget.Outputs.data) np.testing.assert_equal(out.X, concat) @@ -382,7 +432,7 @@ def test_special_and_normal(self): np.testing.assert_equal(out.X, concat[[0, 2, 1]]) def test_special_spectral_reader_metas(self): - n = """\ + n = """\ 100.000000\t200.000000\t300.000000\tmeta \t\t\tstring\n \t\t\tmeta\n @@ -392,8 +442,9 @@ def test_special_spectral_reader_metas(self): self.load_files("small_diamond_nxs.nxs") # special spectral rader self.load_files(fn) out = self.get_output(self.widget.Outputs.data) - self.assertEqual([a.name for a in out.domain.metas[:3]], - ["map_x", "map_y", "meta"]) + self.assertEqual( + [a.name for a in out.domain.metas[:3]], ["map_x", "map_y", "meta"] + ) self.assertAlmostEqual(out[0]['map_x'], -1.77900021) self.assertAlmostEqual(out[0]['map_y'], -2.74319824) self.assertEqual(out[0]['meta'].value, "") diff --git a/orangecontrib/spectroscopy/tests/test_owpeakfit.py b/orangecontrib/spectroscopy/tests/test_owpeakfit.py index 5843b6e92..db052ca8d 100644 --- a/orangecontrib/spectroscopy/tests/test_owpeakfit.py +++ b/orangecontrib/spectroscopy/tests/test_owpeakfit.py @@ -14,11 +14,24 @@ from orangecontrib.spectroscopy.tests.spectral_preprocess import wait_for_preview from orangecontrib.spectroscopy.widgets.gui import MovableVline import orangecontrib.spectroscopy.widgets.owpeakfit as owpeakfit -from orangecontrib.spectroscopy.widgets.owpeakfit import OWPeakFit, fit_peaks, PREPROCESSORS, \ - create_model, prepare_params, unique_prefix, create_composite_model, pack_model_editor -from orangecontrib.spectroscopy.widgets.peak_editors import ParamHintBox, VoigtModelEditor, \ - PseudoVoigtModelEditor, ExponentialGaussianModelEditor, PolynomialModelEditor, \ - GaussianModelEditor +from orangecontrib.spectroscopy.widgets.owpeakfit import ( + OWPeakFit, + fit_peaks, + PREPROCESSORS, + create_model, + prepare_params, + unique_prefix, + create_composite_model, + pack_model_editor, +) +from orangecontrib.spectroscopy.widgets.peak_editors import ( + ParamHintBox, + VoigtModelEditor, + PseudoVoigtModelEditor, + ExponentialGaussianModelEditor, + PolynomialModelEditor, + GaussianModelEditor, +) import orangecontrib.spectroscopy.widgets.peakfit_compute as peakfit_compute # shorter initializations in tests @@ -31,7 +44,6 @@ class TestOWPeakFit(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWPeakFit) self.data = COLLAGEN_1 @@ -47,15 +59,24 @@ def test_allint_indv(self): if p.viewclass == PolynomialModelEditor: self.skipTest("Polynomial Model does not converge on this data") if p.viewclass == ExponentialGaussianModelEditor: - self.skipTest("Exponential Gaussian Model does not converge on this data") + self.skipTest( + "Exponential Gaussian Model does not converge on this data" + ) elif p.viewclass == PseudoVoigtModelEditor: - settings = {'storedsettings': - {'name': '', - 'preprocessors': - [('orangecontrib.spectroscopy.widgets.peak_editors.pv', - {'center': OrderedDict([('value', 1650.0)]), - 'fraction': OrderedDict([('vary', "fixed")]), - })]}} + settings = { + 'storedsettings': { + 'name': '', + 'preprocessors': [ + ( + 'orangecontrib.spectroscopy.widgets.peak_editors.pv', + { + 'center': OrderedDict([('value', 1650.0)]), + 'fraction': OrderedDict([('vary', "fixed")]), + }, + ) + ], + } + } self.widget = self.create_widget(OWPeakFit, stored_settings=settings) self.send_signal("Data", self.data) if settings is None: @@ -92,7 +113,9 @@ def test_outputs(self): self.assertEqual(len(data), len(self.data)) np.testing.assert_array_equal(data.X, self.data.X) np.testing.assert_array_equal(data.Y, self.data.Y) - join_metas = np.asarray(np.hstack((self.data.metas, fit_params.X)), dtype=object) + join_metas = np.asarray( + np.hstack((self.data.metas, fit_params.X)), dtype=object + ) np.testing.assert_array_equal(data.metas, join_metas) def test_saving_models(self): @@ -100,27 +123,57 @@ def test_saving_models(self): self.assertEqual([], settings['storedsettings']['preprocessors']) self.widget.add_preprocessor(PREPROCESSORS[0]) settings = self.widget.settingsHandler.pack_data(self.widget) - self.assertEqual(PREPROCESSORS[0].qualname, - settings['storedsettings']['preprocessors'][0][0]) + self.assertEqual( + PREPROCESSORS[0].qualname, settings['storedsettings']['preprocessors'][0][0] + ) self.widget = self.create_widget(OWPeakFit, stored_settings=settings) vc = self.widget.preprocessormodel.item(0).data(DescriptionRole).viewclass self.assertEqual(PREPROCESSORS[0].viewclass, vc) def test_migrate_refactor1(self): - i1 = ("xyz", {'amplitude': {'value': 42.0, 'vary': False}, - 'center': {'value': 1349.984, 'min': 1307.984, 'max': 1391.984}, - 'sigma': {'min': 0, 'value': 1.0}}) - i2 = ("gam", {'gamma1': {'expr': 'sigma'}, - 'gamma2': {'expr': '', 'value': 0.0, 'min': -1.0, 'max': 1.0}}) + i1 = ( + "xyz", + { + 'amplitude': {'value': 42.0, 'vary': False}, + 'center': {'value': 1349.984, 'min': 1307.984, 'max': 1391.984}, + 'sigma': {'min': 0, 'value': 1.0}, + }, + ) + i2 = ( + "gam", + { + 'gamma1': {'expr': 'sigma'}, + 'gamma2': {'expr': '', 'value': 0.0, 'min': -1.0, 'max': 1.0}, + }, + ) settings = {"storedsettings": {"preprocessors": [i1, i2]}} OWPeakFit.migrate_settings(settings, 1) - o1 = ('xyz', {'amplitude': {'value': 42.0, 'vary': 'fixed'}, - 'center': {'max': 1391.984, 'min': 1307.984, - 'value': 1349.984, 'vary': 'limits'}, - 'sigma': {'min': 0, 'value': 1.0, 'vary': 'limits'}}) - o2 = ('gam', {'gamma1': {'expr': 'sigma', 'vary': 'expr'}, - 'gamma2': {'expr': '', 'max': 1.0, 'min': -1.0, - 'value': 0.0, 'vary': 'limits'}}) + o1 = ( + 'xyz', + { + 'amplitude': {'value': 42.0, 'vary': 'fixed'}, + 'center': { + 'max': 1391.984, + 'min': 1307.984, + 'value': 1349.984, + 'vary': 'limits', + }, + 'sigma': {'min': 0, 'value': 1.0, 'vary': 'limits'}, + }, + ) + o2 = ( + 'gam', + { + 'gamma1': {'expr': 'sigma', 'vary': 'expr'}, + 'gamma2': { + 'expr': '', + 'max': 1.0, + 'min': -1.0, + 'value': 0.0, + 'vary': 'limits', + }, + }, + ) self.assertEqual(settings["storedsettings"]["preprocessors"], [o1, o2]) def test_bug_iris_crash(self): @@ -143,14 +196,12 @@ def test_subset_preview(self): assert self.widget.sample_data(self.data).ids == subset.ids assert self.widget.preview_runner.preview_data.ids == subset.ids - def tearDown(self): self.widget.onDeleteWidget() super().tearDown() class TestPeakFit(unittest.TestCase): - def setUp(self): self.data = COLLAGEN_2 @@ -175,7 +226,7 @@ def test_table_output(self): for i, center in enumerate(pcs): p = f"v{i}_" dx = 20 - params[p + "center"].set(value=center, min=center-dx, max=center+dx) + params[p + "center"].set(value=center, min=center - dx, max=center + dx) params[p + "sigma"].set(max=50) params[p + "amplitude"].set(min=0.0001) out_result = model.fit(self.data.X[0], params, x=getx(self.data)) @@ -185,15 +236,18 @@ def test_table_output(self): attrs = [a.name for a in out_table.domain.attributes[:4]] self.assertEqual(attrs, ["v0 area", "v0 amplitude", "v0 center", "v0 sigma"]) self.assertNotEqual(0, out_row["v0 area"].value) - self.assertEqual(out_result.best_values["v0_amplitude"], out_row["v0 amplitude"].value) - self.assertEqual(out_result.best_values["v0_center"], out_row["v0 center"].value) + self.assertEqual( + out_result.best_values["v0_amplitude"], out_row["v0 amplitude"].value + ) + self.assertEqual( + out_result.best_values["v0_center"], out_row["v0 center"].value + ) self.assertEqual(out_result.best_values["v0_sigma"], out_row["v0 sigma"].value) self.assertEqual(out_result.redchi, out_row["Reduced chi-square"].value) self.assertEqual(out_row.id, self.data.ids[0]) class TestBuildModel(GuiTest): - def test_model_from_editor(self): self.editor = VoigtModelEditor() self.editor.set_hint('center', 'value', 1655) @@ -223,8 +277,9 @@ def setUp(self): self.add_editor(p.viewclass, self.widget) def wait_until_finished(self, widget=None, timeout=None): - super().wait_until_finished(widget, - timeout=timeout if timeout is not None else 10000) + super().wait_until_finished( + widget, timeout=timeout if timeout is not None else 10000 + ) def wait_for_preview(self): wait_for_preview(self.widget, timeout=10000) @@ -287,8 +342,11 @@ def test_set_center(self): def test_only_spec_lines(self): self.editor.activateOptions() model_lines = self.editor.model_lines() - lines = [l.label.toPlainText().strip() for l in self.widget.curveplot.markings - if isinstance(l, MovableVline)] + lines = [ + l.label.toPlainText().strip() + for l in self.widget.curveplot.markings + if isinstance(l, MovableVline) + ] for ml in model_lines: self.assertIn(ml, lines) no_lines = [p for p in self.editor.model_parameters() if p not in model_lines] @@ -305,12 +363,12 @@ def test_move_line(self): class TestVoigtEditorMulti(ModelEditorTest): - def setUp(self): self.pcs = [1547, 1655] self.widget = self.create_widget(OWPeakFit) - self.editors = [self.add_editor(VoigtModelEditor, self.widget) - for _ in range(len(self.pcs))] + self.editors = [ + self.add_editor(VoigtModelEditor, self.widget) for _ in range(len(self.pcs)) + ] self.data = COLLAGEN_2 self.send_signal(self.widget.Inputs.data, self.data) self.model, self.params = self.matched_models() @@ -332,7 +390,9 @@ def matched_models(self): # Set editor to same values e = self.editors[i] e_params = e.parameters() - e_params['center'].update({'value': center, 'min': center - dx, 'max': center + dx}) + e_params['center'].update( + {'value': center, 'min': center - dx, 'max': center + dx} + ) e_params['sigma']['max'] = 50 e_params['amplitude']['min'] = 0.0001 e.setParameters(e_params) @@ -340,8 +400,10 @@ def matched_models(self): return model, params def test_same_params(self): - m_def = [self.widget.preprocessormodel.item(i) - for i in range(self.widget.preprocessormodel.rowCount())] + m_def = [ + self.widget.preprocessormodel.item(i) + for i in range(self.widget.preprocessormodel.rowCount()) + ] ed_model, ed_params = create_composite_model(m_def) self.assertEqual(self.model.name, ed_model.name) @@ -359,19 +421,23 @@ def test_same_output(self): def test_saving_model_params(self): settings = self.widget.settingsHandler.pack_data(self.widget) restored_widget = self.create_widget(OWPeakFit, stored_settings=settings) - m_def = [restored_widget.preprocessormodel.item(i) - for i in range(restored_widget.preprocessormodel.rowCount())] + m_def = [ + restored_widget.preprocessormodel.item(i) + for i in range(restored_widget.preprocessormodel.rowCount()) + ] sv_model, sv_params = create_composite_model(m_def) self.assertEqual(self.model.name, sv_model.name) self.assertEqual(set(self.params), set(sv_params)) def test_total_area(self): - """ Test v0 + v1 area == total fit area """ + """Test v0 + v1 area == total fit area""" fit_params = self.get_output(self.widget.Outputs.fit_params, wait=10000) fits = self.get_output(self.widget.Outputs.fits) xs = getx(fits) - total_areas = Integrate(methods=Integrate.Simple, limits=[[xs.min(), xs.max()]])(fits) + total_areas = Integrate( + methods=Integrate.Simple, limits=[[xs.min(), xs.max()]] + )(fits) total_area = total_areas.X[0, 0] v0_area = fit_params[0]["v0 area"].value v1_area = fit_params[0]["v1 area"].value @@ -379,7 +445,6 @@ def test_total_area(self): class TestParamHintBox(GuiTest): - def test_defaults(self): defaults = { 'value': 0, diff --git a/orangecontrib/spectroscopy/tests/test_owpls.py b/orangecontrib/spectroscopy/tests/test_owpls.py index 72c56bc13..a9c647acc 100644 --- a/orangecontrib/spectroscopy/tests/test_owpls.py +++ b/orangecontrib/spectroscopy/tests/test_owpls.py @@ -5,7 +5,11 @@ from sklearn.cross_decomposition import PLSRegression from Orange.data import Table, Domain, ContinuousVariable -from Orange.widgets.tests.base import WidgetTest, WidgetLearnerTestMixin, ParameterMapping +from Orange.widgets.tests.base import ( + WidgetTest, + WidgetLearnerTestMixin, + ParameterMapping, +) import Orange.version from orangecontrib.spectroscopy.widgets.owpls import OWPLS @@ -27,9 +31,8 @@ def coefficients(sklmodel): class TestPLS(TestCase): - def test_allow_y_dim(self): - """ The current PLS version allows only a single Y dimension. """ + """The current PLS version allows only a single Y dimension.""" learner = PLSRegressionLearner(n_components=2) d = table(10, 5, 0) with self.assertRaises(ValueError): @@ -42,19 +45,21 @@ def test_compare_to_sklearn(self): d = table(10, 5, 1) orange_model = PLSRegressionLearner()(d) scikit_model = PLSRegression().fit(d.X, d.Y) - np.testing.assert_almost_equal(scikit_model.predict(d.X).ravel(), - orange_model(d)) - np.testing.assert_almost_equal(coefficients(scikit_model), - orange_model.coefficients) + np.testing.assert_almost_equal( + scikit_model.predict(d.X).ravel(), orange_model(d) + ) + np.testing.assert_almost_equal( + coefficients(scikit_model), orange_model.coefficients + ) def test_compare_to_sklearn_multid(self): d = table(10, 5, 3) orange_model = PLSRegressionLearner()(d) scikit_model = PLSRegression().fit(d.X, d.Y) - np.testing.assert_almost_equal(scikit_model.predict(d.X), - orange_model(d)) - np.testing.assert_almost_equal(coefficients(scikit_model), - orange_model.coefficients) + np.testing.assert_almost_equal(scikit_model.predict(d.X), orange_model(d)) + np.testing.assert_almost_equal( + coefficients(scikit_model), orange_model.coefficients + ) def test_too_many_components(self): # do not change n_components @@ -82,12 +87,15 @@ def test_scores(self): def test_components(self): def t2d(m): return m.reshape(-1, 1) if len(m.shape) == 1 else m + for d in [table(10, 5, 1), table(10, 5, 3)]: orange_model = PLSRegressionLearner()(d) scikit_model = PLSRegression().fit(d.X, d.Y) components = orange_model.components() np.testing.assert_almost_equal(scikit_model.x_loadings_, components.X.T) - np.testing.assert_almost_equal(scikit_model.y_loadings_, t2d(components.Y).T) + np.testing.assert_almost_equal( + scikit_model.y_loadings_, t2d(components.Y).T + ) def test_coefficients(self): for d in [table(10, 5, 1), table(10, 5, 3)]: @@ -99,15 +107,17 @@ def test_coefficients(self): class TestOWPLS(WidgetTest, WidgetLearnerTestMixin): def setUp(self): - self.widget = self.create_widget(OWPLS, - stored_settings={"auto_apply": False}) + self.widget = self.create_widget(OWPLS, stored_settings={"auto_apply": False}) self.init() self.parameters = [ ParameterMapping('max_iter', self.widget.n_iters), - ParameterMapping('n_components', self.widget.ncomps_spin)] + ParameterMapping('n_components', self.widget.ncomps_spin), + ] - skip_reason = "orange-widget-base changed apply button, which resulted in " \ - "test failures (but was otherwise benign)" + skip_reason = ( + "orange-widget-base changed apply button, which resulted in " + "test failures (but was otherwise benign)" + ) @skipIf(Orange.version.version < "3.35.0", skip_reason) def test_output_learner(self): diff --git a/orangecontrib/spectroscopy/tests/test_owpolar.py b/orangecontrib/spectroscopy/tests/test_owpolar.py index b76f01609..280a9e870 100644 --- a/orangecontrib/spectroscopy/tests/test_owpolar.py +++ b/orangecontrib/spectroscopy/tests/test_owpolar.py @@ -6,8 +6,8 @@ from Orange.widgets.tests.base import WidgetTest from orangecontrib.spectroscopy.widgets.owpolar import OWPolar -class TestOWPolar(WidgetTest): +class TestOWPolar(WidgetTest): @classmethod def setUpClass(cls): super().setUpClass() @@ -16,10 +16,18 @@ def setUpClass(cls): cls.in2 = Orange.data.Table("polar/4-angle-ftir_multiin2.tab") cls.in3 = Orange.data.Table("polar/4-angle-ftir_multiin3.tab") cls.in4 = Orange.data.Table("polar/4-angle-ftir_multiin4.tab") - cls.multifile_polar = Orange.data.Table("polar/4-angle-ftir_multifile_polar-results.tab") - cls.multifile_model = Orange.data.Table("polar/4-angle-ftir_multifile_model-results.tab") - cls.multiin_polar = Orange.data.Table("polar/4-angle-ftir_multiin_polar-results.tab") - cls.multiin_model = Orange.data.Table("polar/4-angle-ftir_multiin_model-results.tab") + cls.multifile_polar = Orange.data.Table( + "polar/4-angle-ftir_multifile_polar-results.tab" + ) + cls.multifile_model = Orange.data.Table( + "polar/4-angle-ftir_multifile_model-results.tab" + ) + cls.multiin_polar = Orange.data.Table( + "polar/4-angle-ftir_multiin_polar-results.tab" + ) + cls.multiin_model = Orange.data.Table( + "polar/4-angle-ftir_multiin_model-results.tab" + ) def setUp(self): self.widget = self.create_widget(OWPolar) @@ -27,18 +35,31 @@ def setUp(self): def test_multifile_init(self): self.send_signal("Data", self.multifile, 0) - testfeats = [ft for ft in self.multifile.domain.metas - if isinstance(ft, ContinuousVariable)] - testfeats = testfeats + [ft for ft in self.multifile.domain.attributes - if isinstance(ft, ContinuousVariable)] - polfeats = [ft for ft in self.widget.featureselect[:] - if isinstance(ft, ContinuousVariable)] + testfeats = [ + ft + for ft in self.multifile.domain.metas + if isinstance(ft, ContinuousVariable) + ] + testfeats = testfeats + [ + ft + for ft in self.multifile.domain.attributes + if isinstance(ft, ContinuousVariable) + ] + polfeats = [ + ft + for ft in self.widget.featureselect[:] + if isinstance(ft, ContinuousVariable) + ] self.assertEqual(polfeats, testfeats) - testinputs = [inp for inp in self.multifile.domain - if isinstance(inp, DiscreteVariable)] + testinputs = [ + inp for inp in self.multifile.domain if isinstance(inp, DiscreteVariable) + ] self.assertEqual(self.widget.anglemetas[:], testinputs) - testxy = [xy for xy in self.multifile.domain.metas - if isinstance(xy, ContinuousVariable)] + testxy = [ + xy + for xy in self.multifile.domain.metas + if isinstance(xy, ContinuousVariable) + ] self.assertEqual(self.widget.x_axis[:], testxy) self.assertEqual(self.widget.y_axis[:], testxy) @@ -64,8 +85,10 @@ def test_multifile_in(self): self.assertEqual(self.widget.map_x, self.multifile.domain.metas[0]) self.widget.map_y = self.widget.y_axis[1] self.assertEqual(self.widget.map_y, self.multifile.domain.metas[1]) - self.widget.feats = [self.widget.feat_view.model()[:][2], - self.widget.feat_view.model()[:][3]] + self.widget.feats = [ + self.widget.feat_view.model()[:][2], + self.widget.feat_view.model()[:][3], + ] self.assertEqual(self.widget.feats[0], self.multifile.domain.metas[3]) self.assertEqual(self.widget.feats[1], self.multifile.domain.metas[4]) self.widget.alphas = [0, 0] @@ -76,14 +99,26 @@ def test_multifile_in(self): polar = self.get_output("Polar Data") model = self.get_output("Curve Fit model data") - np.testing.assert_allclose(np.asarray(self.multifile_polar.metas, dtype=float), - np.asarray(polar.metas, dtype=float), rtol=4e-06) - np.testing.assert_allclose(np.asarray(self.multifile_polar.X, dtype=float), - np.asarray(polar.X, dtype=float), rtol=5e-06) - np.testing.assert_allclose(np.asarray(self.multifile_model.metas, dtype=float), - np.asarray(model.metas, dtype=float), rtol=4e-06) - np.testing.assert_allclose(np.asarray(self.multifile_model.X, dtype=float), - np.asarray(model.X, dtype=float), rtol=5e-06) + np.testing.assert_allclose( + np.asarray(self.multifile_polar.metas, dtype=float), + np.asarray(polar.metas, dtype=float), + rtol=4e-06, + ) + np.testing.assert_allclose( + np.asarray(self.multifile_polar.X, dtype=float), + np.asarray(polar.X, dtype=float), + rtol=5e-06, + ) + np.testing.assert_allclose( + np.asarray(self.multifile_model.metas, dtype=float), + np.asarray(model.metas, dtype=float), + rtol=4e-06, + ) + np.testing.assert_allclose( + np.asarray(self.multifile_model.X, dtype=float), + np.asarray(model.X, dtype=float), + rtol=5e-06, + ) def test_multi_inputs(self): self.send_signal("Data", self.in1, 0, widget=self.widget) @@ -107,12 +142,16 @@ def test_multi_inputs(self): self.widget.map_y = self.widget.y_axis[1] self.assertEqual(self.widget.map_y, self.in1.domain.metas[1]) - self.widget.feats = [self.widget.feat_view.model()[:][2], - self.widget.feat_view.model()[:][3]] - self.assertEqual(self.widget.feats[0], - self.in1.domain.metas[2].copy(compute_value=None)) - self.assertEqual(self.widget.feats[1], - self.in1.domain.metas[3].copy(compute_value=None)) + self.widget.feats = [ + self.widget.feat_view.model()[:][2], + self.widget.feat_view.model()[:][3], + ] + self.assertEqual( + self.widget.feats[0], self.in1.domain.metas[2].copy(compute_value=None) + ) + self.assertEqual( + self.widget.feats[1], self.in1.domain.metas[3].copy(compute_value=None) + ) self.widget.alphas = [0, 0] self.widget.invert_angles = True self.widget.autocommit = True @@ -122,22 +161,30 @@ def test_multi_inputs(self): model = self.get_output("Curve Fit model data") np.testing.assert_allclose( - np.asarray(self.multiin_polar.metas[:,np.r_[0:2,3:7]], dtype=float), - np.asarray(polar.metas[:,np.r_[0:2,3:7]], dtype=float), rtol=2e-06) + np.asarray(self.multiin_polar.metas[:, np.r_[0:2, 3:7]], dtype=float), + np.asarray(polar.metas[:, np.r_[0:2, 3:7]], dtype=float), + rtol=2e-06, + ) np.testing.assert_allclose( - np.asarray(self.multiin_polar.metas[:,7:], dtype=float), - np.asarray(polar.metas[:,7:], dtype=float), rtol=4e-06) + np.asarray(self.multiin_polar.metas[:, 7:], dtype=float), + np.asarray(polar.metas[:, 7:], dtype=float), + rtol=4e-06, + ) np.testing.assert_allclose(self.multiin_polar.X, polar.X, rtol=5e-06) np.testing.assert_allclose( - np.asarray(self.multiin_model.metas[:,np.r_[0:2,3:7]], dtype=float), - np.asarray(model.metas[:,np.r_[0:2,3:7]], dtype=float), rtol=4e-06) + np.asarray(self.multiin_model.metas[:, np.r_[0:2, 3:7]], dtype=float), + np.asarray(model.metas[:, np.r_[0:2, 3:7]], dtype=float), + rtol=4e-06, + ) np.testing.assert_allclose( - np.asarray(self.multiin_model.metas[:,7:], dtype=float), - np.asarray(model.metas[:,7:], dtype=float), rtol=4e-06) + np.asarray(self.multiin_model.metas[:, 7:], dtype=float), + np.asarray(model.metas[:, 7:], dtype=float), + rtol=4e-06, + ) np.testing.assert_allclose(self.multiin_model.X, model.X, rtol=5e-06) def test_pixelsubset(self): - #Test multi in with subset of pixels selected + # Test multi in with subset of pixels selected rng = np.random.default_rng() sub_idx = rng.choice(4, size=(2), replace=False) subset = self.in1[sub_idx] @@ -149,8 +196,10 @@ def test_pixelsubset(self): self.widget.map_x = self.widget.x_axis[0] self.widget.map_y = self.widget.y_axis[1] - self.widget.feats = [self.widget.feat_view.model()[:][2], - self.widget.feat_view.model()[:][3]] + self.widget.feats = [ + self.widget.feat_view.model()[:][2], + self.widget.feat_view.model()[:][3], + ] self.widget.alphas = [0, 0] self.widget.invert_angles = True self.widget.autocommit = True @@ -159,16 +208,15 @@ def test_pixelsubset(self): polar = self.get_output("Polar Data") model = self.get_output("Curve Fit model data") - self.assertEqual(len(polar), len(sub_idx)*4) - self.assertEqual(len(model), len(sub_idx)*4) + self.assertEqual(len(polar), len(sub_idx) * 4) + self.assertEqual(len(model), len(sub_idx) * 4) def test_multiin_mismatched_domain(self): - metadom = self.in1.domain.metas metadom = [i for i in metadom if isinstance(i, ContinuousVariable)] attdom = self.in1.domain.attributes attdom = attdom[0::2] - mismatched_domain = Domain(attdom, metas = metadom) + mismatched_domain = Domain(attdom, metas=metadom) mismatched_table = self.in1.transform(mismatched_domain) self.send_signal("Data", mismatched_table, 0, widget=self.widget) @@ -216,7 +264,7 @@ def test_custom_angles(self): self.assertEqual(j, angles[i]) def test_warnings(self): - #test all warnings + # test all warnings self.send_signal("Data", self.multifile, 0, widget=self.widget) self.widget.autocommit = True @@ -234,11 +282,11 @@ def test_warnings(self): self.widget.polangles = [] self.commit_and_wait(self.widget) self.assertTrue(self.widget.Warning.pol.is_shown()) - self.widget.polangles = [0.0,45.0,'hi',135.0] + self.widget.polangles = [0.0, 45.0, 'hi', 135.0] self.commit_and_wait(self.widget) self.assertTrue(self.widget.Warning.pol.is_shown()) - self.widget.polangles = [0.0,45.0,90.0,135.0] + self.widget.polangles = [0.0, 45.0, 90.0, 135.0] self.widget.feats = [self.widget.feat_view.model()[:][0]] self.widget.alphas = [0] self.commit_and_wait(self.widget) @@ -262,8 +310,10 @@ def test_disconnect(self): self.widget.alphas = [0, 0] self.widget.invert_angles = True self.widget.autocommit = True - self.widget.feats = [self.widget.feat_view.model()[:][2], - self.widget.feat_view.model()[:][3]] + self.widget.feats = [ + self.widget.feat_view.model()[:][2], + self.widget.feat_view.model()[:][3], + ] self.widget.handleNewSignals() self.wait_until_stop_blocking() self.send_signal("Data", None, 0, widget=self.widget) @@ -274,8 +324,10 @@ def test_alpha_changes(self): self.widget.map_x = self.widget.x_axis[0] self.widget.map_y = self.widget.y_axis[1] self.widget.alpha = 0 - vars = [self.widget.feat_view.model()[:][2], - self.widget.feat_view.model()[:][3]] + vars = [ + self.widget.feat_view.model()[:][2], + self.widget.feat_view.model()[:][3], + ] view = self.widget.feat_view model = self.widget.featureselect update_selection(model, view, vars) @@ -289,6 +341,7 @@ def test_alpha_changes(self): self.widget.change_alphas() self.assertEqual(self.widget.alphas, [90, 90]) + def update_selection(model, view, setting): selection = QItemSelection() sel_model = view.selectionModel() @@ -302,5 +355,6 @@ def update_selection(model, view, setting): # #test clearing angles # pass + if __name__ == "__main__": unittest.main() diff --git a/orangecontrib/spectroscopy/tests/test_owpreprocess.py b/orangecontrib/spectroscopy/tests/test_owpreprocess.py index dac9a2e24..6ce832589 100644 --- a/orangecontrib/spectroscopy/tests/test_owpreprocess.py +++ b/orangecontrib/spectroscopy/tests/test_owpreprocess.py @@ -8,15 +8,22 @@ from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.tests import spectral_preprocess -from orangecontrib.spectroscopy.tests.spectral_preprocess import pack_editor, wait_for_preview +from orangecontrib.spectroscopy.tests.spectral_preprocess import ( + pack_editor, + wait_for_preview, +) from orangecontrib.spectroscopy.tests.test_owspectra import wait_for_graph from orangecontrib.spectroscopy.widgets.owpreprocess import OWPreprocess -from orangecontrib.spectroscopy.widgets.preprocessors.misc import \ - CutEditor, SavitzkyGolayFilteringEditor +from orangecontrib.spectroscopy.widgets.preprocessors.misc import ( + CutEditor, + SavitzkyGolayFilteringEditor, +) from orangecontrib.spectroscopy.widgets.preprocessors.normalize import NormalizeEditor from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors -from orangecontrib.spectroscopy.widgets.preprocessors.utils import BaseEditorOrange, \ - REFERENCE_DATA_PARAM +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + BaseEditorOrange, + REFERENCE_DATA_PARAM, +) from orangecontrib.spectroscopy.tests.util import smaller_data PREPROCESSORS = list(map(pack_editor, preprocess_editors.sorted())) @@ -27,7 +34,6 @@ class TestAllPreprocessors(WidgetTest): - def test_allpreproc_indv(self): data = SMALLER_COLLAGEN for p in PREPROCESSORS: @@ -51,9 +57,10 @@ def test_allpreproc_indv_empty(self): self.wait_until_finished(timeout=10000) # no attributes data = data.transform( - Orange.data.Domain([], - class_vars=data.domain.class_vars, - metas=data.domain.metas)) + Orange.data.Domain( + [], class_vars=data.domain.class_vars, metas=data.domain.metas + ) + ) for p in PREPROCESSORS: with self.subTest(p.viewclass, type="no attributes"): self.widget = self.create_widget(OWPreprocess) @@ -91,7 +98,6 @@ def test_allpreproc_indv_ref_multi(self): class TestOWPreprocess(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) @@ -115,8 +121,10 @@ def test_saving_preprocessors(self): self.assertEqual([], settings["storedsettings"]["preprocessors"]) self.widget.add_preprocessor(self.widget.PREPROCESSORS[0]) settings = self.widget.settingsHandler.pack_data(self.widget) - self.assertEqual(self.widget.PREPROCESSORS[0].qualname, - settings["storedsettings"]["preprocessors"][0][0]) + self.assertEqual( + self.widget.PREPROCESSORS[0].qualname, + settings["storedsettings"]["preprocessors"][0][0], + ) def test_saving_preview_position(self): self.assertEqual(None, self.widget.preview_n) @@ -181,19 +189,23 @@ def test_long_preprocessor_list(self): self.wait_until_finished() def test_invalid_preprocessors(self): - settings = {"storedsettings": - {"preprocessors": [("xyz.abc.notme", {})]}} + settings = {"storedsettings": {"preprocessors": [("xyz.abc.notme", {})]}} with self.assertRaises(KeyError): with excepthook_catch(raise_on_exit=True): widget = self.create_widget(OWPreprocess, settings) self.assertTrue(widget.Error.loading.is_shown()) def test_migrate_rubberband(self): - settings = {"storedsettings": - {"preprocessors": [("orangecontrib.infrared.rubberband", {})]}} + settings = { + "storedsettings": { + "preprocessors": [("orangecontrib.infrared.rubberband", {})] + } + } OWPreprocess.migrate_settings(settings, 1) - self.assertEqual(settings["storedsettings"]["preprocessors"], - [("orangecontrib.infrared.baseline", {'baseline_type': 1})]) + self.assertEqual( + settings["storedsettings"]["preprocessors"], + [("orangecontrib.infrared.baseline", {'baseline_type': 1})], + ) def test_migrate_savitzygolay(self): name = "orangecontrib.infrared.savitzkygolay" @@ -210,35 +222,51 @@ def obtain_setting(settings): new_name = settings["storedsettings"]["preprocessors"][0][0] self.assertEqual(new_name, "orangecontrib.spectroscopy.savitzkygolay") - self.assertEqual(obtain_setting(settings), - {'deriv': 0, 'polyorder': 2, 'window': 5}) + self.assertEqual( + obtain_setting(settings), {'deriv': 0, 'polyorder': 2, 'window': 5} + ) settings = create_setting({'deriv': 4, 'polyorder': 4, 'window': 4}) OWPreprocess.migrate_settings(settings, 3) - self.assertEqual(obtain_setting(settings), - {'deriv': 3, 'polyorder': 4, 'window': 5}) + self.assertEqual( + obtain_setting(settings), {'deriv': 3, 'polyorder': 4, 'window': 5} + ) settings = create_setting({'deriv': 4, 'polyorder': 4, 'window': 100}) OWPreprocess.migrate_settings(settings, 3) - self.assertEqual(obtain_setting(settings), - {'deriv': 3, 'polyorder': 4, 'window': 99}) + self.assertEqual( + obtain_setting(settings), {'deriv': 3, 'polyorder': 4, 'window': 99} + ) settings = create_setting({'deriv': 4.1, 'polyorder': 4.1, 'window': 2.2}) OWPreprocess.migrate_settings(settings, 3) - self.assertEqual(obtain_setting(settings), - {'deriv': 2, 'polyorder': 2, 'window': 3}) + self.assertEqual( + obtain_setting(settings), {'deriv': 2, 'polyorder': 2, 'window': 3} + ) def test_migrate_spectral_transforms(self): - settings = {"storedsettings": { - "preprocessors": [("orangecontrib.infrared.transmittance", {}), - ("orangecontrib.infrared.absorbance", {})]}} + settings = { + "storedsettings": { + "preprocessors": [ + ("orangecontrib.infrared.transmittance", {}), + ("orangecontrib.infrared.absorbance", {}), + ] + } + } OWPreprocess.migrate_settings(settings, 3) self.assertEqual( settings["storedsettings"]["preprocessors"], - [("orangecontrib.spectroscopy.transforms", - {'from_type': 0, 'to_type': 1}), - ("orangecontrib.spectroscopy.transforms", - {'from_type': 1, 'to_type': 0})]) + [ + ( + "orangecontrib.spectroscopy.transforms", + {'from_type': 0, 'to_type': 1}, + ), + ( + "orangecontrib.spectroscopy.transforms", + {'from_type': 1, 'to_type': 0}, + ), + ], + ) class RememberData: @@ -254,7 +282,6 @@ def __call__(self, data): class RememberDataEditor(BaseEditorOrange): - def setParameters(self, p): pass @@ -264,7 +291,6 @@ def createinstance(params): class TestSampling(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) @@ -292,7 +318,7 @@ def test_preview_keep_order(self): wait_for_preview(self.widget) ids_new = RememberData.data.ids self.assertEqual(4, len(ids_new)) - self.assertEqual(list(ids_old), list(ids_new[:len(ids_old)])) + self.assertEqual(list(ids_old), list(ids_new[: len(ids_old)])) def test_apply_on_everything(self): data = SMALL_COLLAGEN @@ -306,7 +332,6 @@ def test_apply_on_everything(self): class TestReference(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWPreprocess) self.widget.autocommit = False @@ -367,13 +392,21 @@ def test_workflow_compat_change_preprocess(self): OWPreprocess.migrate_settings(settings, 5) self.assertTrue(settings["process_reference"]) - settings = {"storedsettings": {"preprocessors": [("orangecontrib.infrared.cut", {})]}} + settings = { + "storedsettings": {"preprocessors": [("orangecontrib.infrared.cut", {})]} + } OWPreprocess.migrate_settings(settings, 5) self.assertTrue(settings["process_reference"]) # multiple preprocessors: set to support old workflows - settings = {"storedsettings": {"preprocessors": [("orangecontrib.infrared.cut", {}), - ("orangecontrib.infrared.cut", {})]}} + settings = { + "storedsettings": { + "preprocessors": [ + ("orangecontrib.infrared.cut", {}), + ("orangecontrib.infrared.cut", {}), + ] + } + } OWPreprocess.migrate_settings(settings, 5) self.assertFalse(settings["process_reference"]) @@ -383,7 +416,6 @@ def test_workflow_compat_change_preprocess(self): class TestPreprocessWarning(spectral_preprocess.TestWarning): - widget_cls = OWPreprocess def test_exception_preview_after_data(self): @@ -399,7 +431,6 @@ def test_exception_preview_after_data(self): class PreprocessorEditorTest(WidgetTest): - def wait_for_preview(self): wait_for_preview(self.widget) diff --git a/orangecontrib/spectroscopy/tests/test_owreshape.py b/orangecontrib/spectroscopy/tests/test_owreshape.py index 8e8f92f6e..6bfdc87c3 100644 --- a/orangecontrib/spectroscopy/tests/test_owreshape.py +++ b/orangecontrib/spectroscopy/tests/test_owreshape.py @@ -5,7 +5,6 @@ class TestOWReshape(WidgetTest): - @classmethod def setUpClass(cls): super().setUpClass() diff --git a/orangecontrib/spectroscopy/tests/test_owsnr.py b/orangecontrib/spectroscopy/tests/test_owsnr.py index 935a40352..3ae63b936 100644 --- a/orangecontrib/spectroscopy/tests/test_owsnr.py +++ b/orangecontrib/spectroscopy/tests/test_owsnr.py @@ -5,9 +5,8 @@ class TestOWSNR(WidgetTest): - @classmethod - def setUpClass(cls): # carregando dado de teste + def setUpClass(cls): # carregando dado de teste super().setUpClass() cls.file_test = Orange.data.Table("three_coordinates_data.csv") @@ -48,8 +47,9 @@ def test_1coordinate_average(self): ref = [0.08537, 0.0684601, 0.0553439] np.testing.assert_equal(out.X.shape, (5, 10)) np.testing.assert_allclose(out.X[0, :3], ref, rtol=1e-06, atol=1e-06) - np.testing.assert_equal(out.metas[:3, :2].astype(float), - [[np.nan, 0], [np.nan, 1], [np.nan, 2]]) + np.testing.assert_equal( + out.metas[:3, :2].astype(float), [[np.nan, 0], [np.nan, 1], [np.nan, 2]] + ) def test_none_coordinate_std(self): self.send_signal("Data", self.file_test) diff --git a/orangecontrib/spectroscopy/tests/test_owspectra.py b/orangecontrib/spectroscopy/tests/test_owspectra.py index 004f99dd6..e5b0a1143 100644 --- a/orangecontrib/spectroscopy/tests/test_owspectra.py +++ b/orangecontrib/spectroscopy/tests/test_owspectra.py @@ -15,13 +15,21 @@ import pyqtgraph as pg from Orange.widgets.tests.base import WidgetTest -from Orange.data import Table, Domain, ContinuousVariable, DiscreteVariable - -from orangecontrib.spectroscopy.widgets.owspectra import OWSpectra, MAX_INSTANCES_DRAWN, \ - PlotCurvesItem, NoSuchCurve, MAX_THICK_SELECTED, CurvePlot +from Orange.data import Table, Domain, DiscreteVariable + +from orangecontrib.spectroscopy.widgets.owspectra import ( + OWSpectra, + MAX_INSTANCES_DRAWN, + PlotCurvesItem, + NoSuchCurve, + MAX_THICK_SELECTED, + CurvePlot, +) from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.widgets.line_geometry import intersect_curves, \ - distance_line_segment +from orangecontrib.spectroscopy.widgets.line_geometry import ( + intersect_curves, + distance_line_segment, +) from orangecontrib.spectroscopy.tests.util import hold_modifiers, set_png_graph_save from orangecontrib.spectroscopy.preprocess import Interpolate @@ -40,7 +48,6 @@ def wait_for_graph(widget, timeout=5000): class TestOWSpectra(WidgetTest): - @classmethod def setUpClass(cls): super().setUpClass() @@ -62,7 +69,9 @@ def setUpClass(cls): irisunknown.name = "irisunknown" cls.unknown_last_instance = cls.iris.copy() with cls.unknown_last_instance.unlocked(): - cls.unknown_last_instance.X[73] = NAN # needs to be unknown after sampling and permutation + cls.unknown_last_instance.X[73] = ( + NAN # needs to be unknown after sampling and permutation + ) cls.unknown_last_instance.name = "unknown_last_instance" # dataset with mixed unknowns cls.unknown_pts = cls.collagen.copy() @@ -76,8 +85,15 @@ def setUpClass(cls): with cls.only_inf.unlocked(): cls.only_inf.X *= np.inf cls.only_inf.name = "only_inf" - cls.strange_data = [iris1, iris0, empty, irisunknown, cls.unknown_last_instance, - cls.only_inf, cls.unknown_pts] + cls.strange_data = [ + iris1, + iris0, + empty, + irisunknown, + cls.unknown_last_instance, + cls.only_inf, + cls.unknown_pts, + ] def setUp(self): self.widget = self.create_widget(OWSpectra) # OWSpectra @@ -99,26 +115,35 @@ def test_is_last_instance_force_sampling_and_permutation(self): with patch(mi, 100): self.send_signal("Data", self.unknown_last_instance) wait_for_graph(self.widget) - self.assertTrue(np.all(np.isnan(self.unknown_last_instance[self.widget.curveplot.sampled_indices].X[-1]))) + self.assertTrue( + np.all( + np.isnan( + self.unknown_last_instance[ + self.widget.curveplot.sampled_indices + ].X[-1] + ) + ) + ) def do_mousemove(self): mr = self.widget.curveplot.MOUSE_RADIUS sbr = self.widget.curveplot.plot.sceneBoundingRect() self.widget.curveplot.MOUSE_RADIUS = 1000 - self.widget.curveplot.mouse_moved_closest( - (sbr.center(),)) - if self.widget.curveplot.data is not None \ - and np.any(np.isfinite(self.widget.curveplot.data.X)): # a valid curve exists + self.widget.curveplot.mouse_moved_closest((sbr.center(),)) + if self.widget.curveplot.data is not None and np.any( + np.isfinite(self.widget.curveplot.data.X) + ): # a valid curve exists self.assertIsNotNone(self.widget.curveplot.highlighted) else: # no curve can be detected self.assertIsNone(self.widget.curveplot.highlighted) # assume nothing is at the chosen point # therefore nothing should be highlighted - nothing_point = (sbr.center() + sbr.topLeft())/2 - self.widget.curveplot.MOUSE_RADIUS = 5 # locally, test_mouse_move failed with 30 - self.widget.curveplot.mouse_moved_closest( - (nothing_point,)) + nothing_point = (sbr.center() + sbr.topLeft()) / 2 + self.widget.curveplot.MOUSE_RADIUS = ( + 5 # locally, test_mouse_move failed with 30 + ) + self.widget.curveplot.mouse_moved_closest((nothing_point,)) self.assertIsNone(self.widget.curveplot.highlighted) self.widget.curveplot.MOUSE_RADIUS = mr @@ -180,7 +205,7 @@ def test_select_line(self): np.testing.assert_equal(sa.X, 1) def do_zoom_rect(self, invertX): - """ Test zooming with two clicks. """ + """Test zooming with two clicks.""" self.send_signal("Data", self.iris) vb = self.widget.curveplot.plot.vb self.widget.curveplot.invertX = invertX @@ -274,7 +299,9 @@ def test_line_intersection(self): def test_line_point_distance(self): # nan in point - a = distance_line_segment(np.array([0, 0]), np.array([0, float("nan")]), 10, 10, 5, 5) + a = distance_line_segment( + np.array([0, 0]), np.array([0, float("nan")]), 10, 10, 5, 5 + ) np.testing.assert_equal(a, [0, float("nan")]) # distance to the middle of the line segment @@ -308,7 +335,7 @@ def test_subset(self): self.assertTrue(set(add_subset.ids) <= set(data[sinds].ids)) # the whole subset can not be drawn anymore - add_subset = data[:MAX_INSTANCES_DRAWN+1] + add_subset = data[: MAX_INSTANCES_DRAWN + 1] self.send_signal("Data subset", add_subset) wait_for_graph(self.widget) sinds = self.widget.curveplot.sampled_indices @@ -338,12 +365,15 @@ def test_settings_color(self): self.send_signal("Data", self.iris) self.assertEqual(self.widget.curveplot.feature_color, None) self.widget.curveplot.feature_color = self.iris.domain.class_var - iris_context = self.widget.settingsHandler.pack_data(self.widget)["context_settings"] + iris_context = self.widget.settingsHandler.pack_data(self.widget)[ + "context_settings" + ] self.send_signal("Data", self.titanic) self.assertEqual(self.widget.curveplot.feature_color, None) # because previous settings match any domain, use only context for iris - self.widget = self.create_widget(OWSpectra, - stored_settings={"context_settings": iris_context}) + self.widget = self.create_widget( + OWSpectra, stored_settings={"context_settings": iris_context} + ) self.send_signal("Data", self.iris) self.assertEqual(self.widget.curveplot.feature_color.name, "iris") @@ -351,7 +381,9 @@ def test_cycle_color(self): self.send_signal("Data", self.iris) self.assertEqual(self.widget.curveplot.feature_color, None) self.widget.curveplot.cycle_color_attr() - self.assertEqual(self.widget.curveplot.feature_color, self.iris.domain.class_var) + self.assertEqual( + self.widget.curveplot.feature_color, self.iris.domain.class_var + ) self.widget.curveplot.cycle_color_attr() self.assertEqual(self.widget.curveplot.feature_color, None) @@ -366,7 +398,8 @@ def test_color_individual(self): def test_open_selection(self): # saved selection in the file should be reloaded self.widget = self.create_widget( - OWSpectra, stored_settings={"curveplot": {"selection_group_saved": [(0, 1)]}} + OWSpectra, + stored_settings={"curveplot": {"selection_group_saved": [(0, 1)]}}, ) self.send_signal("Data", self.iris) out = self.get_output("Selection") @@ -383,14 +416,15 @@ def test_selection_changedata(self): wait_for_graph(self.widget) self.widget.curveplot.MOUSE_RADIUS = 1000 self.widget.curveplot.mouse_moved_closest( - (self.widget.curveplot.plot.sceneBoundingRect().center(),)) + (self.widget.curveplot.plot.sceneBoundingRect().center(),) + ) self.widget.curveplot.select_by_click(None) out = self.get_output("Selection") self.assertEqual(len(out), 1) # resending the exact same data should not change the selection self.send_signal("Data", self.iris) wait_for_graph(self.widget) - out2 = self.get_output("Selection") + out2 = self.get_output("Selection") # noqa: F841 self.assertEqual(len(out), 1) # while resending the same data as a different object should self.send_signal("Data", self.iris.copy()) @@ -417,10 +451,13 @@ def test_select_click_multiple_groups(self): out = out[np.flatnonzero(out.transform(Domain([group_at])).X != unselected)] self.assertEqual(len(out), 4) np.testing.assert_equal([o for o in out], [data[i] for i in [1, 2, 3, 4]]) - np.testing.assert_equal([o[group_at].value for o in out], ["G1", "G2", "G3", "G3"]) + np.testing.assert_equal( + [o[group_at].value for o in out], ["G1", "G2", "G3", "G3"] + ) out = self.get_output(self.widget.Outputs.selected_data) - np.testing.assert_equal([o[out.domain["Group"]].value for o in out], - ["G1", "G2", "G3", "G3"]) + np.testing.assert_equal( + [o[out.domain["Group"]].value for o in out], ["G1", "G2", "G3", "G3"] + ) # remove one element with hold_modifiers(self.widget, Qt.AltModifier): @@ -428,8 +465,9 @@ def test_select_click_multiple_groups(self): out = self.get_output(self.widget.Outputs.selected_data) np.testing.assert_equal(len(out), 3) np.testing.assert_equal([o for o in out], [data[i] for i in [2, 3, 4]]) - np.testing.assert_equal([o[out.domain["Group"]].value for o in out], - ["G2", "G3", "G3"]) + np.testing.assert_equal( + [o[out.domain["Group"]].value for o in out], ["G2", "G3", "G3"] + ) def test_select_thick_lines(self): data = self.collagen[:100] @@ -449,7 +487,9 @@ def test_select_thick_lines_threshold(self): threshold = MAX_THICK_SELECTED self.send_signal("Data", data) wait_for_graph(self.widget) - set_curve_pens = 'orangecontrib.spectroscopy.widgets.owspectra.CurvePlot.set_curve_pens' + set_curve_pens = ( + 'orangecontrib.spectroscopy.widgets.owspectra.CurvePlot.set_curve_pens' + ) with patch(set_curve_pens, Mock()) as m: def clen(): @@ -542,8 +582,12 @@ def test_peakline_keep_precision(self): label_text2 = self.widget.curveplot.peak_labels[1].label.textItem.toPlainText() settings = self.widget.settingsHandler.pack_data(self.widget) self.widget = self.create_widget(OWSpectra, stored_settings=settings) - label_text_loaded = self.widget.curveplot.peak_labels[0].label.textItem.toPlainText() - label_text2_loaded = self.widget.curveplot.peak_labels[1].label.textItem.toPlainText() + label_text_loaded = self.widget.curveplot.peak_labels[ + 0 + ].label.textItem.toPlainText() + label_text2_loaded = self.widget.curveplot.peak_labels[ + 1 + ].label.textItem.toPlainText() self.assertEqual(label_text, label_text_loaded) self.assertEqual(label_text2, label_text2_loaded) @@ -581,11 +625,15 @@ def test_waterfall(self): def test_migrate_context_feature_color(self): # avoid class_vars in tests, because the setting does not allow them iris = self.iris.transform( - Domain(self.iris.domain.attributes, None, self.iris.domain.class_vars)) - c = self.widget.settingsHandler.new_context(iris.domain, None, iris.domain.metas) + Domain(self.iris.domain.attributes, None, self.iris.domain.class_vars) + ) + c = self.widget.settingsHandler.new_context( + iris.domain, None, iris.domain.metas + ) c.values["curveplot"] = {"feature_color": ("iris", 1)} - self.widget = self.create_widget(OWSpectra, - stored_settings={"context_settings": [c]}) + self.widget = self.create_widget( + OWSpectra, stored_settings={"context_settings": [c]} + ) self.send_signal("Data", iris) self.assertIsInstance(self.widget.curveplot.feature_color, DiscreteVariable) @@ -671,18 +719,18 @@ def test_visual_settings(self, timeout=5): self.assertEqual(axis.labelText, "Foo3") self.assertFalse(self.widget.Information.view_locked.is_shown()) - key, value = ("View Range", "X", "xMin"), 1. + key, value = ("View Range", "X", "xMin"), 1.0 self.widget.set_visual_settings(key, value) - key, value = ("View Range", "X", "xMax"), 3. + key, value = ("View Range", "X", "xMax"), 3.0 self.widget.set_visual_settings(key, value) vr = graph.plot.vb.viewRect() self.assertEqual(vr.left(), 1) self.assertEqual(vr.right(), 3) self.assertTrue(self.widget.Information.view_locked.is_shown()) - key, value = ("View Range", "Y", "yMin"), 2. + key, value = ("View Range", "Y", "yMin"), 2.0 self.widget.set_visual_settings(key, value) - key, value = ("View Range", "Y", "yMax"), 42. + key, value = ("View Range", "Y", "yMax"), 42.0 self.widget.set_visual_settings(key, value) vr = graph.plot.vb.viewRect() self.assertEqual(vr.top(), 2) @@ -694,16 +742,22 @@ def assertFontEqual(self, font1, font2): self.assertEqual(font1.italic(), font2.italic()) def test_migrate_visual_setttings(self): - settings = {"curveplot": - {"label_title": "title", - "label_xaxis": "x", - "label_yaxis": "y"} - } + settings = { + "curveplot": { + "label_title": "title", + "label_xaxis": "x", + "label_yaxis": "y", + } + } OWSpectra.migrate_settings(settings, 4) - self.assertEqual(settings["visual_settings"], - {('Annotations', 'Title', 'Title'): 'title', - ('Annotations', 'x-axis title', 'Title'): 'x', - ('Annotations', 'y-axis title', 'Title'): 'y'}) + self.assertEqual( + settings["visual_settings"], + { + ('Annotations', 'Title', 'Title'): 'title', + ('Annotations', 'x-axis title', 'Title'): 'x', + ('Annotations', 'y-axis title', 'Title'): 'y', + }, + ) settings = {} OWSpectra.migrate_settings(settings, 4) self.assertNotIn("visual_settings", settings) diff --git a/orangecontrib/spectroscopy/tests/test_owspectralseries.py b/orangecontrib/spectroscopy/tests/test_owspectralseries.py index 6281dcda0..f71f3d01c 100644 --- a/orangecontrib/spectroscopy/tests/test_owspectralseries.py +++ b/orangecontrib/spectroscopy/tests/test_owspectralseries.py @@ -15,7 +15,6 @@ class TestOWSpectralSeries(WidgetTest): - @classmethod def setUpClass(cls): super().setUpClass() @@ -34,14 +33,15 @@ def setUpClass(cls): irisunknown = Interpolate(np.arange(20))(cls.iris) # dataset without any attributes, but XY whitelight0 = cls.whitelight.transform( - Orange.data.Domain([], None, metas=cls.whitelight.domain.metas)) + Orange.data.Domain([], None, metas=cls.whitelight.domain.metas) + ) cls.strange_data = [None, cls.iris1, iris0, empty, irisunknown, whitelight0] def setUp(self): self.widget = self.create_widget(OWSpectralSeries) def try_big_selection(self): - all_select = None if self.widget.data is None else [1]*len(self.widget.data) + all_select = None if self.widget.data is None else [1] * len(self.widget.data) self.widget.imageplot.make_selection(all_select) self.widget.imageplot.make_selection(None) @@ -87,8 +87,9 @@ def test_select_click(self): self.widget.imageplot.select_by_click(QPointF(53.1, 1)) out = self.get_output("Selection") np.testing.assert_almost_equal(out.metas[:, 0], 53.099327, decimal=3) - np.testing.assert_almost_equal(out.metas[:, 1], - self.whitelight[::200].metas[:, 1], decimal=3) + np.testing.assert_almost_equal( + out.metas[:, 1], self.whitelight[::200].metas[:, 1], decimal=3 + ) np.testing.assert_equal(out.Y, 0) # selection group def test_single_update_view(self): @@ -102,14 +103,16 @@ def test_tooltip(self): self.send_signal(self.widget.Inputs.data, data) event = MagicMock() - with patch.object(self.widget.imageplot.plot.vb, "mapSceneToView"), \ - patch.object(QToolTip, "showText") as show_text: - + with ( + patch.object(self.widget.imageplot.plot.vb, "mapSceneToView"), + patch.object(QToolTip, "showText") as show_text, + ): sel = np.zeros(len(data), dtype="bool") sel[3] = 1 # a single instance - with patch.object(self.widget.imageplot, "_points_at_pos", - return_value=(sel, 2)): + with patch.object( + self.widget.imageplot, "_points_at_pos", return_value=(sel, 2) + ): self.assertTrue(self.widget.imageplot.help_event(event)) (_, text), _ = show_text.call_args self.assertIn("iris = {}".format(data[3, "iris"]), text) @@ -117,8 +120,9 @@ def test_tooltip(self): self.assertEqual(1, text.count("iris =")) sel[51] = 1 # add a data point - with patch.object(self.widget.imageplot, "_points_at_pos", - return_value=(sel, 2)): + with patch.object( + self.widget.imageplot, "_points_at_pos", return_value=(sel, 2) + ): self.assertTrue(self.widget.imageplot.help_event(event)) (_, text), _ = show_text.call_args self.assertIn("iris = {}".format(data[3, "iris"]), text) diff --git a/orangecontrib/spectroscopy/tests/test_preprocess.py b/orangecontrib/spectroscopy/tests/test_preprocess.py index 52355a524..dd7630853 100644 --- a/orangecontrib/spectroscopy/tests/test_preprocess.py +++ b/orangecontrib/spectroscopy/tests/test_preprocess.py @@ -9,14 +9,32 @@ from Orange.evaluation import TestOnTestData, AUC from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.preprocess import Absorbance, Transmittance, \ - Integrate, Interpolate, SavitzkyGolayFiltering, \ - GaussianSmoothing, PCADenoising, RubberbandBaseline, \ - Normalize, LinearBaseline, ShiftAndScale, MissingReferenceException, \ - WrongReferenceException, NormalizeReference, \ - PreprocessException, NormalizePhaseReference, SpSubtract, MNFDenoising +from orangecontrib.spectroscopy.preprocess import ( + Absorbance, + Transmittance, + Integrate, + Interpolate, + SavitzkyGolayFiltering, + GaussianSmoothing, + PCADenoising, + RubberbandBaseline, + Normalize, + LinearBaseline, + ShiftAndScale, + MissingReferenceException, + WrongReferenceException, + NormalizeReference, + PreprocessException, + NormalizePhaseReference, + SpSubtract, + MNFDenoising, +) from orangecontrib.spectroscopy.preprocess.utils import replacex -from orangecontrib.spectroscopy.tests.test_conversion import separate_learn_test, slightly_change_wavenumbers, odd_attr +from orangecontrib.spectroscopy.tests.test_conversion import ( + separate_learn_test, + slightly_change_wavenumbers, + odd_attr, +) from orangecontrib.spectroscopy.tests.util import smaller_data @@ -26,7 +44,7 @@ def add_zeros(data): - """ Every 5th value is zero """ + """Every 5th value is zero""" s = data.copy() with s.unlocked(): s[:, ::5] = 0 @@ -37,42 +55,43 @@ def make_edges_nan(data): s = data.copy() with s.unlocked(): s[:, 0:3] = np.nan - s[:, s.X.shape[1]-3:] = np.nan + s[:, s.X.shape[1] - 3 :] = np.nan return s def make_middle_nan(data): - """ Four middle values are NaN """ + """Four middle values are NaN""" s = data.copy() - half = s.X.shape[1]//2 + half = s.X.shape[1] // 2 with s.unlocked(): - s[:, half-2:half+2] = np.nan + s[:, half - 2 : half + 2] = np.nan return s def shuffle_attr(data): natts = list(data.domain.attributes) random.Random(0).shuffle(natts) - ndomain = Orange.data.Domain(natts, data.domain.class_vars, - metas=data.domain.metas) + ndomain = Orange.data.Domain(natts, data.domain.class_vars, metas=data.domain.metas) return data.transform(ndomain) def reverse_attr(data): natts = reversed(data.domain.attributes) - ndomain = Orange.data.Domain(natts, data.domain.class_vars, - metas=data.domain.metas) + ndomain = Orange.data.Domain(natts, data.domain.class_vars, metas=data.domain.metas) return data.transform(ndomain) -def add_edge_case_data_parameter(class_, data_arg_name, data_to_modify, *args, **kwargs): - modified = [data_to_modify, - shuffle_attr(data_to_modify), - make_edges_nan(data_to_modify), - shuffle_attr(make_edges_nan(data_to_modify)), - make_middle_nan(data_to_modify), - add_zeros(data_to_modify), - ] +def add_edge_case_data_parameter( + class_, data_arg_name, data_to_modify, *args, **kwargs +): + modified = [ + data_to_modify, + shuffle_attr(data_to_modify), + make_edges_nan(data_to_modify), + shuffle_attr(make_edges_nan(data_to_modify)), + make_middle_nan(data_to_modify), + add_zeros(data_to_modify), + ] for i, d in enumerate(modified): kwargs[data_arg_name] = d p = class_(*args, **kwargs) @@ -83,10 +102,9 @@ def add_edge_case_data_parameter(class_, data_arg_name, data_to_modify, *args, * class TestConversionMixin: - def test_slightly_different_domain(self): - """ If test data has a slightly different domain then (with interpolation) - we should obtain a similar classification score. """ + """If test data has a slightly different domain then (with interpolation) + we should obtain a similar classification score.""" learner = RandomForestLearner(random_state=42) for proc in self.preprocessors: @@ -101,18 +119,24 @@ def test_slightly_different_domain(self): test = odd_attr(test) # a subset of points for training so that all test sets points # are within the train set points, which gives no unknowns - train = Interpolate(points=getx(train)[1:-3])(train) # interpolatable train + train = Interpolate(points=getx(train)[1:-3])( + train + ) # interpolatable train train = proc(train) # explicit domain conversion test to catch exceptions that would # otherwise be silently handled in TestOnTestData _ = test.transform(train.domain) aucnow = AUC(TestOnTestData()(train, test, [learner])) - self.assertAlmostEqual(aucnow, aucorig, delta=0.03, msg="Preprocessor " + str(proc)) - test = Interpolate(points=getx(test) - 1.)(test) # also do a shift + self.assertAlmostEqual( + aucnow, aucorig, delta=0.03, msg="Preprocessor " + str(proc) + ) + test = Interpolate(points=getx(test) - 1.0)(test) # also do a shift _ = test.transform(train.domain) # explicit call again aucnow = AUC(TestOnTestData()(train, test, [learner])) # the difference should be slight - self.assertAlmostEqual(aucnow, aucorig, delta=0.05, msg="Preprocessor " + str(proc)) + self.assertAlmostEqual( + aucnow, aucorig, delta=0.05, msg="Preprocessor " + str(proc) + ) class TestConversionIndpSamplesMixin(TestConversionMixin): @@ -122,9 +146,9 @@ class TestConversionIndpSamplesMixin(TestConversionMixin): """ def test_whole_and_train_separate(self): - """ Applying a preprocessor before spliting data into train and test + """Applying a preprocessor before spliting data into train and test and applying is just on train data should yield the same transformation of - the test data. """ + the test data.""" for proc in self.preprocessors: with self.subTest(proc): data = self.data @@ -132,31 +156,33 @@ def test_whole_and_train_separate(self): train, test = separate_learn_test(data) train = proc(train) test_transformed = test.transform(train.domain) - np.testing.assert_almost_equal(test_transformed.X, test1.X, - err_msg="Preprocessor " + str(proc)) + np.testing.assert_almost_equal( + test_transformed.X, test1.X, err_msg="Preprocessor " + str(proc) + ) class TestStrangeDataMixin: - def test_no_samples(self): - """ Preprocessors should not crash when there are no input samples. """ + """Preprocessors should not crash when there are no input samples.""" data = self.data[:0] for proc in self.preprocessors: with self.subTest(proc): _ = proc(data) def test_no_attributes(self): - """ Preprocessors should not crash when samples have no attributes. """ + """Preprocessors should not crash when samples have no attributes.""" data = self.data - data = data.transform(Orange.data.Domain([], - class_vars=data.domain.class_vars, - metas=data.domain.metas)) + data = data.transform( + Orange.data.Domain( + [], class_vars=data.domain.class_vars, metas=data.domain.metas + ) + ) for proc in self.preprocessors: with self.subTest(proc): _ = proc(data) def test_all_nans(self): - """ Preprocessors should not crash when there are all-nan samples. """ + """Preprocessors should not crash when there are all-nan samples.""" for proc in self.preprocessors: with self.subTest(proc): data = self.data.copy() @@ -177,10 +203,14 @@ def test_unordered_features(self): X = pdata.X[:, np.argsort(getx(pdata))] pdata_reversed = proc(data_reversed) X_reversed = pdata_reversed.X[:, np.argsort(getx(pdata_reversed))] - np.testing.assert_almost_equal(X, X_reversed, err_msg="Preprocessor " + str(proc)) + np.testing.assert_almost_equal( + X, X_reversed, err_msg="Preprocessor " + str(proc) + ) pdata_shuffle = proc(data_shuffle) X_shuffle = pdata_shuffle.X[:, np.argsort(getx(pdata_shuffle))] - np.testing.assert_almost_equal(X, X_shuffle, err_msg="Preprocessor " + str(proc)) + np.testing.assert_almost_equal( + X, X_shuffle, err_msg="Preprocessor " + str(proc) + ) def test_unknown_no_propagate(self): for proc in self.preprocessors: @@ -198,7 +228,7 @@ def test_unknown_no_propagate(self): self.assertFalse(np.any(sumnans > 1), msg="Preprocessor " + str(proc)) def test_no_infs(self): - """ Preprocessors should not return (-)inf """ + """Preprocessors should not return (-)inf""" for proc in self.preprocessors: with self.subTest(proc): data = self.data.copy() @@ -225,9 +255,11 @@ class TestCommonIndpSamplesMixin(TestStrangeDataMixin, TestConversionIndpSamples class TestSpSubtract(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = list(add_edge_case_data_parameter( - SpSubtract, "reference", SMALLER_COLLAGEN[:1], amount=0.1)) + preprocessors = list( + add_edge_case_data_parameter( + SpSubtract, "reference", SMALLER_COLLAGEN[:1], amount=0.1 + ) + ) data = SMALLER_COLLAGEN def test_simple(self): @@ -239,10 +271,9 @@ def test_simple(self): class TestTransmittance(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = [Transmittance()] + \ - list(add_edge_case_data_parameter( - Transmittance, "reference", SMALLER_COLLAGEN[0:1])) + preprocessors = [Transmittance()] + list( + add_edge_case_data_parameter(Transmittance, "reference", SMALLER_COLLAGEN[0:1]) + ) data = SMALLER_COLLAGEN def test_domain_conversion(self): @@ -278,13 +309,11 @@ def test_eq(self): class TestAbsorbance(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = [Absorbance()] + \ - list(add_edge_case_data_parameter( - Absorbance, "reference", SMALLER_COLLAGEN[0:1])) + preprocessors = [Absorbance()] + list( + add_edge_case_data_parameter(Absorbance, "reference", SMALLER_COLLAGEN[0:1]) + ) data = SMALLER_COLLAGEN - def test_domain_conversion(self): """Test whether a domain can be used for conversion.""" data = Transmittance()(self.data) @@ -317,8 +346,7 @@ def test_eq(self): class TestSavitzkyGolay(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = [SavitzkyGolayFiltering(window=9, polyorder=2, deriv=2)] + preprocessors = [SavitzkyGolayFiltering(window=9, polyorder=2, deriv=2)] data = SMALL_COLLAGEN def test_simple(self): @@ -326,8 +354,9 @@ def test_simple(self): f = SavitzkyGolayFiltering() data = data[:1] fdata = f(data) - np.testing.assert_almost_equal(fdata.X, - [[4.86857143, 3.47428571, 1.49428571, 0.32857143]]) + np.testing.assert_almost_equal( + fdata.X, [[4.86857143, 3.47428571, 1.49428571, 0.32857143]] + ) def test_eq(self): data = Table.from_numpy(None, [[2, 1, 2, 2, 3]]) @@ -349,26 +378,25 @@ def test_eq(self): class TestGaussian(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = [GaussianSmoothing(sd=3.)] + preprocessors = [GaussianSmoothing(sd=3.0)] data = SMALL_COLLAGEN def test_simple(self): data = Orange.data.Table("iris") - f = GaussianSmoothing(sd=1.) + f = GaussianSmoothing(sd=1.0) data = data[:1] fdata = f(data) - np.testing.assert_almost_equal(fdata.X, - [[4.4907066, 3.2794677, 1.7641664, 0.6909083]]) + np.testing.assert_almost_equal( + fdata.X, [[4.4907066, 3.2794677, 1.7641664, 0.6909083]] + ) class TestRubberbandBaseline(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = [RubberbandBaseline()] + preprocessors = [RubberbandBaseline()] data = SMALLER_COLLAGEN def test_whole(self): - """ Every point belongs in the convex region. """ + """Every point belongs in the convex region.""" data = Table.from_numpy(None, [[2, 1, 2]]) i = RubberbandBaseline()(data) np.testing.assert_equal(i.X, 0) @@ -377,7 +405,7 @@ def test_whole(self): np.testing.assert_equal(i.X, 0) def test_simple(self): - """ Just one point is not in the convex region. """ + """Just one point is not in the convex region.""" data = Table.from_numpy(None, [[1, 2, 1, 1]]) i = RubberbandBaseline()(data) np.testing.assert_equal(i.X, [[0, 1, 0, 0]]) @@ -387,8 +415,7 @@ def test_simple(self): class TestLinearBaseline(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = [LinearBaseline()] + preprocessors = [LinearBaseline()] data = SMALL_COLLAGEN def test_whole(self): @@ -422,11 +449,13 @@ def test_edgepoints_out_of_data(self): class TestNormalize(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = [Normalize(method=Normalize.Vector), - Normalize(method=Normalize.Area, - int_method=Integrate.PeakMax, lower=0, upper=10000), - Normalize(method=Normalize.MinMax)] + preprocessors = [ + Normalize(method=Normalize.Vector), + Normalize( + method=Normalize.Area, int_method=Integrate.PeakMax, lower=0, upper=10000 + ), + Normalize(method=Normalize.MinMax), + ] data = SMALL_COLLAGEN @@ -456,33 +485,44 @@ def test_vector_norm_nan_correction(self): with data.unlocked(): data.X[0, 3] = float("nan") p = Normalize(method=Normalize.Vector)(data) - self.assertAlmostEqual(p.X[0, 0], 2**0.5/2) + self.assertAlmostEqual(p.X[0, 0], 2**0.5 / 2) self.assertTrue(np.all(np.isnan(p.X[0, 2:]))) def test_area_norm(self): data = Table.from_numpy(None, [[2, 1, 2, 2, 3]]) - p = Normalize(method=Normalize.Area, int_method=Integrate.PeakMax, lower=0, upper=4)(data) + p = Normalize( + method=Normalize.Area, int_method=Integrate.PeakMax, lower=0, upper=4 + )(data) np.testing.assert_equal(p.X, data.X / 3) - p = Normalize(method=Normalize.Area, int_method=Integrate.Simple, lower=0, upper=4)(data) + p = Normalize( + method=Normalize.Area, int_method=Integrate.Simple, lower=0, upper=4 + )(data) np.testing.assert_equal(p.X, data.X / 7.5) - p = Normalize(method=Normalize.Area, int_method=Integrate.Simple, lower=0, upper=2)(data) + p = Normalize( + method=Normalize.Area, int_method=Integrate.Simple, lower=0, upper=2 + )(data) q = Integrate(methods=Integrate.Simple, limits=[[0, 2]])(p) np.testing.assert_equal(q.X, np.ones_like(q.X)) def test_attribute_norm(self): data = Table.from_numpy(None, [[2, 1, 2, 2, 3]]) - ndom = Orange.data.Domain(data.domain.attributes, data.domain.class_vars, - metas=[Orange.data.ContinuousVariable("f")]) + ndom = Orange.data.Domain( + data.domain.attributes, + data.domain.class_vars, + metas=[Orange.data.ContinuousVariable("f")], + ) data = data.transform(ndom) with data.unlocked(data.metas): data[0]["f"] = 2 p = Normalize(method=Normalize.Attribute, attr=data.domain.metas[0])(data) np.testing.assert_equal(p.X, data.X / 2) - p = Normalize(method=Normalize.Attribute, attr=data.domain.metas[0], - lower=0, upper=4)(data) + p = Normalize( + method=Normalize.Attribute, attr=data.domain.metas[0], lower=0, upper=4 + )(data) np.testing.assert_equal(p.X, data.X / 2) - p = Normalize(method=Normalize.Attribute, attr=data.domain.metas[0], - lower=2, upper=4)(data) + p = Normalize( + method=Normalize.Attribute, attr=data.domain.metas[0], lower=2, upper=4 + )(data) np.testing.assert_equal(p.X, data.X / 2) def test_attribute_norm_unknown(self): @@ -518,25 +558,33 @@ def test_eq(self): self.assertNotEqual(p1.domain, p2.domain) self.assertEqual(p1.domain, p3.domain) - p1 = Normalize(method=Normalize.Area, int_method=Integrate.PeakMax, - lower=0, upper=4)(data) - p2 = Normalize(method=Normalize.Area, int_method=Integrate.Baseline, - lower=0, upper=4)(data) - p3 = Normalize(method=Normalize.Area, int_method=Integrate.PeakMax, - lower=1, upper=4)(data) - p4 = Normalize(method=Normalize.Area, int_method=Integrate.PeakMax, - lower=0, upper=4)(data) + p1 = Normalize( + method=Normalize.Area, int_method=Integrate.PeakMax, lower=0, upper=4 + )(data) + p2 = Normalize( + method=Normalize.Area, int_method=Integrate.Baseline, lower=0, upper=4 + )(data) + p3 = Normalize( + method=Normalize.Area, int_method=Integrate.PeakMax, lower=1, upper=4 + )(data) + p4 = Normalize( + method=Normalize.Area, int_method=Integrate.PeakMax, lower=0, upper=4 + )(data) self.assertNotEqual(p1.domain, p2.domain) self.assertNotEqual(p1.domain, p3.domain) self.assertEqual(p1.domain, p4.domain) class TestNormalizeReference(unittest.TestCase, TestCommonIndpSamplesMixin): - - preprocessors = (list(add_edge_case_data_parameter(NormalizeReference, - "reference", SMALLER_COLLAGEN[:1])) + - list(add_edge_case_data_parameter(NormalizePhaseReference, - "reference", SMALLER_COLLAGEN[:1]))) + preprocessors = list( + add_edge_case_data_parameter( + NormalizeReference, "reference", SMALLER_COLLAGEN[:1] + ) + ) + list( + add_edge_case_data_parameter( + NormalizePhaseReference, "reference", SMALLER_COLLAGEN[:1] + ) + ) data = SMALLER_COLLAGEN def test_reference(self): @@ -555,7 +603,6 @@ def test_reference_exceptions(self): class TestPCADenoising(unittest.TestCase, TestCommonMixin): - preprocessors = [PCADenoising(components=2)] data = SMALLER_COLLAGEN @@ -575,9 +622,13 @@ def test_iris(self): self.assertTrue(np.all(np.abs(differences) < 0.6)) # pin some values to detect changes in the PCA implementation # (for example normalization) - np.testing.assert_almost_equal(newdata.X[:2], - [[5.08718247, 3.51315614, 1.40204280, 0.21105556], - [4.75015528, 3.15366444, 1.46254138, 0.23693223]]) + np.testing.assert_almost_equal( + newdata.X[:2], + [ + [5.08718247, 3.51315614, 1.40204280, 0.21105556], + [4.75015528, 3.15366444, 1.46254138, 0.23693223], + ], + ) def test_selected_components(self): data = Orange.data.Table("iris") @@ -589,7 +640,6 @@ def test_selected_components(self): class TestMNFDenoising(unittest.TestCase, TestCommonMixin): - preprocessors = [MNFDenoising(components=2)] data = SMALL_COLLAGEN @@ -609,9 +659,13 @@ def test_iris(self): self.assertTrue(np.all(np.abs(differences) < 0.6)) # pin some values to detect changes in the PCA implementation # (for example normalization) - np.testing.assert_almost_equal(newdata.X[:2], - [[5.1084779, 3.4893387, 1.4068703, 0.1887913], - [4.7484942, 3.1913347, 1.427665, 0.2304239]]) + np.testing.assert_almost_equal( + newdata.X[:2], + [ + [5.1084779, 3.4893387, 1.4068703, 0.1887913], + [4.7484942, 3.1913347, 1.427665, 0.2304239], + ], + ) def test_slightly_different_domain(self): # test is disabled because this method is too sensitive to small input changes @@ -619,26 +673,22 @@ def test_slightly_different_domain(self): class TestShiftAndScale(unittest.TestCase, TestCommonIndpSamplesMixin): - preprocessors = [ShiftAndScale(1, 2)] data = SMALL_COLLAGEN def test_simple(self): data = Table.from_numpy(None, [[1.0, 2.0, 3.0, 4.0]]) - f = ShiftAndScale(offset=1.1, scale=2.) + f = ShiftAndScale(offset=1.1, scale=2.0) fdata = f(data) - np.testing.assert_almost_equal(fdata.X, - [[3.1, 5.1, 7.1, 9.1]]) + np.testing.assert_almost_equal(fdata.X, [[3.1, 5.1, 7.1, 9.1]]) class TestUtils(unittest.TestCase): - def test_replacex(self): data = Table.from_numpy(None, [[1.0, 2.0, 3.0, 4.0]]) self.assertEqual(list(getx(data)), [0, 1, 2, 3]) dr = replacex(data, ["a", 1, 2, 3]) - self.assertEqual([a.name for a in dr.domain.attributes], - ["a", "1", "2", "3"]) + self.assertEqual([a.name for a in dr.domain.attributes], ["a", "1", "2", "3"]) dr = replacex(data, np.array([0.5, 1, 2, 3])) self.assertEqual(list(getx(dr)), [0.5, 1, 2, 3]) np.testing.assert_equal(data.X, dr.X) diff --git a/orangecontrib/spectroscopy/tests/test_preprocess_utils.py b/orangecontrib/spectroscopy/tests/test_preprocess_utils.py index 38f5f055a..f23ac6406 100644 --- a/orangecontrib/spectroscopy/tests/test_preprocess_utils.py +++ b/orangecontrib/spectroscopy/tests/test_preprocess_utils.py @@ -6,7 +6,6 @@ class TestEq(TestCase): - @classmethod def setUpClass(cls): cls.iris = Table("iris") diff --git a/orangecontrib/spectroscopy/tests/test_readers.py b/orangecontrib/spectroscopy/tests/test_readers.py index cc1cf50a6..45753cfc9 100644 --- a/orangecontrib/spectroscopy/tests/test_readers.py +++ b/orangecontrib/spectroscopy/tests/test_readers.py @@ -9,7 +9,11 @@ from Orange.tests import named_file from Orange.widgets.data.owfile import OWFile from orangecontrib.spectroscopy.data import getx, build_spec_table -from orangecontrib.spectroscopy.io.neaspec import NeaReader, NeaReaderGSF, NeaReaderMultiChannel +from orangecontrib.spectroscopy.io.neaspec import ( + NeaReader, + NeaReaderGSF, + NeaReaderMultiChannel, +) from orangecontrib.spectroscopy.io.util import ConstantBytesVisibleImage from orangecontrib.spectroscopy.io.soleil import SelectColumnReader, HDF5Reader_HERMES from orangecontrib.spectroscopy.preprocess import features_with_interpolation @@ -43,7 +47,6 @@ def check_attributes(table): class TestReaders(unittest.TestCase): - def test_autointerpolate(self): d2 = Orange.data.Table("collagen.csv") d2x = getx(d2) @@ -53,7 +56,6 @@ def test_autointerpolate(self): class TestDat(unittest.TestCase): - @unittest.skipIf(opusFC is None, "opusFC module not installed") def test_peach_juice(self): d1 = Orange.data.Table("peach_juice.dpt") @@ -79,43 +81,47 @@ def test_roundtrip(self): def test_semicolon_comments(self): with named_file("15 500;comment1\n30 650; comment2\n", suffix=".dpt") as fn: d = Orange.data.Table(fn) - np.testing.assert_equal(d.X, [[500., 650.]]) + np.testing.assert_equal(d.X, [[500.0, 650.0]]) def test_semicolon_delimiter(self): with named_file("15;500\n30;650\n", suffix=".dpt") as fn: d = Orange.data.Table(fn) - np.testing.assert_equal(d.X, [[500., 650.]]) + np.testing.assert_equal(d.X, [[500.0, 650.0]]) def test_comma_delim(self): with named_file("15,500\n30,650\n", suffix=".dpt") as fn: d = Orange.data.Table(fn) - np.testing.assert_equal(d.X, [[500., 650.]]) + np.testing.assert_equal(d.X, [[500.0, 650.0]]) def test_unlabeled_comment_row(self): - with named_file("Wavenumber,Intensity\n650.4205,39.928503\n651.3523,40.086846", suffix=".dpt") as fn: + with named_file( + "Wavenumber,Intensity\n650.4205,39.928503\n651.3523,40.086846", + suffix=".dpt", + ) as fn: d = Orange.data.Table(fn) np.testing.assert_equal(d.X, [[39.928503, 40.086846]]) try: - no_visible_image = FileFormat.locate("opus/no_visible_images.0", - Orange.data.table.dataset_dirs) + no_visible_image = FileFormat.locate( + "opus/no_visible_images.0", Orange.data.table.dataset_dirs + ) except OSError: no_visible_image = False try: - one_visible_image = FileFormat.locate("opus/one_visible_image.0", - Orange.data.table.dataset_dirs) + one_visible_image = FileFormat.locate( + "opus/one_visible_image.0", Orange.data.table.dataset_dirs + ) except OSError: one_visible_image = False @unittest.skipIf(opusFC is None, "opusFC module not installed") class TestOpusReader(unittest.TestCase): - def test_read(self): juice = Orange.data.Table("peach_juice.0") - self.assertAlmostEqual(juice.X[0,3], 0.90655535) + self.assertAlmostEqual(juice.X[0, 3], 0.90655535) self.assertEqual(juice.domain.attributes[3].name, "3994.330014648437") @unittest.skipIf(no_visible_image is False, "Missing opus/no_visible_images.0") @@ -135,10 +141,8 @@ def test_one_visible_image_read(self): img_info = d.attributes["visible_images"][0] self.assertIsInstance(img_info, ConstantBytesVisibleImage) self.assertEqual(img_info.name, "Image 01") - self.assertAlmostEqual(img_info.pos_x, - 43552.0 * 0.9008849859237671) - self.assertAlmostEqual(img_info.pos_y, - 20727.0 * 0.8928490281105042) + self.assertAlmostEqual(img_info.pos_x, 43552.0 * 0.9008849859237671) + self.assertAlmostEqual(img_info.pos_y, 20727.0 * 0.8928490281105042) self.assertAlmostEqual(img_info.size_x, 600, places=0) self.assertAlmostEqual(img_info.size_y, 480, places=0) @@ -148,10 +152,8 @@ def test_one_visible_image_read(self): class TestHermesHDF5Reader(unittest.TestCase): - def test_read(self): - reader = initialize_reader(HDF5Reader_HERMES, - "Hermes_HDF5/small_OK.hdf5") + reader = initialize_reader(HDF5Reader_HERMES, "Hermes_HDF5/small_OK.hdf5") d = reader.read() self.assertEqual(d[0, 0], 1000.1) self.assertEqual(d[1, 0], 2000.1) @@ -162,7 +164,6 @@ def test_read(self): class TestNXS_STXM_Diamond_I08(unittest.TestCase): - def test_read(self): d = Orange.data.Table("small_diamond_nxs.nxs") self.assertAlmostEqual(d[0]['map_x'], -1.77900021) @@ -171,7 +172,6 @@ def test_read(self): class TestOmnicMapReader(unittest.TestCase): - def test_read(self): d = Orange.data.Table("small_Omnic.map") self.assertAlmostEqual(d[1, 0], 4.01309, places=5) @@ -183,7 +183,6 @@ def test_read(self): class TestAsciiMapReader(unittest.TestCase): - def test_read(self): d = Orange.data.Table("map_test.xyz") self.assertEqual(len(d), 16) @@ -212,7 +211,6 @@ def test_undefined_map_positions(self): class TestRenishawReader(unittest.TestCase): - def test_single_sp_reader(self): d = Orange.data.Table("renishaw_test_files/sp.wdf") self.assertEqual(d.X[0][4], 52.4945182800293) @@ -236,7 +234,6 @@ def disabled_test_map_reader(self): class TestPerkinElmerReader(unittest.TestCase): - def test_single_sp_reader(self): d = Orange.data.Table("perkinelmer/single_PE_spectrum.sp") self.assertEqual(d.X[0][4], 100.65028381347656) @@ -256,15 +253,12 @@ def test_map_reader(self): class TestAgilentReader(unittest.TestCase): - def test_image_read(self): d = Orange.data.Table("agilent/4_noimage_agg256.dat") self.assertEqual(len(d), 64) # Pixel sizes are 5.5 * 16 = 88.0 (binning to reduce test data) - self.assertAlmostEqual( - d[1]["map_x"] - d[0]["map_x"], 88.0) - self.assertAlmostEqual( - d[8]["map_y"] - d[7]["map_y"], 88.0) + self.assertAlmostEqual(d[1]["map_x"] - d[0]["map_x"], 88.0) + self.assertAlmostEqual(d[8]["map_y"] - d[7]["map_y"], 88.0) # Last pixel should start at (8 - 1) * 88.0 = 616.0 self.assertAlmostEqual(d[-1]["map_x"], 616.0) self.assertAlmostEqual(d[-1]["map_y"], 616.0) @@ -277,10 +271,8 @@ def test_mosaic_read(self): d = Orange.data.Table("agilent/5_mosaic_agg1024.dmt") self.assertEqual(len(d), 32) # Pixel sizes are 5.5 * 32 = 176.0 (binning to reduce test data) - self.assertAlmostEqual( - d[1]["map_x"] - d[0]["map_x"], 176.0) - self.assertAlmostEqual( - d[4]["map_y"] - d[3]["map_y"], 176.0) + self.assertAlmostEqual(d[1]["map_x"] - d[0]["map_x"], 176.0) + self.assertAlmostEqual(d[4]["map_y"] - d[3]["map_y"], 176.0) # Last pixel should start at (4 - 1) * 176.0 = 528.0 self.assertAlmostEqual(d[-1]["map_x"], 528.0) # 1 x 2 mosiac, (8 - 1) * 176.0 = 1232.0 @@ -297,11 +289,15 @@ def test_no_visible_image_read(self): # visible_images is not a permanent key self.assertNotIn("visible_images", d.attributes) - @unittest.skipIf(not hasattr(resources, "files"), - "importlib.resources.files requires python>=3.9") + @unittest.skipIf( + not hasattr(resources, "files"), + "importlib.resources.files requires python>=3.9", + ) def test_visible_image_read(self): # Test file in agilent_format has 2 visible images - vis_mosaic = resources.files("agilent_format") / "datasets" / "5_mosaic_agg1024.dmt" + vis_mosaic = ( + resources.files("agilent_format") / "datasets" / "5_mosaic_agg1024.dmt" + ) d = Orange.data.Table.from_file(vis_mosaic) self.assertIn("visible_images", d.attributes) @@ -310,10 +306,8 @@ def test_visible_image_read(self): img_info = d.attributes["visible_images"][0] self.assertIsInstance(img_info, ConstantBytesVisibleImage) self.assertEqual(img_info.name, "IR Cutout") - self.assertAlmostEqual(img_info.pos_x, - 0) - self.assertAlmostEqual(img_info.pos_y, - 0) + self.assertAlmostEqual(img_info.pos_x, 0) + self.assertAlmostEqual(img_info.pos_y, 0) self.assertAlmostEqual(img_info.size_x, 701, places=0) self.assertAlmostEqual(img_info.size_y, 1444, places=0) @@ -339,30 +333,27 @@ def test_image_ifg_read(self): self.assertEqual(len(d), 64) self.assertEqual(len(d.domain.attributes), 311) # Pixel sizes are 5.5 * 16 = 88.0 (binning to reduce test data) - self.assertAlmostEqual( - d[1]["map_x"] - d[0]["map_x"], 88.0) - self.assertAlmostEqual( - d[8]["map_y"] - d[7]["map_y"], 88.0) + self.assertAlmostEqual(d[1]["map_x"] - d[0]["map_x"], 88.0) + self.assertAlmostEqual(d[8]["map_y"] - d[7]["map_y"], 88.0) self.assertAlmostEqual(d[-1]["map_x"], 616.0) self.assertAlmostEqual(d[-1]["map_y"], 616.0) self.assertAlmostEqual(d[9][0], 0.64558595) self.assertAlmostEqual(d[18][0], 0.5792696) # Metadata - self.assertEqual(d.metas[0, 2], 1.57980039e+04) + self.assertEqual(d.metas[0, 2], 1.57980039e04) self.assertEqual(d.metas[0, 3], 4) def test_mosaic_ifg_read(self): # This reader will only be selected manually due to shared .dmt extension - reader = initialize_reader(agilentMosaicIFGReader, - "agilent/5_mosaic_agg1024.dmt") + reader = initialize_reader( + agilentMosaicIFGReader, "agilent/5_mosaic_agg1024.dmt" + ) d = reader.read() self.assertEqual(len(d), 32) self.assertEqual(len(d.domain.attributes), 311) # Pixel sizes are 5.5 * 32 = 176.0 (binning to reduce test data) - self.assertAlmostEqual( - d[1]["map_x"] - d[0]["map_x"], 176.0) - self.assertAlmostEqual( - d[4]["map_y"] - d[3]["map_y"], 176.0) + self.assertAlmostEqual(d[1]["map_x"] - d[0]["map_x"], 176.0) + self.assertAlmostEqual(d[4]["map_y"] - d[3]["map_y"], 176.0) # Last pixel should start at (4 - 1) * 176.0 = 528.0 self.assertAlmostEqual(d[-1]["map_x"], 528.0) # 1 x 2 mosiac, (8 - 1) * 176.0 = 1232.0 @@ -370,15 +361,15 @@ def test_mosaic_ifg_read(self): self.assertAlmostEqual(d[21][0], 0.7116039) self.assertAlmostEqual(d[26][0], 0.48532167) # Metadata - self.assertEqual(d.metas[0, 2], 1.57980039e+04) + self.assertEqual(d.metas[0, 2], 1.57980039e04) self.assertEqual(d.metas[0, 3], 4) class TestPTIRFileReader(unittest.TestCase): - def test_get_channels(self): - reader = initialize_reader(PTIRFileReader, - "photothermal/Nodax_Spectral_Array.ptir") + reader = initialize_reader( + PTIRFileReader, "photothermal/Nodax_Spectral_Array.ptir" + ) channel_map = reader.get_channels() signal = b'//ZI/*/DEMODS/0/R' label = b'OPTIR (mV)' @@ -386,8 +377,9 @@ def test_get_channels(self): self.assertEqual(channel_map[signal], label) def test_array_read(self): - reader = initialize_reader(PTIRFileReader, - "photothermal/Nodax_Spectral_Array.ptir") + reader = initialize_reader( + PTIRFileReader, "photothermal/Nodax_Spectral_Array.ptir" + ) reader.data_signal = b'//ZI/*/DEMODS/0/R' d = reader.read() self.assertAlmostEqual(d[0][0], 0.21426094) @@ -398,8 +390,7 @@ def test_array_read(self): self.assertAlmostEqual(d[0]["map_y"], -500.1499938964844) def test_hyperspectral_read(self): - reader = initialize_reader(PTIRFileReader, - "photothermal/Hyper_Sample.ptir") + reader = initialize_reader(PTIRFileReader, "photothermal/Hyper_Sample.ptir") reader.data_signal = b'//ZI/*/DEMODS/0/R' d = reader.read() self.assertEqual(len(d), 35) @@ -412,8 +403,7 @@ def test_hyperspectral_read(self): self.assertAlmostEqual(d[0]["map_y"], -886.1981201171875) def test_image_read(self): - reader = initialize_reader(PTIRFileReader, - "photothermal/Spectra_w_Image.ptir") + reader = initialize_reader(PTIRFileReader, "photothermal/Spectra_w_Image.ptir") reader.data_signal = b'//ZI/*/DEMODS/0/R' d = reader.read() self.assertAlmostEqual(d[0][0], -74.8579711914063) @@ -427,7 +417,6 @@ def test_image_read(self): class TestGSF(unittest.TestCase): - def test_open_line(self): data = Orange.data.Table("Au168mA_nodisplacement.gsf") self.assertEqual(data.X.shape, (20480, 1)) @@ -436,21 +425,17 @@ def test_open_2d(self): data = Orange.data.Table("whitelight.gsf") self.assertEqual(data.X.shape, (20000, 1)) # check some pixel vaules - self.assertAlmostEqual(data.X[235,0], 1.2788502, 7) - np.testing.assert_almost_equal(data.metas[235], - [53.2443, 30.6984], decimal=3) + self.assertAlmostEqual(data.X[235, 0], 1.2788502, 7) + np.testing.assert_almost_equal(data.metas[235], [53.2443, 30.6984], decimal=3) - self.assertAlmostEqual(data.X[1235,0], 1.2770579, 7) - np.testing.assert_almost_equal(data.metas[1235], - [53.2443, 30.6484], decimal=3) + self.assertAlmostEqual(data.X[1235, 0], 1.2770579, 7) + np.testing.assert_almost_equal(data.metas[1235], [53.2443, 30.6484], decimal=3) - self.assertAlmostEqual(data.X[11235,0], 1.2476133, 7) - np.testing.assert_almost_equal(data.metas[11235], - [53.2443, 30.1484], decimal=3) + self.assertAlmostEqual(data.X[11235, 0], 1.2476133, 7) + np.testing.assert_almost_equal(data.metas[11235], [53.2443, 30.1484], decimal=3) class TestNea(unittest.TestCase): - def test_open_v1(self): data = Orange.data.Table("spectra20_small.nea") self.assertEqual(len(data), 260) @@ -471,7 +456,7 @@ def test_open_v2(self): data = reader.read() self.assertEqual(len(data), 12) self.assertEqual("channel", data.domain.metas[2].name) - np.testing.assert_almost_equal(getx(data), [15., 89.]) + np.testing.assert_almost_equal(getx(data), [15.0, 89.0]) self.assertEqual("O0A", data.metas[0][2]) np.testing.assert_almost_equal(data.X[0, 0], 92.0) self.assertEqual("O0A", data.metas[6][2]) @@ -496,15 +481,17 @@ def test_ifg_read_info(self): data = NeaReader(absolute_filename).read() self.assertEqual(len(data), 30) self.assertEqual("channel", data.domain.metas[3].name) - self.assertEqual("O0A", data.metas[2][3]) # New reader has more channels + self.assertEqual("O0A", data.metas[2][3]) # New reader has more channels self.assertEqual("O0P", data.metas[3][3]) self.assertEqual(data.attributes['Channel Data Type'][0], 'Polar') - self.assertEqual(data.attributes['Calculated Datapoint Spacing (Δx)'][0], '[cm]') + self.assertEqual( + data.attributes['Calculated Datapoint Spacing (Δx)'][0], '[cm]' + ) self.assertEqual(data.attributes['Scan'], 'Fourier Scan') check_attributes(data) -class TestNeaGSF(unittest.TestCase): +class TestNeaGSF(unittest.TestCase): def test_read(self): fn = 'NeaReaderGSF_test/NeaReaderGSF_test O2P raw.gsf' absolute_filename = FileFormat.locate(fn, dataset_dirs) @@ -519,11 +506,13 @@ def test_read(self): self.assertEqual(n_ifg, 1024) self.assertEqual(n_ifg, len(data.domain.attributes)) self.assertEqual(data.attributes['Channel Data Type'][0], 'Polar') - self.assertEqual(data.attributes['Calculated Datapoint Spacing (Δx)'][0], '[cm]') + self.assertEqual( + data.attributes['Calculated Datapoint Spacing (Δx)'][0], '[cm]' + ) check_attributes(data) -class TestNeaImageGSF(unittest.TestCase): +class TestNeaImageGSF(unittest.TestCase): def test_type_detect(self): # For Phase fn = 'NeaReaderGSF_test O2P raw.gsf' @@ -558,20 +547,18 @@ def test_read(self): fn = "whitelight.gsf" absolute_filename = FileFormat.locate(fn, dataset_dirs) data = NeaReader(absolute_filename).read() - self.assertEqual(data.attributes["measurement.signaltype"],'Topography') + self.assertEqual(data.attributes["measurement.signaltype"], 'Topography') self.assertEqual(data.X.shape, (20000, 1)) # check some pixel vaules - self.assertAlmostEqual(data.X[235,0], 1.2788502, 7) - np.testing.assert_almost_equal(data.metas[235], - [53.2443, 29.7284], decimal=3) + self.assertAlmostEqual(data.X[235, 0], 1.2788502, 7) + np.testing.assert_almost_equal(data.metas[235], [53.2443, 29.7284], decimal=3) + + self.assertAlmostEqual(data.X[1235, 0], 1.2770579, 7) + np.testing.assert_almost_equal(data.metas[1235], [53.2443, 29.77848], decimal=3) - self.assertAlmostEqual(data.X[1235,0], 1.2770579, 7) - np.testing.assert_almost_equal(data.metas[1235], - [53.2443, 29.77848], decimal=3) + self.assertAlmostEqual(data.X[11235, 0], 1.2476133, 7) + np.testing.assert_almost_equal(data.metas[11235], [53.2443, 30.2784], decimal=3) - self.assertAlmostEqual(data.X[11235,0], 1.2476133, 7) - np.testing.assert_almost_equal(data.metas[11235], - [53.2443, 30.2784], decimal=3) class TestNeaMultiChannel(unittest.TestCase): def test_read(self): @@ -583,12 +570,14 @@ def test_read(self): self.assertEqual("O0A", data.metas[0][3]) self.assertEqual("O0P", data.metas[1][3]) self.assertEqual(data.attributes['Channel Data Type'][0], 'Polar') - self.assertEqual(data.attributes['Calculated Datapoint Spacing (Δx)'][0], '[cm]') + self.assertEqual( + data.attributes['Calculated Datapoint Spacing (Δx)'][0], '[cm]' + ) self.assertEqual(data.attributes['Scan'], 'Fourier Scan') check_attributes(data) -class TestEnvi(unittest.TestCase): +class TestEnvi(unittest.TestCase): def test_read(self): data = Orange.data.Table("agilent/4_noimage_agg256.hdr") self.assertEqual(len(data), 64) @@ -601,7 +590,6 @@ def test_read(self): class TestSpa(unittest.TestCase): - def test_open(self): _ = Orange.data.Table("sample1.spa") @@ -613,7 +601,6 @@ def test_read_header(self): class TestSpc(unittest.TestCase): - def test_multiple_x(self): data = Orange.data.Table("m_xyxy.spc") self.assertEqual(len(data), 512) @@ -622,7 +609,6 @@ def test_multiple_x(self): class TestMatlab(unittest.TestCase): - def test_simple(self): """ octave --eval "A = [ 5:7; 4:6 ]; save -6 simple.mat A" @@ -704,7 +690,6 @@ def test_IOError(self): class TestDataUtil(unittest.TestCase): - def test_build_spec_table_not_copy(self): """build_spec_table should not copy tables if not neccessary""" xs = np.arange(3) @@ -719,7 +704,6 @@ def test_build_spec_table_not_copy(self): class TestSelectColumn(unittest.TestCase): - def test_select_column(self): # explicit reader selection because of shared extension reader = initialize_reader(SelectColumnReader, "rock.txt") @@ -729,18 +713,17 @@ def test_select_column(self): reader.sheet = "3" d = reader.read() - np.testing.assert_equal(d.X, - [[0.91213142, 0.89539732, 0.87925428, 0.86225812]]) + np.testing.assert_equal(d.X, [[0.91213142, 0.89539732, 0.87925428, 0.86225812]]) np.testing.assert_equal(getx(d), [6870, 6880, 6890, 6900]) class TestStxmHdrXim(unittest.TestCase): - def test_read(self): data = Orange.data.Table("max_iv.hdr") self.assertEqual(len(data), 100) self.assertAlmostEqual(float(data.domain.attributes[0].name), 698) self.assertAlmostEqual(float(data.domain.attributes[-1].name), 700) + if __name__ == "__main__": unittest.main() diff --git a/orangecontrib/spectroscopy/tests/test_tile_reader.py b/orangecontrib/spectroscopy/tests/test_tile_reader.py index 0e5a32d65..a6126708e 100644 --- a/orangecontrib/spectroscopy/tests/test_tile_reader.py +++ b/orangecontrib/spectroscopy/tests/test_tile_reader.py @@ -4,17 +4,24 @@ import Orange import numpy as np from Orange.data import Table -from Orange.data.io import FileFormat from Orange.preprocess.preprocess import PreprocessorList from Orange.widgets.tests.base import WidgetTest from orangecontrib.spectroscopy import get_sample_datasets_dir -from orangecontrib.spectroscopy.preprocess import Interpolate, SavitzkyGolayFiltering, Cut, \ - GaussianSmoothing, Absorbance, Transmittance, Integrate +from orangecontrib.spectroscopy.preprocess import ( + Interpolate, + SavitzkyGolayFiltering, + Cut, + GaussianSmoothing, + Absorbance, + Transmittance, + Integrate, +) from orangecontrib.spectroscopy.widgets.owintegrate import OWIntegrate -from orangecontrib.spectroscopy.widgets.owpreprocess import OWPreprocess, \ - create_preprocessor - +from orangecontrib.spectroscopy.widgets.owpreprocess import ( + OWPreprocess, + create_preprocessor, +) from orangecontrib.spectroscopy.widgets.owtilefile import OWTilefile AGILENT_TILE = "agilent/5_mosaic_agg1024.dmt" @@ -25,15 +32,14 @@ Interpolate(np.linspace(1000, 1700, 100)), SavitzkyGolayFiltering(window=9, polyorder=2, deriv=2), Cut(lowlim=1000, highlim=1800), - GaussianSmoothing(sd=3.), + GaussianSmoothing(sd=3.0), Absorbance(), Transmittance(), - Integrate(limits=[[900, 100], [1100, 1200], [1200, 1300]]) + Integrate(limits=[[900, 100], [1100, 1200], [1200, 1300]]), ] class TestTileReaders(unittest.TestCase): - def test_tile_load(self): Orange.data.Table(AGILENT_TILE) @@ -49,12 +55,12 @@ def test_match_not_tiled(self): t = reader.read() t_orig = Table(path) np.testing.assert_array_equal(t.X, t_orig.X) - np.testing.assert_array_equal(t[:, ["map_x", "map_y"]].metas, - t_orig[:, ["map_x", "map_y"]].metas) + np.testing.assert_array_equal( + t[:, ["map_x", "map_y"]].metas, t_orig[:, ["map_x", "map_y"]].metas + ) class TestTilePreprocessors(unittest.TestCase): - def test_single_preproc(self): # TODO problematic interface design: should be able to use Orange.data.Table directly path = os.path.join(get_sample_datasets_dir(), AGILENT_TILE) @@ -74,7 +80,6 @@ def test_preprocessor_list(self): class TestTileReaderWidget(WidgetTest): - def setUp(self): self.widget = self.create_widget(OWTilefile) @@ -87,7 +92,7 @@ def test_load(self): self.assertNotEqual(self.get_output("Data"), None) def test_preproc_load(self): - """ Test that loading a preprocessor signal in the widget works """ + """Test that loading a preprocessor signal in the widget works""" # OWPreprocess test setup from test_owpreprocess.test_allpreproc_indv self.preproc_widget = self.create_widget(OWPreprocess) self.preproc_widget.add_preprocessor(self.preproc_widget.PREPROCESSORS[0]) @@ -97,7 +102,9 @@ def test_preproc_load(self): # Single Input self.assertEqual(self.widget.preprocessor.preprocessors[0], pp_out) # Preprocessor members match editor model - pp_from_model = create_preprocessor(self.preproc_widget.preprocessormodel.item(0), None) + pp_from_model = create_preprocessor( + self.preproc_widget.preprocessormodel.item(0), None + ) pp_tile = self.widget.preprocessor.preprocessors[0].preprocessors[0] self.assertIsInstance(pp_tile, type(pp_from_model)) # MultiInput with OWIntegrate @@ -106,5 +113,4 @@ def test_preproc_load(self): self.int_widget.commit.now() pp_out_2 = self.get_output("Preprocessor", widget=self.int_widget) self.send_signal("Preprocessor", pp_out_2, 2, widget=self.widget) - self.assertEqual(self.widget.preprocessor.preprocessors, - [pp_out, pp_out_2]) + self.assertEqual(self.widget.preprocessor.preprocessors, [pp_out, pp_out_2]) diff --git a/orangecontrib/spectroscopy/tests/test_utils.py b/orangecontrib/spectroscopy/tests/test_utils.py index ddb5de023..4bbc8945d 100644 --- a/orangecontrib/spectroscopy/tests/test_utils.py +++ b/orangecontrib/spectroscopy/tests/test_utils.py @@ -1,18 +1,19 @@ import unittest -import array - -import numpy as np import Orange.data +import numpy as np from orangecontrib.spectroscopy.data import build_spec_table, getx from orangecontrib.spectroscopy.io.util import _spectra_from_image -from orangecontrib.spectroscopy.utils import get_hypercube, index_values, \ - InvalidAxisException, split_to_size +from orangecontrib.spectroscopy.utils import ( + get_hypercube, + index_values, + InvalidAxisException, + split_to_size, +) class TestHyperspec(unittest.TestCase): - @classmethod def setUpClass(cls): super().setUpClass() @@ -34,8 +35,9 @@ def test_hypercube_roundtrip(self): x_locs = coords[:, 0, 0] y_locs = coords[0, :, 1] - features, spectra, data = _spectra_from_image(hypercube, features, - x_locs, y_locs) + features, spectra, data = _spectra_from_image( + hypercube, features, x_locs, y_locs + ) nd = build_spec_table(features, spectra, data) np.testing.assert_equal(d.X, nd.X) @@ -49,7 +51,6 @@ def test_none_attr(self): class TestSplitToSize(unittest.TestCase): - def test_single(self): self.assertEqual([], split_to_size(0, 10)) self.assertEqual([slice(0, 1)], split_to_size(1, 10)) @@ -58,4 +59,6 @@ def test_single(self): def test_more(self): self.assertEqual([slice(0, 10), slice(10, 11)], split_to_size(11, 10)) self.assertEqual([slice(0, 10), slice(10, 20)], split_to_size(20, 10)) - self.assertEqual([slice(0, 10), slice(10, 20), slice(20, 21)], split_to_size(21, 10)) + self.assertEqual( + [slice(0, 10), slice(10, 20), slice(20, 21)], split_to_size(21, 10) + ) diff --git a/orangecontrib/spectroscopy/tests/test_widgets_utils.py b/orangecontrib/spectroscopy/tests/test_widgets_utils.py index 5416c6304..8f109f5eb 100644 --- a/orangecontrib/spectroscopy/tests/test_widgets_utils.py +++ b/orangecontrib/spectroscopy/tests/test_widgets_utils.py @@ -7,7 +7,6 @@ class TestSelectionPacking(unittest.TestCase): - def test_pack(self): # None self.assertEqual(pack_selection(None), None) diff --git a/orangecontrib/spectroscopy/tests/test_xas.py b/orangecontrib/spectroscopy/tests/test_xas.py index a70015c05..7900d557d 100644 --- a/orangecontrib/spectroscopy/tests/test_xas.py +++ b/orangecontrib/spectroscopy/tests/test_xas.py @@ -4,80 +4,118 @@ from Orange.data import Table from Orange.preprocess import PreprocessorList -from orangecontrib.spectroscopy.preprocess import XASnormalization, ExtractEXAFS, NoEdgejumpProvidedException -from orangecontrib.spectroscopy.tests.test_preprocess import TestCommonIndpSamplesMixin, \ - SMALLER_COLLAGEN - - -xas_norm_collagen = XASnormalization(edge=1630, - preedge_dict={'from': 1000, 'to': 1300, 'deg': 1}, - postedge_dict={'from': 1650, 'to': 1700, 'deg': 1}) -extract_exafs = ExtractEXAFS(edge=1630, extra_from=1630, extra_to=1800, - poly_deg=1, kweight=0, m=0) +from orangecontrib.spectroscopy.preprocess import ( + XASnormalization, + ExtractEXAFS, + NoEdgejumpProvidedException, +) +from orangecontrib.spectroscopy.tests.test_preprocess import ( + TestCommonIndpSamplesMixin, + SMALLER_COLLAGEN, +) + + +xas_norm_collagen = XASnormalization( + edge=1630, + preedge_dict={'from': 1000, 'to': 1300, 'deg': 1}, + postedge_dict={'from': 1650, 'to': 1700, 'deg': 1}, +) +extract_exafs = ExtractEXAFS( + edge=1630, extra_from=1630, extra_to=1800, poly_deg=1, kweight=0, m=0 +) class ExtractEXAFSUsage(PreprocessorList): """ExtractEXAFS needs previous XAS normalization""" + def __init__(self): - super().__init__(preprocessors=[xas_norm_collagen, - extract_exafs]) + super().__init__(preprocessors=[xas_norm_collagen, extract_exafs]) class TestXASnormalization(unittest.TestCase, TestCommonIndpSamplesMixin): - preprocessors = [xas_norm_collagen, ExtractEXAFSUsage()] data = SMALLER_COLLAGEN def test_flat(self): - domain = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in [6800., 6940., 7060., 7400., 7800., 8000.]]) + domain = Orange.data.Domain( + [ + Orange.data.ContinuousVariable(str(w)) + for w in [6800.0, 6940.0, 7060.0, 7400.0, 7800.0, 8000.0] + ] + ) data = Table.from_numpy(domain, [[0.2, 0.2, 0.8, 0.8, 0.8, 0.8]]) - f = XASnormalization(edge=7000., - preedge_dict={'from': 6800., 'to': 6950., 'deg': 1}, - postedge_dict={'from': 7050., 'to': 8000., 'deg': 2}) + f = XASnormalization( + edge=7000.0, + preedge_dict={'from': 6800.0, 'to': 6950.0, 'deg': 1}, + postedge_dict={'from': 7050.0, 'to': 8000.0, 'deg': 2}, + ) fdata = f(data) - numpy.testing.assert_almost_equal(fdata.X, [[0., 0., 1., 1., 1., 1.]]) + numpy.testing.assert_almost_equal(fdata.X, [[0.0, 0.0, 1.0, 1.0, 1.0, 1.0]]) numpy.testing.assert_almost_equal(fdata.metas, [[0.6]]) class TestExtractEXAFS(unittest.TestCase): - def test_edgejump_exception(self): - - domain = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in [6800., 6940., 7060., 7400., 7800., 8000.]]) - spectra = [[0., 0., 1., 1., 1., 1.]] + domain = Orange.data.Domain( + [ + Orange.data.ContinuousVariable(str(w)) + for w in [6800.0, 6940.0, 7060.0, 7400.0, 7800.0, 8000.0] + ] + ) + spectra = [[0.0, 0.0, 1.0, 1.0, 1.0, 1.0]] data = Table.from_numpy(domain, spectra) - test_edge = 7000. - test_extra_from = 7002. - test_extra_to = 7500. + test_edge = 7000.0 + test_extra_from = 7002.0 + test_extra_to = 7500.0 test_poly_deg = 7 test_kweight = 2 test_m = 2 with self.assertRaises(NoEdgejumpProvidedException): - extra = ExtractEXAFS(edge=test_edge, extra_from=test_extra_from, extra_to=test_extra_to, - poly_deg=test_poly_deg, kweight=test_kweight, m=test_m) + extra = ExtractEXAFS( + edge=test_edge, + extra_from=test_extra_from, + extra_to=test_extra_to, + poly_deg=test_poly_deg, + kweight=test_kweight, + m=test_m, + ) _ = extra(data) def test_file(self): data = Table("exafs-test.tab") - test_edge = 20020. + test_edge = 20020.0 test_extra_from = 20020.0 test_extra_to = 20990.0 test_poly_deg = 8 test_kweight = 2 test_m = 0 - extra = ExtractEXAFS(edge=test_edge, extra_from=test_extra_from, extra_to=test_extra_to, - poly_deg=test_poly_deg, kweight=test_kweight, m=test_m) + extra = ExtractEXAFS( + edge=test_edge, + extra_from=test_extra_from, + extra_to=test_extra_to, + poly_deg=test_poly_deg, + kweight=test_kweight, + m=test_m, + ) exafs = extra(data) numpy.testing.assert_almost_equal( - [-3.46450033e-01, -3.45888957e-01, -3.44362296e-01, -3.41912861e-01, - -3.38582017e-01, -3.34409725e-01, -3.29434571e-01, -3.23693808e-01], exafs.X[0, :8], - decimal=3) + [ + -3.46450033e-01, + -3.45888957e-01, + -3.44362296e-01, + -3.41912861e-01, + -3.38582017e-01, + -3.34409725e-01, + -3.29434571e-01, + -3.23693808e-01, + ], + exafs.X[0, :8], + decimal=3, + ) diff --git a/orangecontrib/spectroscopy/tests/time_interpolation.py b/orangecontrib/spectroscopy/tests/time_interpolation.py index 340cec501..2dc548b3a 100644 --- a/orangecontrib/spectroscopy/tests/time_interpolation.py +++ b/orangecontrib/spectroscopy/tests/time_interpolation.py @@ -4,8 +4,11 @@ from Orange.data import Table from orangecontrib.spectroscopy.tests.bigdata import dust, spectra20nea -from orangecontrib.spectroscopy.preprocess import Interpolate, \ - interp1d_with_unknowns_numpy, interp1d_with_unknowns_scipy +from orangecontrib.spectroscopy.preprocess import ( + Interpolate, + interp1d_with_unknowns_numpy, + interp1d_with_unknowns_scipy, +) from orangecontrib.spectroscopy.data import getx @@ -27,7 +30,8 @@ def test_time(): intp.interpfn = interp1d_with_unknowns_scipy interpolated = intp(data) print("nan handling with scipy", time.time() - t) - assert(not np.any(np.isnan(interpolated.X))) + assert not np.any(np.isnan(interpolated.X)) + if __name__ == "__main__": test_time() diff --git a/orangecontrib/spectroscopy/tests/time_irfft.py b/orangecontrib/spectroscopy/tests/time_irfft.py index 619169e53..aeb854ba8 100644 --- a/orangecontrib/spectroscopy/tests/time_irfft.py +++ b/orangecontrib/spectroscopy/tests/time_irfft.py @@ -6,18 +6,24 @@ from orangecontrib.spectroscopy.io.agilent import agilentMosaicIFGReader -from orangecontrib.spectroscopy.irfft import (IRFFT, MultiIRFFT, PhaseCorrection, - PeakSearch, ApodFunc, - ) +from orangecontrib.spectroscopy.irfft import ( + IRFFT, + MultiIRFFT, + PhaseCorrection, + PeakSearch, + ApodFunc, +) from orangecontrib.spectroscopy.tests.test_readers import initialize_reader -FILENAMES = ["agilent/4_noimage_agg256.seq", - "C:\\Users\\reads\\tmp\\aff-testdata\\2017-11-10 4X-25X\\2017-11-10 4X-25X.dmt", - "/data/staff/reads/aff-testdata/2017-11-10 4X-25X/2017-11-10 4x-25x.dmt", - "/data/staff/reads/USAF 25X Mosaic/usaf 25x mosaic.dmt" - ] +FILENAMES = [ + "agilent/4_noimage_agg256.seq", + "C:\\Users\\reads\\tmp\\aff-testdata\\2017-11-10 4X-25X\\2017-11-10 4X-25X.dmt", + "/data/staff/reads/aff-testdata/2017-11-10 4X-25X/2017-11-10 4x-25x.dmt", + "/data/staff/reads/USAF 25X Mosaic/usaf 25x mosaic.dmt", +] FILENAMES_FAST = FILENAMES[0:1] + def load_data(filename): if filename[-3:] == 'dmt': # This reader will only be selected manually due to shared .dmt extension @@ -34,19 +40,23 @@ def test_time_multi_fft(fn): except (IOError, OSError): print("Skipping, not present") return - dx_ag = (1 / 1.57980039e+04 / 2) * 4 - fft = IRFFT(dx=dx_ag, - apod_func=ApodFunc.BLACKMAN_HARRIS_4, - zff=1, - phase_res=None, - phase_corr=PhaseCorrection.MERTZ, - peak_search=PeakSearch.MINIMUM) - mfft = MultiIRFFT(dx=dx_ag, - apod_func=ApodFunc.BLACKMAN_HARRIS_4, - zff=1, - phase_res=None, - phase_corr=PhaseCorrection.MERTZ, - peak_search=PeakSearch.MINIMUM) + dx_ag = (1 / 1.57980039e04 / 2) * 4 + fft = IRFFT( + dx=dx_ag, + apod_func=ApodFunc.BLACKMAN_HARRIS_4, + zff=1, + phase_res=None, + phase_corr=PhaseCorrection.MERTZ, + peak_search=PeakSearch.MINIMUM, + ) + mfft = MultiIRFFT( + dx=dx_ag, + apod_func=ApodFunc.BLACKMAN_HARRIS_4, + zff=1, + phase_res=None, + phase_corr=PhaseCorrection.MERTZ, + peak_search=PeakSearch.MINIMUM, + ) print(data.X.shape) diff --git a/orangecontrib/spectroscopy/tests/time_preprocess.py b/orangecontrib/spectroscopy/tests/time_preprocess.py index 672a2c915..13aa4036e 100644 --- a/orangecontrib/spectroscopy/tests/time_preprocess.py +++ b/orangecontrib/spectroscopy/tests/time_preprocess.py @@ -3,9 +3,8 @@ import numpy as np from Orange.data import Table -from orangecontrib.spectroscopy.tests.bigdata import dust, spectra20nea from orangecontrib.spectroscopy.preprocess import Normalize -from orangecontrib.spectroscopy.data import getx +from orangecontrib.spectroscopy.tests.bigdata import dust, spectra20nea def test_normalization_vector(): @@ -22,7 +21,8 @@ def test_normalization_vector(): t = time.time() r = p(data) print("with interpolate", time.time() - t) - assert(np.all(np.argwhere(np.isnan(r.X)) == [[0, 2]])) + assert np.all(np.argwhere(np.isnan(r.X)) == [[0, 2]]) + if __name__ == "__main__": test_normalization_vector() diff --git a/orangecontrib/spectroscopy/tests/util.py b/orangecontrib/spectroscopy/tests/util.py index 4301dd67e..2e728080a 100644 --- a/orangecontrib/spectroscopy/tests/util.py +++ b/orangecontrib/spectroscopy/tests/util.py @@ -19,24 +19,25 @@ def hold_modifiers(widget, modifiers): def smaller_data(data, nth_instance, nth_feature): - natts = [a for i, a in enumerate(data.domain.attributes) - if i % nth_feature == 0] + natts = [a for i, a in enumerate(data.domain.attributes) if i % nth_feature == 0] data = data[::nth_instance] - ndomain = Orange.data.Domain(natts, data.domain.class_vars, - metas=data.domain.metas) + ndomain = Orange.data.Domain(natts, data.domain.class_vars, metas=data.domain.metas) return data.transform(ndomain) @contextmanager def set_png_graph_save(): with named_file("", suffix=".png") as fname: - with patch("AnyQt.QtWidgets.QFileDialog.getSaveFileName", - lambda *x: (fname, 'Portable Network Graphics (*.png)')): + with patch( + "AnyQt.QtWidgets.QFileDialog.getSaveFileName", + lambda *x: (fname, 'Portable Network Graphics (*.png)'), + ): yield fname def spectra_table(wavenumbers, *args, **kwargs): - domain = Orange.data.Domain([Orange.data.ContinuousVariable(str(w)) - for w in wavenumbers]) + domain = Orange.data.Domain( + [Orange.data.ContinuousVariable(str(w)) for w in wavenumbers] + ) data = Orange.data.Table.from_numpy(domain, *args, **kwargs) return data diff --git a/orangecontrib/spectroscopy/util.py b/orangecontrib/spectroscopy/util.py index 7bef47e03..f25c41ead 100644 --- a/orangecontrib/spectroscopy/util.py +++ b/orangecontrib/spectroscopy/util.py @@ -10,7 +10,7 @@ def getx(data): x = np.arange(len(data.domain.attributes), dtype=np.float64) try: x = np.array([float(a.name) for a in data.domain.attributes]) - except: + except: # noqa: E722 pass return x diff --git a/orangecontrib/spectroscopy/utils/__init__.py b/orangecontrib/spectroscopy/utils/__init__.py index e741a3d81..ad0763374 100644 --- a/orangecontrib/spectroscopy/utils/__init__.py +++ b/orangecontrib/spectroscopy/utils/__init__.py @@ -1,12 +1,14 @@ import numpy as np -from Orange.data import Domain, Table +from Orange.data import Domain MAP_X_VAR = "map_x" MAP_Y_VAR = "map_y" -def apply_columns_numpy(array, function, selector=None, chunk_size=10 ** 7, callback=None): +def apply_columns_numpy( + array, function, selector=None, chunk_size=10**7, callback=None +): """Split the array by columns, applies selection and then the function. Returns output equivalent to function(array[selector]) """ @@ -32,14 +34,14 @@ def values_to_linspace(vals): vals = np.unique(vals) # returns sorted array if len(vals) == 1: return vals[0], vals[0], 1 - minabsdiff = (vals[-1] - vals[0])/(len(vals)*100) + minabsdiff = (vals[-1] - vals[0]) / (len(vals) * 100) diffs = np.diff(vals) diffs = diffs[diffs > minabsdiff] first_valid = diffs[0] # allow for a percent mismatch - diffs = diffs[diffs < first_valid*1.01] + diffs = diffs[diffs < first_valid * 1.01] step = np.mean(diffs) - size = int(round((vals[-1]-vals[0])/step) + 1) + size = int(round((vals[-1] - vals[0]) / step) + 1) return vals[0], vals[-1], size return None @@ -57,12 +59,12 @@ def location_values(vals, linspace): def index_values(vals, linspace): - """ Remap values into index of array defined by linspace. """ + """Remap values into index of array defined by linspace.""" return index_values_nan(vals, linspace)[0] def index_values_nan(vals, linspace): - """ Remap values into index of array defined by linspace. + """Remap values into index of array defined by linspace. Returns two arrays: first contains the indices, the second invalid values.""" positions = location_values(vals, linspace) return np.round(positions).astype(int), np.isnan(positions) diff --git a/orangecontrib/spectroscopy/utils/binning.py b/orangecontrib/spectroscopy/utils/binning.py index 01c3f4b7b..d8719a59f 100644 --- a/orangecontrib/spectroscopy/utils/binning.py +++ b/orangecontrib/spectroscopy/utils/binning.py @@ -1,11 +1,8 @@ import bottleneck import numpy as np - from Orange.data import Domain, Table -from orangecontrib.spectroscopy.utils import index_values, values_to_linspace, \ - get_ndim_hyperspec, axes_to_ndim_linspace, \ - NanInsideHypercube, InvalidAxisException +from orangecontrib.spectroscopy.utils import get_ndim_hyperspec, axes_to_ndim_linspace from orangecontrib.spectroscopy.utils.skimage.shape import view_as_blocks diff --git a/orangecontrib/spectroscopy/utils/pyproject.toml b/orangecontrib/spectroscopy/utils/pyproject.toml new file mode 100644 index 000000000..0940c48f5 --- /dev/null +++ b/orangecontrib/spectroscopy/utils/pyproject.toml @@ -0,0 +1,4 @@ +[tool.ruff] +extend = "../../../pyproject.toml" +# Exclude vendored code from linting +extend-exclude = ["pymca5", "skimage", "spc", "specio"] \ No newline at end of file diff --git a/orangecontrib/spectroscopy/widgets/__init__.py b/orangecontrib/spectroscopy/widgets/__init__.py index d3a1549a1..faef0787c 100644 --- a/orangecontrib/spectroscopy/widgets/__init__.py +++ b/orangecontrib/spectroscopy/widgets/__init__.py @@ -15,15 +15,13 @@ # make htmlhelp # inside doc folder ("{DEVELOP_ROOT}/doc/build/htmlhelp/index.html", None), - # Documentation included in wheel # Correct DATA_FILES entry is needed in setup.py and documentation has to be built # before the wheel is created. ("{}/help/orange-spectroscopy/index.html".format(sysconfig.get_path("data")), None), - # Online documentation url, used when the local documentation is not available. # Url should point to a page with a section Widgets. This section should # includes links to documentation pages of each widget. Matching is # performed by comparing link caption to widget name. - ("https://orange-spectroscopy.readthedocs.io/en/latest/", "") + ("https://orange-spectroscopy.readthedocs.io/en/latest/", ""), ) diff --git a/orangecontrib/spectroscopy/widgets/gui.py b/orangecontrib/spectroscopy/widgets/gui.py index 31429096d..5392540e3 100644 --- a/orangecontrib/spectroscopy/widgets/gui.py +++ b/orangecontrib/spectroscopy/widgets/gui.py @@ -30,7 +30,7 @@ def pixel_decimals(viewbox): """ try: xpixel, ypixel = viewbox.viewPixelSize() - except: + except: # noqa: E722 xpixel, ypixel = 0, 0 return pixels_to_decimals(xpixel), pixels_to_decimals(ypixel) @@ -54,7 +54,6 @@ def round_virtual_pixels(v, range, pixels=10000): class AnyOrEmptyValidator(QValidator): - def __init__(self, parent, allow_empty, bottom, top, default_text): super().__init__(parent) self.allow_empty = allow_empty @@ -95,23 +94,43 @@ def validate(self, s, pos): class FloatOrEmptyValidator(AnyOrEmptyValidator): - - def __init__(self, parent, allow_empty=False, bottom=float("-inf"), top=float("inf"), - default_text=""): + def __init__( + self, + parent, + allow_empty=False, + bottom=float("-inf"), + top=float("inf"), + default_text="", + ): self.dv = QDoubleValidator(parent) self.valid_type = float - super().__init__(parent, allow_empty=allow_empty, bottom=bottom, top=top, - default_text=default_text) + super().__init__( + parent, + allow_empty=allow_empty, + bottom=bottom, + top=top, + default_text=default_text, + ) class IntOrEmptyValidator(AnyOrEmptyValidator): - - def __init__(self, parent, allow_empty=False, bottom=-2147483647, top=2147483647, - default_text=""): + def __init__( + self, + parent, + allow_empty=False, + bottom=-2147483647, + top=2147483647, + default_text="", + ): self.dv = QIntValidator(parent) self.valid_type = int - super().__init__(parent, allow_empty=allow_empty, bottom=bottom, top=top, - default_text=default_text) + super().__init__( + parent, + allow_empty=allow_empty, + bottom=bottom, + top=top, + default_text=default_text, + ) def floatornone(a): @@ -124,7 +143,7 @@ def floatornone(a): def decimalornone(a): try: # because also intermediate values are passed forward return Decimal(a) - except: + except: # noqa: E722 return None @@ -145,7 +164,6 @@ def str_or_empty(val): class CallFrontLineEditCustomConversion(ControlledCallFront): - def __init__(self, control, valToStr): super().__init__(control) self.valToStr = valToStr @@ -163,7 +181,6 @@ def __init__(self, parent=None): class LineEdit(LineEditMarkFinished): - newInput = Signal(str) """ Emitted when the editing was finished and contents actually changed""" @@ -194,106 +211,154 @@ def focusInEvent(self, *e): def sizeHint(self): sh = super().sizeHint() - return QSize(int(sh.width()*self.sizeHintFactor), sh.height()) + return QSize(int(sh.width() * self.sizeHintFactor), sh.height()) -def connect_line_edit_finished(lineedit, master, value, valueToStr=str, valueType=str, callback=None): +def connect_line_edit_finished( + lineedit, master, value, valueToStr=str, valueType=str, callback=None +): # callback is only for compatibility with the old code update_value = ValueCallback(master, value, valueType) # save the value - update_control = CallFrontLineEditCustomConversion(lineedit, valueToStr) # update control + update_control = CallFrontLineEditCustomConversion( + lineedit, valueToStr + ) # update control update_value.opposite = update_control update_control(getdeepattr(master, value)) # set the first value master.connect_control(value, update_control) - lineedit.newInput.connect(lambda x: (update_value(x), - callback() if callback is not None else None)) + lineedit.newInput.connect( + lambda x: (update_value(x), callback() if callback is not None else None) + ) -def lineEditUpdateWhenFinished(parent, master, value, valueToStr=str, valueType=str, validator=None, callback=None): - """ Line edit that only calls callback when update is finished """ +def lineEditUpdateWhenFinished( + parent, master, value, valueToStr=str, valueType=str, validator=None, callback=None +): + """Line edit that only calls callback when update is finished""" ledit = LineEdit(parent) ledit.setValidator(validator) if value: - connect_line_edit_finished(ledit, master, value, valueToStr=valueToStr, valueType=valueType, callback=callback) + connect_line_edit_finished( + ledit, + master, + value, + valueToStr=valueToStr, + valueType=valueType, + callback=callback, + ) return ledit -def lineEditValidator(widget, master, value, validator=None, valueType=None, - valueToStr=str, **kwargs): - le = lineEditUpdateWhenFinished(widget, master, value, validator=validator, valueType=valueType, - valueToStr=valueToStr, **kwargs) +def lineEditValidator( + widget, master, value, validator=None, valueType=None, valueToStr=str, **kwargs +): + le = lineEditUpdateWhenFinished( + widget, + master, + value, + validator=validator, + valueType=valueType, + valueToStr=valueToStr, + **kwargs, + ) return le def set_numeric_le(le): - """ Smaller line edit that does not expand. """ + """Smaller line edit that does not expand.""" le.sizeHintFactor = 0.8 le.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)) return le def lineEditIntOrNone(widget, master, value, **kwargs): - le = lineEditValidator(widget, master, value, - validator=IntOrEmptyValidator(master), - valueType=intornone, - valueToStr=str_or_empty, - **kwargs) + le = lineEditValidator( + widget, + master, + value, + validator=IntOrEmptyValidator(master), + valueType=intornone, + valueToStr=str_or_empty, + **kwargs, + ) return le def lineEditFloatOrNone(widget, master, value, **kwargs): - le = lineEditValidator(widget, master, value, - validator=FloatOrEmptyValidator(master, allow_empty=True), - valueType=floatornone, - valueToStr=str_or_empty, - **kwargs) + le = lineEditValidator( + widget, + master, + value, + validator=FloatOrEmptyValidator(master, allow_empty=True), + valueType=floatornone, + valueToStr=str_or_empty, + **kwargs, + ) set_numeric_le(le) return le -def lineEditFloatRange(widget, master, value, bottom=float("-inf"), top=float("inf"), default=0., **kwargs): - le = lineEditValidator(widget, master, value, - validator=FloatOrEmptyValidator(master, allow_empty=False, - bottom=bottom, top=top, default_text=str(default)), - valueType=Decimal, # every text need to be a valid float before saving setting - valueToStr=str, - **kwargs) +def lineEditFloatRange( + widget, master, value, bottom=float("-inf"), top=float("inf"), default=0.0, **kwargs +): + le = lineEditValidator( + widget, + master, + value, + validator=FloatOrEmptyValidator( + master, allow_empty=False, bottom=bottom, top=top, default_text=str(default) + ), + valueType=Decimal, # every text need to be a valid float before saving setting + valueToStr=str, + **kwargs, + ) le.set_default = lambda v: le.validator().setDefault(str(v)) set_numeric_le(le) return le -def lineEditIntRange(widget, master, value, bottom=-2147483647, top=2147483647, - default=0, **kwargs): - le = lineEditValidator(widget, master, value, - validator=IntOrEmptyValidator(master, allow_empty=False, - bottom=bottom, top=top, - default_text=str(default)), - valueType=int, # every text need to be a valid before saving - valueToStr=str, - **kwargs) +def lineEditIntRange( + widget, master, value, bottom=-2147483647, top=2147483647, default=0, **kwargs +): + le = lineEditValidator( + widget, + master, + value, + validator=IntOrEmptyValidator( + master, allow_empty=False, bottom=bottom, top=top, default_text=str(default) + ), + valueType=int, # every text need to be a valid before saving + valueToStr=str, + **kwargs, + ) le.set_default = lambda v: le.validator().setDefault(str(v)) set_numeric_le(le) return le -def lineEditDecimalOrNone(widget, master, value, bottom=float("-inf"), top=float("inf"), default=0., **kwargs): - le = lineEditValidator(widget, master, value, - validator=FloatOrEmptyValidator(master, allow_empty=True, - bottom=bottom, top=top, default_text=str(default)), - valueType=decimalornone, # every text need to be a valid float before saving setting - valueToStr=str_or_empty, - **kwargs) +def lineEditDecimalOrNone( + widget, master, value, bottom=float("-inf"), top=float("inf"), default=0.0, **kwargs +): + le = lineEditValidator( + widget, + master, + value, + validator=FloatOrEmptyValidator( + master, allow_empty=True, bottom=bottom, top=top, default_text=str(default) + ), + valueType=decimalornone, # every text need to be a valid float before saving setting + valueToStr=str_or_empty, + **kwargs, + ) le.set_default = lambda v: le.validator().setDefault(str(v)) set_numeric_le(le) return le class MovableVline(pg.UIGraphicsItem): - sigMoveFinished = Signal(object) sigMoved = Signal(object) - def __init__(self, position=0., label="", color=(225, 0, 0), report=None): + def __init__(self, position=0.0, label="", color=(225, 0, 0), report=None): pg.UIGraphicsItem.__init__(self) self.moving = False self.mouseHovering = False @@ -315,7 +380,9 @@ def __init__(self, position=0., label="", color=(225, 0, 0), report=None): self.setLabel(label) self.line.sigPositionChangeFinished.connect(self._moveFinished) - self.line.sigPositionChanged.connect(lambda: (self._moved(), self.sigMoved.emit(self.value()))) + self.line.sigPositionChanged.connect( + lambda: (self._moved(), self.sigMoved.emit(self.value())) + ) self._lastTransform = None @@ -334,8 +401,8 @@ def value(self): return self.line.value() def rounded_value(self): - """ Round the value according to current view on the graph. - Return a decimal.Decimal object """ + """Round the value according to current view on the graph. + Return a decimal.Decimal object""" v = self.value() dx, dy = pixel_decimals(self.getViewBox()) if v is not None: @@ -394,13 +461,11 @@ def paint(self, p, *args): class LineCallFront(ControlledCallFront): - def action(self, value): self.control.setValue(floatornone(value)) class ValueTransform(metaclass=ABCMeta): - @abstractmethod def transform(self, v): """Transform the argument""" @@ -411,7 +476,6 @@ def inverse(self, v): class ValueCallbackTransform(ControlledCallback): - def __call__(self, value): self.acyclic_setattr(value) @@ -426,10 +490,12 @@ def connect_settings(master, value, value2, transform=None): :param value2: name of the second value :param transform: a transform from value to value2 (an instance of ValueTransform) """ - update_value = ValueCallbackTransform(master, value, - f=transform.inverse if transform is not None else None) - update_value2 = ValueCallbackTransform(master, value2, - f=transform.transform if transform is not None else None) + update_value = ValueCallbackTransform( + master, value, f=transform.inverse if transform is not None else None + ) + update_value2 = ValueCallbackTransform( + master, value2, f=transform.transform if transform is not None else None + ) update_value.opposite = update_value2 update_value2.opposite = update_value update_value2(getdeepattr(master, value)) @@ -447,7 +513,6 @@ def connect_line(line, master, value): class XPosLineEdit(QWidget, OWComponent): - edited = Signal() focusIn = Signal() @@ -477,7 +542,6 @@ def focusInEvent(self, *e): class VerticalPeakLine(pg.InfiniteLine): - sigDeleteRequested = Signal(object) def __init__(self, pos=None): @@ -503,8 +567,11 @@ def request_deletion(self): def contextMenuEvent(self, ev): menu = QMenu() delete = QAction( - "Delete", self, shortcut=Qt.Key_Delete, checkable=False, - triggered=lambda x: self.request_deletion() + "Delete", + self, + shortcut=Qt.Key_Delete, + checkable=False, + triggered=lambda x: self.request_deletion(), ) menu.addAction(delete) menu.exec(ev.screenPos()) diff --git a/orangecontrib/spectroscopy/widgets/line_geometry.py b/orangecontrib/spectroscopy/widgets/line_geometry.py index 8d13802e0..9406c9612 100644 --- a/orangecontrib/spectroscopy/widgets/line_geometry.py +++ b/orangecontrib/spectroscopy/widgets/line_geometry.py @@ -54,10 +54,10 @@ def intersect_line_segments(x1, y1, x2, y2, x3, y3, x4, y4): This implementation builds intermediate arrays that are bigger than the input array. """ - D = ((y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1)) + D = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) with np.errstate(divide='ignore'): - ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3)) / D - ub = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3)) / D + ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / D + ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / D return (D != 0) * (ua < 1) * (0 < ua) * (ub < 1) * (0 < ub) @@ -79,9 +79,9 @@ def intersect_curves(x, ys, q1, q2): xp = rolling_window(x, 2) ysp = rolling_window(ys, 2) - r = intersect_line_segments(xp[:, 0], ysp[:, :, 0], - xp[:, 1], ysp[:, :, 1], - q1[0], q1[1], q2[0], q2[1]) + r = intersect_line_segments( + xp[:, 0], ysp[:, :, 0], xp[:, 1], ysp[:, :, 1], q1[0], q1[1], q2[0], q2[1] + ) return np.any(r, axis=1) @@ -121,13 +121,19 @@ def distance_line_segment(x1, y1, x2, y2, x3, y3): to a point (x3, y3). """ with np.errstate(divide='ignore', invalid='ignore'): - u = ((x3 - x1) * (x2-x1) + (y3 - y1) * (y2 - y1)) / ((x1-x2)**2 + (y1-y2)**2) - xc = x1 + u*(x2 - x1) - yc = y1 + u*(y2 - y1) - return np.where((u <= 1) * (u >= 0), - ((x3 - xc)**2 + (y3-yc)**2)**0.5, # distance to a point on line - np.fmin(((x3 - x1)**2 + (y3-y1)**2)**0.5, # closest point - ((x3 - x1)**2 + (y3-y1)**2)**0.5)) + u = ((x3 - x1) * (x2 - x1) + (y3 - y1) * (y2 - y1)) / ( + (x1 - x2) ** 2 + (y1 - y2) ** 2 + ) + xc = x1 + u * (x2 - x1) + yc = y1 + u * (y2 - y1) + return np.where( + (u <= 1) * (u >= 0), + ((x3 - xc) ** 2 + (y3 - yc) ** 2) ** 0.5, # distance to a point on line + np.fmin( + ((x3 - x1) ** 2 + (y3 - y1) ** 2) ** 0.5, # closest point + ((x3 - x1) ** 2 + (y3 - y1) ** 2) ** 0.5, + ), + ) def distance_curves(x, ys, q1): @@ -146,16 +152,18 @@ def distance_curves(x, ys, q1): xp = rolling_window(x, 2) ysp = rolling_window(ys, 2) - r = bottleneck.nanmin(distance_line_segment(xp[:, 0], ysp[:, :, 0], - xp[:, 1], ysp[:, :, 1], - q1[0], q1[1]), axis=1) + r = bottleneck.nanmin( + distance_line_segment( + xp[:, 0], ysp[:, :, 0], xp[:, 1], ysp[:, :, 1], q1[0], q1[1] + ), + axis=1, + ) return r def is_left(l0x, l0y, l1x, l1y, px, py): - return (l1x - l0x)*(py - l0y) \ - - (px - l0x)*(l1y - l0y) + return (l1x - l0x) * (py - l0y) - (px - l0x) * (l1y - l0y) def in_polygon(point, polygon): @@ -180,13 +188,13 @@ def in_polygon(point, polygon): left = is_left(pp[0][:, 0], pp[1][:, 0], pp[0][:, 1], pp[1][:, 1], x, y) upward_crossing = (pp[1][:, 0] <= y) * (y < pp[1][:, 1]) downward_crossing = (pp[1][:, 0] > y) * (y >= pp[1][:, 1]) - wn = np.sum((left > 0) * (upward_crossing), axis=-1) \ - - np.sum((left < 0) * (downward_crossing), axis=-1) + wn = np.sum((left > 0) * (upward_crossing), axis=-1) - np.sum( + (left < 0) * (downward_crossing), axis=-1 + ) return wn != 0 if __name__ == "__main__": - import Orange from orangecontrib.spectroscopy.data import getx import time @@ -206,11 +214,11 @@ def in_polygon(point, polygon): t = time.time() intc = np.where(intersect_curves(x, ys, np.array([0, 1.0]), np.array([3000, 1.0]))) - print(time.time()-t) + print(time.time() - t) print(intc) t = time.time() - #dists = [ distance_curves(x, ys[i:i+1], np.array([910, 1.0])) for i in range(len(ys)-1) ] + # dists = [ distance_curves(x, ys[i:i+1], np.array([910, 1.0])) for i in range(len(ys)-1) ] dists = distance_curves(x, ys, np.array([910, 1.0])) print(time.time() - t) print(dists) diff --git a/orangecontrib/spectroscopy/widgets/owaverage.py b/orangecontrib/spectroscopy/widgets/owaverage.py index 63eb002c5..742749da7 100644 --- a/orangecontrib/spectroscopy/widgets/owaverage.py +++ b/orangecontrib/spectroscopy/widgets/owaverage.py @@ -13,8 +13,7 @@ class OWAverage(OWWidget): name = "Average Spectra" # Short widget description - description = ( - "Calculates averages.") + description = "Calculates averages." icon = "icons/average.svg" @@ -38,11 +37,18 @@ def __init__(self): self.data = None self.group_vars = DomainModel( - placeholder="None", separators=False, - valid_types=Orange.data.DiscreteVariable) + placeholder="None", + separators=False, + valid_types=Orange.data.DiscreteVariable, + ) self.group_view = gui.listView( - self.controlArea, self, "group_var", box="Group by", - model=self.group_vars, callback=self.grouping_changed) + self.controlArea, + self, + "group_var", + box="Group by", + model=self.group_vars, + callback=self.grouping_changed, + ) gui.auto_commit(self.controlArea, self, "autocommit", "Apply") @@ -70,13 +76,25 @@ def average_table(table): """ if len(table) == 0: return table - mean = bottleneck.nanmean(table.X, axis=0, ).reshape(1, -1).copy() - avg_table = Orange.data.Table.from_numpy(table.domain, - X=mean, - Y=np.atleast_2d(table.Y[0]).copy(), - metas=np.atleast_2d(table.metas[0]).copy()) - cont_vars = [var for var in table.domain.class_vars + table.domain.metas - if isinstance(var, Orange.data.ContinuousVariable)] + mean = ( + bottleneck.nanmean( + table.X, + axis=0, + ) + .reshape(1, -1) + .copy() + ) + avg_table = Orange.data.Table.from_numpy( + table.domain, + X=mean, + Y=np.atleast_2d(table.Y[0]).copy(), + metas=np.atleast_2d(table.metas[0]).copy(), + ) + cont_vars = [ + var + for var in table.domain.class_vars + table.domain.metas + if isinstance(var, Orange.data.ContinuousVariable) + ] with avg_table.unlocked(): for var in cont_vars: @@ -84,8 +102,11 @@ def average_table(table): col = table.get_column(index) avg_table[0, index] = np.nanmean(col) - other_vars = [var for var in table.domain.class_vars + table.domain.metas - if not isinstance(var, Orange.data.ContinuousVariable)] + other_vars = [ + var + for var in table.domain.class_vars + table.domain.metas + if not isinstance(var, Orange.data.ContinuousVariable) + ] for var in other_vars: index = table.domain.index(var) col = table.get_column(index) @@ -114,8 +135,9 @@ def commit(self): # Using "None" as in OWSelectRows # Values is required because FilterDiscrete doesn't have # negate keyword or IsDefined method - deffilter = Values(conditions=[FilterDiscrete(self.group_var, None)], - negate=True) + deffilter = Values( + conditions=[FilterDiscrete(self.group_var, None)], negate=True + ) v_table = self.average_table(deffilter(self.data)) parts.append(v_table) averages = Orange.data.Table.concatenate(parts, axis=0) @@ -124,4 +146,5 @@ def commit(self): if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWAverage).run(Orange.data.Table("iris")) diff --git a/orangecontrib/spectroscopy/widgets/owbin.py b/orangecontrib/spectroscopy/widgets/owbin.py index e31f9eb35..d75d97498 100644 --- a/orangecontrib/spectroscopy/widgets/owbin.py +++ b/orangecontrib/spectroscopy/widgets/owbin.py @@ -8,19 +8,21 @@ from Orange.widgets.widget import OWWidget, Input, Output, Msg from Orange.widgets import gui, settings -from orangecontrib.spectroscopy.utils import NanInsideHypercube, InvalidAxisException +from orangecontrib.spectroscopy.utils import InvalidAxisException from orangecontrib.spectroscopy.utils.binning import bin_hyperspectra, InvalidBlockShape from orangecontrib.spectroscopy.widgets.gui import lineEditIntRange MAX_DIMENSIONS = 5 + class OWBin(OWWidget): # Widget's name as displayed in the canvas name = "Bin" # Short widget description description = ( - "Bins a hyperspectral dataset by continous variable such as coordinates.") + "Bins a hyperspectral dataset by continous variable such as coordinates." + ) icon = "icons/bin.svg" @@ -65,33 +67,51 @@ def __init__(self): box = gui.widgetBox(self.controlArea, "Parameters") - gui.checkBox(box, self, "square_bin", - label="Use square bin shape", - callback=self._bin_changed) + gui.checkBox( + box, + self, + "square_bin", + label="Use square bin shape", + callback=self._bin_changed, + ) gui.separator(box) - gui.spin(box, self, "ndim", minv=1, maxv=MAX_DIMENSIONS, - label="Number of axes to bin:", - callback=self._dim_changed) + gui.spin( + box, + self, + "ndim", + minv=1, + maxv=MAX_DIMENSIONS, + label="Number of axes to bin:", + callback=self._dim_changed, + ) self.axes_box = gui.widgetBox(self.controlArea, "Axes") - self.xy_model = DomainModel(DomainModel.METAS | DomainModel.CLASSES, - valid_types=ContinuousVariable) + self.xy_model = DomainModel( + DomainModel.METAS | DomainModel.CLASSES, valid_types=ContinuousVariable + ) self.contextAboutToBeOpened.connect(self._init_interface_data) - common_options = dict(labelWidth=50, orientation=Qt.Horizontal, - sendSelectedValue=True) + common_options = dict( + labelWidth=50, orientation=Qt.Horizontal, sendSelectedValue=True + ) for i in range(MAX_DIMENSIONS): hbox = gui.hBox(self.axes_box) gui.comboBox( - hbox, self, f"attr_{i}", label=f"Axis {i}:", + hbox, + self, + f"attr_{i}", + label=f"Axis {i}:", callback=self._attr_changed, - model=self.xy_model, **common_options) - le = lineEditIntRange(hbox, self, f"bin_{i}", bottom=1, default=1, - callback=self._bin_changed) + model=self.xy_model, + **common_options, + ) + le = lineEditIntRange( + hbox, self, f"bin_{i}", bottom=1, default=1, callback=self._bin_changed + ) le.setFixedWidth(40) gui.separator(hbox, width=40) gui.widgetLabel(hbox, label="Bin size:", labelWidth=50) @@ -106,9 +126,8 @@ def __init__(self): gui.auto_commit(self.controlArea, self, "autocommit", "Send Data") - def _sanitize_bin_value(self): - pass #TODO make sure bin value is compatible with dataset + pass # TODO make sure bin value is compatible with dataset def _update_bins(self): if self.square_bin: @@ -223,4 +242,5 @@ def commit(self): if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWBin).run(Table("agilent/5_mosaic_agg1024.dmt")) diff --git a/orangecontrib/spectroscopy/widgets/owcos.py b/orangecontrib/spectroscopy/widgets/owcos.py index d1f74a334..bf1cbfcdb 100644 --- a/orangecontrib/spectroscopy/widgets/owcos.py +++ b/orangecontrib/spectroscopy/widgets/owcos.py @@ -6,7 +6,10 @@ from pyqtgraph import LabelItem import Orange.data -from Orange.widgets.visualize.utils.customizableplot import CommonParameterSetter, Updater +from Orange.widgets.visualize.utils.customizableplot import ( + CommonParameterSetter, + Updater, +) from Orange.widgets.visualize.utils.plotutils import PlotItem, GraphicsView, AxisItem from Orange.widgets.widget import OWWidget, Msg, Input, Output from Orange.widgets import gui, settings @@ -16,7 +19,10 @@ from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.widgets.owhyper import ImageColorLegend from orangecontrib.spectroscopy.widgets.owspectra import InteractiveViewBox -from orangecontrib.spectroscopy.widgets.gui import float_to_str_decimals as strdec, pixel_decimals +from orangecontrib.spectroscopy.widgets.gui import ( + float_to_str_decimals as strdec, + pixel_decimals, +) # put calculation widgets outside the class for easier reuse @@ -41,7 +47,7 @@ def calc_cos(table1, table2): sync = series1.T @ series2 / (len(series1) - 1) # Hilbert-Noda transformation matrix - i, j = np.ogrid[:len(series1), :len(series1)] + i, j = np.ogrid[: len(series1), : len(series1)] # TODO - We could use the code below after allowing numpy>2.0 # with np.errstate(divide='ignore'): @@ -130,9 +136,11 @@ def update_font_family(**settings): @property def axis_items(self): - return [self.master.left_plot.getAxis("left"), - self.master.top_plot.getAxis("top"), - self.master.cbarCOS.axis] + return [ + self.master.left_plot.getAxis("left"), + self.master.top_plot.getAxis("top"), + self.master.cbarCOS.axis, + ] class COS2DViewBox(InteractiveViewBox): @@ -152,8 +160,7 @@ class OWCos(OWWidget): name = "2D Correlation Plot" # Short widget description - description = ( - "Perform 2D correlation analysis with series spectra") + description = "Perform 2D correlation analysis with series spectra" icon = "icons/2dcos.svg" @@ -166,7 +173,9 @@ class Inputs: class Outputs: # TODO implement outputting the matrix - corr_matrix = Output("2D correlation matrix", Orange.misc.DistMatrix, dynamic=False) + corr_matrix = Output( + "2D correlation matrix", Orange.misc.DistMatrix, dynamic=False + ) class Error(OWWidget.Error): empty_data = Msg("Input data is empty.") @@ -187,22 +196,32 @@ class Warning(OWWidget.Warning): def __init__(self): super().__init__() self.parameter_setter = ParameterSetter(self) - VisualSettingsDialog( - self, self.parameter_setter.initial_settings - ) + VisualSettingsDialog(self, self.parameter_setter.initial_settings) self.cosmat = None self.data1 = None self.data2 = None # control area - gui.radioButtons(self.controlArea, self, "selector", - btnLabels=("Synchronous", "Asynchronous"), box="Plot type", - callback=self.plotCOS) - self.isocurve_spin = gui.spin(self.controlArea, self, "isonum", - minv=0, maxv=9, step=1, - label="Number of curves", box="Isocurves", - callback=self.plotCOS) + gui.radioButtons( + self.controlArea, + self, + "selector", + btnLabels=("Synchronous", "Asynchronous"), + box="Plot type", + callback=self.plotCOS, + ) + self.isocurve_spin = gui.spin( + self.controlArea, + self, + "isonum", + minv=0, + maxv=9, + step=1, + label="Number of curves", + box="Isocurves", + callback=self.plotCOS, + ) gui.rubber(self.controlArea) self.cursorPos = gui.label(self.controlArea, self, "", box="Crosshair") @@ -221,9 +240,15 @@ def __init__(self): ci.layout.setRowStretchFactor(1, 5) # image - self.cos2Dplot = PlotItem(viewBox=COS2DViewBox(self), - axisItems={"left": AxisItem("left"), "bottom": AxisItem("bottom"), - "right": AxisItem("right"), "top": AxisItem("top")}) + self.cos2Dplot = PlotItem( + viewBox=COS2DViewBox(self), + axisItems={ + "left": AxisItem("left"), + "bottom": AxisItem("bottom"), + "right": AxisItem("right"), + "top": AxisItem("top"), + }, + ) self.cos2Dplot.buttonsHidden = True ci.addItem(self.cos2Dplot, row=1, col=1) self.cos2Dplot.getAxis("left").setStyle(showValues=False) @@ -241,9 +266,15 @@ def __init__(self): self.vLine.setZValue(1000) # top spectrum plot - self.top_plot = PlotItem(viewBox=COS2DViewBox(self), - axisItems={"left": AxisItem("left"), "bottom": AxisItem("bottom"), - "right": AxisItem("right"), "top": AxisItem("top")}) + self.top_plot = PlotItem( + viewBox=COS2DViewBox(self), + axisItems={ + "left": AxisItem("left"), + "bottom": AxisItem("bottom"), + "right": AxisItem("right"), + "top": AxisItem("top"), + }, + ) ci.addItem(self.top_plot, row=0, col=1) # visual settings self.top_plot.showAxis("right") @@ -262,9 +293,15 @@ def __init__(self): self.top_vLine.setZValue(1000) # left spectrum plot - self.left_plot = PlotItem(viewBox=COS2DViewBox(self), - axisItems={"left": AxisItem("left"), "bottom": AxisItem("bottom"), - "right": AxisItem("right"), "top": AxisItem("top")}) + self.left_plot = PlotItem( + viewBox=COS2DViewBox(self), + axisItems={ + "left": AxisItem("left"), + "bottom": AxisItem("bottom"), + "right": AxisItem("right"), + "top": AxisItem("top"), + }, + ) ci.addItem(self.left_plot, row=1, col=0) # visual settings self.left_plot.showAxis("right") @@ -342,8 +379,14 @@ def handleNewSignals(self): if not d2: d2 = d1 - if (not d1 or not d2 or not d1.X.size or not d2.X.size or - np.all(np.isnan(d1.X)) or np.all(np.isnan(d2.X))): + if ( + not d1 + or not d2 + or not d1.X.size + or not d2.X.size + or np.all(np.isnan(d1.X)) + or np.all(np.isnan(d2.X)) + ): self.Error.empty_data() else: self.cosmat = calc_cos(d1, d2) @@ -398,7 +441,9 @@ def plotCOS(self): leftSPwn = self.cosmat[5] COSimage = pg.ImageItem(image=cosmat) - COSimage.setLevels([-1 * np.absolute(cosmat).max(), np.absolute(cosmat).max()]) + COSimage.setLevels( + [-1 * np.absolute(cosmat).max(), np.absolute(cosmat).max()] + ) COSimage.setLookupTable(np.array(colorcet.diverging_bwr_40_95_c42) * 255) self.cos2Dplot.addItem(COSimage) @@ -418,18 +463,28 @@ def plotCOS(self): ic = pg.IsocurveItem(data=cosmat, level=-level, pen=iso_pen_neg) ic.setParentItem(COSimage) - COSimage.setRect(QRectF(topSPwn.min(), - leftSPwn.min(), - (topSPwn.max() - topSPwn.min()), - (leftSPwn.max() - leftSPwn.min()))) - - self.cbarCOS.set_range(-1 * np.nanmax(np.absolute(cosmat)), np.nanmax(np.absolute(cosmat))) + COSimage.setRect( + QRectF( + topSPwn.min(), + leftSPwn.min(), + (topSPwn.max() - topSPwn.min()), + (leftSPwn.max() - leftSPwn.min()), + ) + ) + + self.cbarCOS.set_range( + -1 * np.nanmax(np.absolute(cosmat)), np.nanmax(np.absolute(cosmat)) + ) self.cbarCOS.set_colors(np.array(colorcet.diverging_bwr_40_95_c42) * 255) - left_indices = np.linspace(0, len(leftSP)-1, min(100, len(leftSP)), dtype=int) + left_indices = np.linspace( + 0, len(leftSP) - 1, min(100, len(leftSP)), dtype=int + ) for s in leftSP[left_indices]: - pt = pg.PlotCurveItem(s, leftSPwn, pen=pg.mkPen(color=(50, 50, 50), width=0.5)) + pt = pg.PlotCurveItem( + s, leftSPwn, pen=pg.mkPen(color=(50, 50, 50), width=0.5) + ) self.left_plot.addItem(pt, ignoreBounds=True) self.left_plot.plot(leftSP.mean(axis=0), leftSPwn, pen=p) @@ -437,10 +492,14 @@ def plotCOS(self): self.left_plot.setXRange(np.min(leftSP), np.max(leftSP)) - top_indices = np.linspace(0, len(topSP)-1, min(100, len(topSP)), dtype=int) + top_indices = np.linspace( + 0, len(topSP) - 1, min(100, len(topSP)), dtype=int + ) for s in topSP[top_indices]: - pt = pg.PlotCurveItem(topSPwn, s, pen=pg.mkPen(color=(50, 50, 50), width=0.5)) + pt = pg.PlotCurveItem( + topSPwn, s, pen=pg.mkPen(color=(50, 50, 50), width=0.5) + ) self.top_plot.addItem(pt, ignoreBounds=True) self.top_plot.plot(topSPwn, topSP.mean(axis=0), pen=p) diff --git a/orangecontrib/spectroscopy/widgets/owfft.py b/orangecontrib/spectroscopy/widgets/owfft.py index 352c7a88e..069021d86 100644 --- a/orangecontrib/spectroscopy/widgets/owfft.py +++ b/orangecontrib/spectroscopy/widgets/owfft.py @@ -22,9 +22,11 @@ def add_meta_to_table(data, var, values): newtable[:, var] = np.atleast_1d(values).reshape(-1, 1) return newtable + DEFAULT_HENE = 15797.337544 CHUNK_SIZE = 100 + class OWFFT(OWWidget): # Widget's name as displayed in the canvas name = "Interferogram to Spectrum" @@ -32,7 +34,8 @@ class OWFFT(OWWidget): # Short widget description description = ( "Performs Fast Fourier Transform on an interferogram, including " - "zero filling, apodization and phase correction.") + "zero filling, apodization and phase correction." + ) # An icon resource file path for this widget # (a path relative to the module where this widget is defined) @@ -61,7 +64,9 @@ class Outputs: zpd1 = settings.Setting(0) zpd2 = settings.Setting(0) apod_func = settings.Setting(1) - zff = settings.Setting(1) # an exponent for zero-filling factor, IRFFT() needs 2**zff + zff = settings.Setting( + 1 + ) # an exponent for zero-filling factor, IRFFT() needs 2**zff phase_corr = settings.Setting(0) phase_res_limit = settings.Setting(True) phase_resolution = settings.Setting(32) @@ -71,22 +76,26 @@ class Outputs: autocommit = settings.Setting(False) complexfft = settings.Setting(False) - sweep_opts = ("Single", - "Forward-Backward", - "Forward", - "Backward", - ) - - apod_opts = ("Boxcar (None)", # - "Blackman-Harris (3-term)", # - "Blackman-Harris (4-term)", # - "Blackman Nuttall (EP)") # - - phase_opts = ("Mertz", # - "Mertz Signed", # - "Stored Phase", # - "None (real/imag)", # - ) + sweep_opts = ( + "Single", + "Forward-Backward", + "Forward", + "Backward", + ) + + apod_opts = ( + "Boxcar (None)", # + "Blackman-Harris (3-term)", # + "Blackman-Harris (4-term)", # + "Blackman Nuttall (EP)", + ) # + + phase_opts = ( + "Mertz", # + "Mertz Signed", # + "Stored Phase", # + "None (real/imag)", # + ) # GUI definition: # a simple 'single column' GUI layout @@ -97,7 +106,9 @@ class Outputs: class Warning(OWWidget.Warning): # This is not actuully called anywhere at the moment phase_res_limit_low = Msg("Phase resolution limit too low") - complex_data_phase_zero = Msg("Phase data is not connected, thus it is considered zero.") + complex_data_phase_zero = Msg( + "Phase data is not connected, thus it is considered zero." + ) class Error(OWWidget.Error): fft_error = Msg("FFT error:\n{}") @@ -134,12 +145,16 @@ def __init__(self): grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) self.dx_auto_cb = gui.checkBox( - self.dataBox, self, "dx_auto", + self.dataBox, + self, + "dx_auto", label="Auto", callback=self.dx_auto_changed, - ) + ) self.dx_edit = gui.lineEdit( - self.dataBox, self, "dx", + self.dataBox, + self, + "dx", callback=self.setting_changed, valueType=float, disabled=self.dx_auto, @@ -152,45 +167,57 @@ def __init__(self): wl = gui.widgetLabel(self.dataBox, "Sweep Direction:") box = gui.comboBox( - self.dataBox, self, "sweeps", + self.dataBox, + self, + "sweeps", label=None, items=self.sweep_opts, callback=self.sweeps_changed, - disabled=self.auto_sweeps - ) + disabled=self.auto_sweeps, + ) cb2 = gui.checkBox( - self.dataBox, self, "auto_sweeps", + self.dataBox, + self, + "auto_sweeps", label="Auto", callback=self.sweeps_changed, - ) + ) grid.addWidget(wl, 1, 0, 1, 3) grid.addWidget(cb2, 2, 0) grid.addWidget(box, 2, 1, 1, 2) wl = gui.widgetLabel(self.dataBox, "ZPD Peak Search:") box = gui.comboBox( - self.dataBox, self, "peak_search", + self.dataBox, + self, + "peak_search", label=None, items=[name.title() for name, _ in irfft.PeakSearch.__members__.items()], callback=self.peak_search_changed, enabled=self.peak_search_enable, - ) + ) le1 = gui.lineEdit( - self.dataBox, self, "zpd1", + self.dataBox, + self, + "zpd1", callback=self.peak_search_changed, valueType=int, controlWidth=50, disabled=self.peak_search_enable, - ) + ) le2 = gui.lineEdit( - self.dataBox, self, "zpd2", + self.dataBox, + self, + "zpd2", callback=self.peak_search_changed, valueType=int, controlWidth=50, disabled=self.peak_search_enable, - ) + ) cb = gui.checkBox( - self.dataBox, self, "peak_search_enable", + self.dataBox, + self, + "peak_search_enable", label=None, callback=self.peak_search_changed, ) @@ -208,46 +235,59 @@ def __init__(self): layout.addWidget(self.optionsBox, 0, 1, 3, 1) self.complexfft_cb = gui.checkBox( - self.optionsBox, self, "complexfft", + self.optionsBox, + self, + "complexfft", label="Complex FFT", - callback=self.complex_fft_changed - ) + callback=self.complex_fft_changed, + ) box = gui.comboBox( - self.optionsBox, self, "apod_func", + self.optionsBox, + self, + "apod_func", label="Apodization function:", items=self.apod_opts, - callback=self.setting_changed - ) + callback=self.setting_changed, + ) box = gui.comboBox( - self.optionsBox, self, "zff", + self.optionsBox, + self, + "zff", label="Zero Filling Factor:", items=(2**n for n in range(10)), - callback=self.setting_changed - ) + callback=self.setting_changed, + ) box = gui.comboBox( - self.optionsBox, self, "phase_corr", + self.optionsBox, + self, + "phase_corr", label="Phase Correction:", items=self.phase_opts, - callback=self.setting_changed - ) + callback=self.setting_changed, + ) grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) le1 = gui.lineEdit( - self.optionsBox, self, "phase_resolution", + self.optionsBox, + self, + "phase_resolution", callback=self.setting_changed, - valueType=int, controlWidth=30 - ) + valueType=int, + controlWidth=30, + ) cb1 = gui.checkBox( - self.optionsBox, self, "phase_res_limit", + self.optionsBox, + self, + "phase_res_limit", label="Limit phase resolution to ", callback=self.setting_changed, - disables=le1 - ) + disables=le1, + ) lb1 = gui.widgetLabel(self.optionsBox, "cm-1") grid.addWidget(cb1, 0, 0) @@ -263,21 +303,29 @@ def __init__(self): grid = QGridLayout() grid.setContentsMargins(0, 0, 0, 0) le2 = gui.lineEdit( - self.outputBox, self, "out_limit1", + self.outputBox, + self, + "out_limit1", callback=self.out_limit_changed, - valueType=float, controlWidth=50 - ) + valueType=float, + controlWidth=50, + ) le3 = gui.lineEdit( - self.outputBox, self, "out_limit2", + self.outputBox, + self, + "out_limit2", callback=self.out_limit_changed, - valueType=float, controlWidth=50 - ) + valueType=float, + controlWidth=50, + ) cb2 = gui.checkBox( - self.outputBox, self, "limit_output", + self.outputBox, + self, + "limit_output", label="Limit spectral region:", callback=self.setting_changed, - disables=[le2, le3] - ) + disables=[le2, le3], + ) lb2 = gui.widgetLabel(self.outputBox, "-") lb3 = gui.widgetLabel(self.outputBox, "cm-1") grid.addWidget(cb2, 0, 0, 1, 6) @@ -300,9 +348,13 @@ def set_data(self, dataset): if dataset and dataset.X.size: self.data = dataset self.determine_sweeps() - self.info_type.setText('%d %s interferogram(s)' % - (dataset.X.shape[0], - (["Single"] + 3*["Forward-Backward"])[self.sweeps])) + self.info_type.setText( + '%d %s interferogram(s)' + % ( + dataset.X.shape[0], + (["Single"] + 3 * ["Forward-Backward"])[self.sweeps], + ) + ) self.info_pts.setText('%d points each' % dataset.X.shape[1]) self.check_metadata() else: @@ -392,13 +444,14 @@ def calculateFFT(self): self.Error.clear() self.Warning.clear() - fft_single = irfft.IRFFT(dx=self.dx, - apod_func=self.apod_func, - zff=2**self.zff, - phase_res=self.phase_resolution if self.phase_res_limit else None, - phase_corr=self.phase_corr, - peak_search=self.peak_search, - ) + fft_single = irfft.IRFFT( + dx=self.dx, + apod_func=self.apod_func, + zff=2**self.zff, + phase_res=self.phase_resolution if self.phase_res_limit else None, + phase_corr=self.phase_corr, + peak_search=self.peak_search, + ) ifg_data = self.data.X stored_phase = self.stored_phase @@ -414,7 +467,7 @@ def calculateFFT(self): stored_zpd_back = int(stored_phase["zpd_back"].value) except ValueError: stored_zpd_back = None - stored_phase = stored_phase.x # lowercase x for RowInstance + stored_phase = stored_phase.x # lowercase x for RowInstance # Use manual zpd value(s) if specified and enable batch processing elif not self.peak_search_enable: stored_zpd_fwd = self.zpd1 @@ -443,7 +496,8 @@ def calculateFFT(self): if self.sweeps in [0, 2, 3]: try: spectrum_out, phase_out, wavenumbers = fft_single( - row, zpd=stored_zpd_fwd, phase=stored_phase) + row, zpd=stored_zpd_fwd, phase=stored_phase + ) zpd_fwd.append(fft_single.zpd) except ValueError as e: self.Error.fft_error(e) @@ -464,10 +518,12 @@ def calculateFFT(self): # Calculate spectrum for both forward and backward sweeps try: spectrum_fwd, phase_fwd, wavenumbers = fft_single( - fwd, zpd=stored_zpd_fwd, phase=stored_phase) + fwd, zpd=stored_zpd_fwd, phase=stored_phase + ) zpd_fwd.append(fft_single.zpd) spectrum_back, phase_back, wavenumbers = fft_single( - back, zpd=stored_zpd_back, phase=stored_phase) + back, zpd=stored_zpd_back, phase=stored_phase + ) zpd_back.append(fft_single.zpd) except ValueError as e: self.Error.fft_error(e) @@ -485,32 +541,32 @@ def calculateFFT(self): spectra = np.vstack(spectra) phases = np.vstack(phases) - self.phases_table = build_spec_table(wavenumbers, phases, - additional_table=self.data) + self.phases_table = build_spec_table( + wavenumbers, phases, additional_table=self.data + ) if not self.peak_search_enable: # All zpd values are equal by definition zpd_fwd = zpd_fwd[:1] - self.phases_table = add_meta_to_table(self.phases_table, - ContinuousVariable.make("zpd_fwd"), - zpd_fwd) + self.phases_table = add_meta_to_table( + self.phases_table, ContinuousVariable.make("zpd_fwd"), zpd_fwd + ) if zpd_back: if not self.peak_search_enable: zpd_back = zpd_back[:1] - self.phases_table = add_meta_to_table(self.phases_table, - ContinuousVariable.make("zpd_back"), - zpd_back) + self.phases_table = add_meta_to_table( + self.phases_table, ContinuousVariable.make("zpd_back"), zpd_back + ) if self.limit_output is True: wavenumbers, spectra = self.limit_range(wavenumbers, spectra) - self.spectra_table = build_spec_table(wavenumbers, spectra, - additional_table=self.data) + self.spectra_table = build_spec_table( + wavenumbers, spectra, additional_table=self.data + ) self.Outputs.spectra.send(self.spectra_table) self.Outputs.phases.send(self.phases_table) - def calculate_complex_FFT(self): - # Reset info, error and warning dialogs self.Error.clear() self.Warning.clear() @@ -566,7 +622,7 @@ def calculate_complex_FFT(self): ) else: self.spectra_table = Table.from_numpy( - wavenumbers_domain, X=spectra, metas=self.data.metas + wavenumbers_domain, X=spectra, metas=self.data.metas ) phases_table = Table.from_numpy( wavenumbers_domain, X=phases, metas=self.data.metas @@ -646,7 +702,9 @@ def check_metadata(self): self.controls.auto_sweeps.setDisabled(True) self.controls.sweeps.setDisabled(True) - self.info_dx.setText("Using Calculated Datapoint Spacing (Δx) from metadata.") + self.info_dx.setText( + "Using Calculated Datapoint Spacing (Δx) from metadata." + ) return except KeyError: pass @@ -666,20 +724,20 @@ def check_metadata(self): lwn = lwn[0] if (lwn == lwn[0]).all() else ValueError() self.info_dx.setText("") - self.info_dx.setText(self.info_dx.text() + f"{lwn} cm⁻¹ laser \n{udr} sampling interval") + self.info_dx.setText( + self.info_dx.text() + f"{lwn} cm⁻¹ laser \n{udr} sampling interval" + ) if self.dx_auto: - self.dx = (1 / lwn / 2 ) * udr + self.dx = (1 / lwn / 2) * udr def limit_range(self, wavenumbers, spectra): - - limits = np.searchsorted(wavenumbers, - [self.out_limit1, self.out_limit2]) - wavenumbers = wavenumbers[limits[0]:limits[1]] + limits = np.searchsorted(wavenumbers, [self.out_limit1, self.out_limit2]) + wavenumbers = wavenumbers[limits[0] : limits[1]] # Handle 1D array if necessary if spectra.ndim == 1: - spectra = spectra[None, limits[0]:limits[1]] + spectra = spectra[None, limits[0] : limits[1]] else: - spectra = spectra[:, limits[0]:limits[1]] + spectra = spectra[:, limits[0] : limits[1]] return wavenumbers, spectra @@ -716,7 +774,9 @@ def load_test_gsf() -> Orange.data.Table: absolute_filename = FileFormat.locate(fn, dataset_dirs) return NeaReaderGSF(absolute_filename).read() + if __name__ == "__main__": # pragma: no cover # pylint: disable=ungrouped-imports from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWFFT).run(load_test_gsf()) diff --git a/orangecontrib/spectroscopy/widgets/owhyper.py b/orangecontrib/spectroscopy/widgets/owhyper.py index 3fb96450d..b1ce076a7 100644 --- a/orangecontrib/spectroscopy/widgets/owhyper.py +++ b/orangecontrib/spectroscopy/widgets/owhyper.py @@ -5,11 +5,31 @@ from collections import OrderedDict from xml.sax.saxutils import escape -from AnyQt.QtWidgets import QWidget, QPushButton, \ - QGridLayout, QFormLayout, QAction, QVBoxLayout, QWidgetAction, QSplitter, \ - QToolTip, QGraphicsRectItem, QLabel -from AnyQt.QtGui import QColor, QKeySequence, QPainter, QBrush, QStandardItemModel, \ - QStandardItem, QLinearGradient, QPixmap, QIcon, QPen +from AnyQt.QtWidgets import ( + QWidget, + QPushButton, + QGridLayout, + QFormLayout, + QAction, + QVBoxLayout, + QWidgetAction, + QSplitter, + QToolTip, + QGraphicsRectItem, + QLabel, +) +from AnyQt.QtGui import ( + QColor, + QKeySequence, + QPainter, + QBrush, + QStandardItemModel, + QStandardItem, + QLinearGradient, + QPixmap, + QIcon, + QPen, +) from AnyQt.QtCore import Qt, QRectF, QPointF, QSize from AnyQt.QtTest import QTest @@ -31,8 +51,12 @@ from Orange.widgets.visualize.utils.customizableplot import CommonParameterSetter from Orange.widgets.widget import OWWidget, Msg, OWComponent, Input from Orange.widgets import gui -from Orange.widgets.settings import \ - Setting, ContextSetting, DomainContextHandler, SettingProvider +from Orange.widgets.settings import ( + Setting, + ContextSetting, + DomainContextHandler, + SettingProvider, +) from Orange.widgets.utils.itemmodels import DomainModel, PyListModel from Orange.widgets.utils import saveplot from Orange.widgets.utils.concurrent import TaskState, ConcurrentMixin @@ -42,22 +66,43 @@ from orangewidget.utils.visual_settings_dlg import VisualSettingsDialog from orangecontrib.spectroscopy.preprocess import Integrate -from orangecontrib.spectroscopy.utils import values_to_linspace, index_values_nan, split_to_size - -from orangecontrib.spectroscopy.widgets.owspectra import InteractiveViewBox, \ - MenuFocus, CurvePlot, SELECTONE, SELECTMANY, INDIVIDUAL, AVERAGE, \ - HelpEventDelegate, selection_modifiers, \ - ParameterSetter as SpectraParameterSetter +from orangecontrib.spectroscopy.utils import ( + values_to_linspace, + index_values_nan, + split_to_size, +) + +from orangecontrib.spectroscopy.widgets.owspectra import ( + InteractiveViewBox, + MenuFocus, + CurvePlot, + SELECTONE, + SELECTMANY, + INDIVIDUAL, + AVERAGE, + HelpEventDelegate, + selection_modifiers, + ParameterSetter as SpectraParameterSetter, +) from orangecontrib.spectroscopy.io.util import VisibleImage, build_spec_table -from orangecontrib.spectroscopy.widgets.gui import MovableVline, lineEditDecimalOrNone,\ - pixels_to_decimals, float_to_str_decimals -from orangecontrib.spectroscopy.widgets.line_geometry import in_polygon, intersect_line_segments -from orangecontrib.spectroscopy.widgets.utils import \ - SelectionGroupMixin, SelectionOutputsMixin +from orangecontrib.spectroscopy.widgets.gui import ( + MovableVline, + lineEditDecimalOrNone, + pixels_to_decimals, + float_to_str_decimals, +) +from orangecontrib.spectroscopy.widgets.line_geometry import ( + in_polygon, + intersect_line_segments, +) +from orangecontrib.spectroscopy.widgets.utils import ( + SelectionGroupMixin, + SelectionOutputsMixin, +) -IMAGE_TOO_BIG = 1024*1024*100 +IMAGE_TOO_BIG = 1024 * 1024 * 100 NAN_COLOR = (100, 100, 100, 255) @@ -86,14 +131,12 @@ def add_marking(a): curveplot.add_marking(a) for di in dis: - if di is None: continue # nothing to draw color = QColor(di.get("color", "red")) for el in di["draw"]: - if el[0] == "curve": bs_x, bs_ys, penargs = el[1] bs_x, bs_ys = np.asarray(bs_x), np.asarray(bs_ys) @@ -134,11 +177,11 @@ def add_marking(a): def _shift(ls): if ls[2] == 1: return 0.5 - return (ls[1]-ls[0])/(2*(ls[2]-1)) + return (ls[1] - ls[0]) / (2 * (ls[2] - 1)) def get_levels(array): - """ Compute levels. Account for NaN values. """ + """Compute levels. Account for NaN values.""" mn, mx = bottleneck.nanmin(array), bottleneck.nanmax(array) if mn == mx or math.isnan(mx) or math.isnan(mn): mn = 0 @@ -147,7 +190,6 @@ def get_levels(array): class VisibleImageListModel(PyListModel): - def data(self, index, role=Qt.DisplayRole): if self._is_index_valid(index): img = self[index.row()] @@ -157,7 +199,7 @@ def data(self, index, role=Qt.DisplayRole): class ImageItemNan(pg.ImageItem): - """ Simplified ImageItem that can show NaN color. """ + """Simplified ImageItem that can show NaN color.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -186,7 +228,7 @@ def render(self): levels = self.levels if self.axisOrder == 'col-major': - image = image.transpose((1, 0, 2)[:image.ndim]) + image = image.transpose((1, 0, 2)[: image.ndim]) image_nans = np.isnan(image).all(axis=2) @@ -230,12 +272,9 @@ def color_palette_table(colors, underflow=None, overflow=None): if overflow is None: overflow = [None, None, None] - r = np.interp(space, points, colors[:, 0], - left=underflow[0], right=overflow[0]) - g = np.interp(space, points, colors[:, 1], - left=underflow[1], right=overflow[1]) - b = np.interp(space, points, colors[:, 2], - left=underflow[2], right=overflow[2]) + r = np.interp(space, points, colors[:, 0], left=underflow[0], right=overflow[0]) + g = np.interp(space, points, colors[:, 1], left=underflow[1], right=overflow[1]) + b = np.interp(space, points, colors[:, 2], left=underflow[2], right=overflow[2]) return np.c_[r, g, b] @@ -247,33 +286,33 @@ def color_palette_table(colors, underflow=None, overflow=None): ("dimgray", {0: np.array(colorcet.linear_grey_10_95_c0) * 255}), ("blues", {0: np.array(colorcet.linear_blue_95_50_c20) * 255}), ("fire", {0: np.array(colorcet.linear_kryw_0_100_c71) * 255}), - # diverging - TODO set point ("bkr", {0: np.array(colorcet.diverging_bkr_55_10_c35) * 255}), ("bky", {0: np.array(colorcet.diverging_bky_60_10_c30) * 255}), ("coolwarm", {0: np.array(colorcet.diverging_bwr_40_95_c42) * 255}), ("bjy", {0: np.array(colorcet.diverging_linear_bjy_30_90_c45) * 255}), - # misc ("rainbow", {0: np.array(colorcet.rainbow_bgyr_35_85_c73) * 255}), ("isolum", {0: np.array(colorcet.isoluminant_cgo_80_c38) * 255}), ("Jet", {0: pg.colormap.get("jet", source='matplotlib').getLookupTable(nPts=256)}), - ("Viridis", {0: pg.colormap.get("viridis", source='matplotlib').getLookupTable(nPts=256)}), - + ( + "Viridis", + {0: pg.colormap.get("viridis", source='matplotlib').getLookupTable(nPts=256)}, + ), # cyclic ("HSV", {0: pg.colormap.get("hsv", source='matplotlib').getLookupTable(nPts=256)}), ] -#r, g, b, c, m, y, k, w +# r, g, b, c, m, y, k, w vector_color = [ - ("Black", {0: (0,0,0)}), - ("White", {0: (255,255,255)}), - ("Red", {0: (255,0,0)}), - ("Green", {0: (0,255,0)}), - ("Blue", {0: (0,0,255)}), - ("Cyan", {0: (0,255,255)}), - ("Magenta", {0: (255,0,255)}), - ("Yellow", {0: (255,255,0)}), - ("By Feature", {0: ('by feature')}) + ("Black", {0: (0, 0, 0)}), + ("White", {0: (255, 255, 255)}), + ("Red", {0: (255, 0, 0)}), + ("Green", {0: (0, 255, 0)}), + ("Blue", {0: (0, 0, 255)}), + ("Cyan", {0: (0, 255, 255)}), + ("Magenta", {0: (255, 0, 255)}), + ("Yellow", {0: (255, 255, 0)}), + ("By Feature", {0: ('by feature')}), ] bins = [ @@ -289,10 +328,11 @@ def color_palette_table(colors, underflow=None, overflow=None): ("10 x 10", {0, (10)}), ] + def palette_gradient(colors): n = len(colors) stops = np.linspace(0.0, 1.0, n, endpoint=True) - gradstops = [(float(stop), color) for stop, color in zip(stops, colors)] + gradstops = [(float(stop), color) for stop, color in zip(stops, colors)] # noqa: B905 grad = QLinearGradient(QPointF(0, 0), QPointF(1, 0)) grad.setStops(gradstops) return grad @@ -313,7 +353,7 @@ def palette_pixmap(colors, size): return img -def color_palette_model(palettes, iconsize=QSize(64, 16)): +def color_palette_model(palettes, iconsize=QSize(64, 16)): # noqa: B008 model = QStandardItemModel() for name, palette in palettes: _, colors = max(palette.items()) @@ -336,9 +376,9 @@ def vector_color_model(colors): def circular_mean(degs): - sin = np.nansum(np.sin(np.radians(degs*2))) - cos = np.nansum(np.cos(np.radians(degs*2))) - return np.arctan2(sin, cos)/2 + sin = np.nansum(np.sin(np.radians(degs * 2))) + cos = np.nansum(np.cos(np.radians(degs * 2))) + return np.arctan2(sin, cos) / 2 class VectorSettingMixin: @@ -354,20 +394,25 @@ class VectorSettingMixin: v_bin = Setting(0) def setup_vector_plot_controls(self): - box = gui.vBox(self) - self.cb_vector = gui.checkBox(box, self, "show_vector_plot", - label="Show vector plot", - callback=self.enable_vector) + self.cb_vector = gui.checkBox( + box, + self, + "show_vector_plot", + label="Show vector plot", + callback=self.enable_vector, + ) self.vectorbox = gui.widgetBox(box, box=False) - self.vector_opts = DomainModel(DomainModel.SEPARATED, - valid_types=DomainModel.PRIMITIVE, placeholder='None') + self.vector_opts = DomainModel( + DomainModel.SEPARATED, valid_types=DomainModel.PRIMITIVE, placeholder='None' + ) - self.vector_cbyf_opts = DomainModel(DomainModel.SEPARATED, - valid_types=(ContinuousVariable,), placeholder='None') + self.vector_cbyf_opts = DomainModel( + DomainModel.SEPARATED, valid_types=(ContinuousVariable,), placeholder='None' + ) self.vector_col_opts = vector_color_model(vector_color) self.vector_pal_opts = color_palette_model(_color_palettes, (QSize(64, 16))) @@ -380,69 +425,131 @@ def setup_vector_plot_controls(self): gb = create_gridbox(self.vectorbox, box=False) - v_angle_select = gui.comboBox(None, self, 'vector_angle', searchable=True, - label="Vector Angle", model=self.vector_opts, - contentsLength=10, - callback=self._update_vector_params) + v_angle_select = gui.comboBox( + None, + self, + 'vector_angle', + searchable=True, + label="Vector Angle", + model=self.vector_opts, + contentsLength=10, + callback=self._update_vector_params, + ) grid_add_labelled_row(gb, "Angle: ", v_angle_select) - v_mag_select = gui.comboBox(None, self, 'vector_magnitude', searchable=True, - label="Vector Magnitude", model=self.vector_opts, - contentsLength=10, - callback=self._update_vector_params) + v_mag_select = gui.comboBox( + None, + self, + 'vector_magnitude', + searchable=True, + label="Vector Magnitude", + model=self.vector_opts, + contentsLength=10, + callback=self._update_vector_params, + ) grid_add_labelled_row(gb, "Magnitude: ", v_mag_select) - v_bin_select = gui.comboBox(None, self, 'v_bin', label="Pixel Binning", - model=self.vector_bin_opts, - contentsLength=10, - callback=self._update_binsize) + v_bin_select = gui.comboBox( + None, + self, + 'v_bin', + label="Pixel Binning", + model=self.vector_bin_opts, + contentsLength=10, + callback=self._update_binsize, + ) grid_add_labelled_row(gb, "Binning: ", v_bin_select) - v_color_select = gui.comboBox(None, self, 'vector_color_index', - label="Vector color", model=self.vector_col_opts, - contentsLength=10, - callback=self._update_vector) + v_color_select = gui.comboBox( + None, + self, + 'vector_color_index', + label="Vector color", + model=self.vector_col_opts, + contentsLength=10, + callback=self._update_vector, + ) grid_add_labelled_row(gb, "Color: ", v_color_select) - v_color_byval = gui.comboBox(None, self, 'vcol_byval_feat', - label="Vector color by Feature", - model=self.vector_cbyf_opts, - contentsLength=10, - callback=self._update_cbyval) + v_color_byval = gui.comboBox( + None, + self, + 'vcol_byval_feat', + label="Vector color by Feature", + model=self.vector_cbyf_opts, + contentsLength=10, + callback=self._update_cbyval, + ) grid_add_labelled_row(gb, "Feature: ", v_color_byval) - v_color_byval_select = gui.comboBox(None, self, 'vcol_byval_index', - label="", model=self.vector_pal_opts, - contentsLength=5, - callback=self._update_cbyval) + v_color_byval_select = gui.comboBox( + None, + self, + 'vcol_byval_index', + label="", + model=self.vector_pal_opts, + contentsLength=5, + callback=self._update_cbyval, + ) v_color_byval_select.setIconSize(QSize(64, 16)) grid_add_labelled_row(gb, "Palette: ", v_color_byval_select) gb = create_gridbox(self.vectorbox, box=False) - v_scale_slider = gui.hSlider(None, self, 'vector_scale', label="Scale", - minValue=1, maxValue=1000, step=10, createLabel=False, - callback=self._update_vector) + v_scale_slider = gui.hSlider( + None, + self, + 'vector_scale', + label="Scale", + minValue=1, + maxValue=1000, + step=10, + createLabel=False, + callback=self._update_vector, + ) grid_add_labelled_row(gb, "Scale: ", v_scale_slider) - v_width_slider = gui.hSlider(None, self, 'vector_width', label="Width", - minValue=1, maxValue=20, step=1, createLabel=False, - callback=self._update_vector) + v_width_slider = gui.hSlider( + None, + self, + 'vector_width', + label="Width", + minValue=1, + maxValue=20, + step=1, + createLabel=False, + callback=self._update_vector, + ) grid_add_labelled_row(gb, "Width: ", v_width_slider) - v_opacity_slider = gui.hSlider(None, self, 'vector_opacity', label="Opacity", - minValue=0, maxValue=255, step=5, createLabel=False, - callback=self._update_vector) + v_opacity_slider = gui.hSlider( + None, + self, + 'vector_opacity', + label="Opacity", + minValue=0, + maxValue=255, + step=5, + createLabel=False, + callback=self._update_vector, + ) grid_add_labelled_row(gb, "Opacity: ", v_opacity_slider) self.vectorbox.setVisible(self.show_vector_plot) self.update_vector_plot_interface() - + return box def update_vector_plot_interface(self): - vector_params = ['vector_angle', 'vector_magnitude', 'vector_color_index', - 'vector_scale', 'vector_width', 'vector_opacity', 'v_bin'] + vector_params = [ + 'vector_angle', + 'vector_magnitude', + 'vector_color_index', + 'vector_scale', + 'vector_width', + 'vector_opacity', + 'v_bin', + ] for i in vector_params: getattr(self.controls, i).setEnabled(self.show_vector_plot) @@ -494,7 +601,6 @@ def init_vector_plot(self, data): class VectorMixin: - def __init__(self): self.a = None self.th = None @@ -523,42 +629,41 @@ def update_vectors(self): xindex, yindex = self.xindex, self.yindex scale = self.vector_scale w = self.vector_width - th = np.asarray(v[:,0], dtype=float) - v_mag = np.asarray(v[:,1], dtype=float) - wy = _shift(lsy)*2 - wx = _shift(lsx)*2 + th = np.asarray(v[:, 0], dtype=float) + v_mag = np.asarray(v[:, 1], dtype=float) + wy = _shift(lsy) * 2 + wx = _shift(lsx) * 2 if self.v_bin == 0: y = np.linspace(*lsy)[yindex[valid]] x = np.linspace(*lsx)[xindex[valid]] - amp = v_mag / max(v_mag) * (scale/100) - dispx = amp*wx/2*np.cos(np.radians(th)) - dispy = amp*wy/2*np.sin(np.radians(th)) - xcurve = np.empty((dispx.shape[0]*2)) - ycurve = np.empty((dispy.shape[0]*2)) + amp = v_mag / max(v_mag) * (scale / 100) + dispx = amp * wx / 2 * np.cos(np.radians(th)) + dispy = amp * wy / 2 * np.sin(np.radians(th)) + xcurve = np.empty((dispx.shape[0] * 2)) + ycurve = np.empty((dispy.shape[0] * 2)) xcurve[0::2], xcurve[1::2] = x - dispx, x + dispx ycurve[0::2], ycurve[1::2] = y - dispy, y + dispy - vcols = self.get_vector_color(v[:,2]) + vcols = self.get_vector_color(v[:, 2]) v_params = [xcurve, ycurve, w, vcols] self.vector_plot.setData(v_params) else: if self.a is None: self.update_binsize() - amp = self.a / max(self.a) * (scale/100) - dispx = amp*wx/2*np.cos(self.th) - dispy = amp*wy/2*np.sin(self.th) - xcurve = np.empty((dispx.shape[0]*2)) - ycurve = np.empty((dispy.shape[0]*2)) + amp = self.a / max(self.a) * (scale / 100) + dispx = amp * wx / 2 * np.cos(self.th) + dispy = amp * wy / 2 * np.sin(self.th) + xcurve = np.empty((dispx.shape[0] * 2)) + ycurve = np.empty((dispy.shape[0] * 2)) xcurve[0::2], xcurve[1::2] = self.new_xs - dispx, self.new_xs + dispx ycurve[0::2], ycurve[1::2] = self.new_ys - dispy, self.new_ys + dispy - vcols = self.get_vector_color(v[:,2]) + vcols = self.get_vector_color(v[:, 2]) v_params = [xcurve, ycurve, w, vcols] self.vector_plot.setData(v_params) self.vector_plot.show() - if self.vector_color_index == 8 and \ - self.vcol_byval_feat is not None: + if self.vector_color_index == 8 and self.vcol_byval_feat is not None: self.update_vect_legend() - def update_vect_legend(self):#feat + def update_vect_legend(self): # feat if self.v_bin != 0: feat = self.cols else: @@ -584,7 +689,7 @@ def get_vector_data(self): def get_vector_color(self, feat): if self.vector_color_index == 8: - if feat[0] is None: # a feat has not been selected yet + if feat[0] is None: # a feat has not been selected yet return vector_color[0][1][0] + (self.vector_opacity,) else: if self.v_bin != 0: @@ -595,11 +700,14 @@ def get_vector_color(self, feat): if fmin == fmax: # put a warning here? return vector_color[0][1][0] + (self.vector_opacity,) - feat_idxs = np.asarray(((feat-fmin)/(fmax-fmin))*255, dtype=int) - col_vals = np.asarray(_color_palettes[self.vcol_byval_index][1][0][feat_idxs], - dtype=int) - out = [np.hstack((np.expand_dims(feat_idxs, 1), col_vals)), - self.vector_opacity] + feat_idxs = np.asarray(((feat - fmin) / (fmax - fmin)) * 255, dtype=int) + col_vals = np.asarray( + _color_palettes[self.vcol_byval_index][1][0][feat_idxs], dtype=int + ) + out = [ + np.hstack((np.expand_dims(feat_idxs, 1), col_vals)), + self.vector_opacity, + ] return out else: return vector_color[self.vector_color_index][1][0] + (self.vector_opacity,) @@ -620,50 +728,73 @@ def update_binsize(self): self.v_bin_change = 0 self.vector_plot.hide() else: - th = np.asarray(v[:,0], dtype=float) - v_mag = np.asarray(v[:,1], dtype=float) - col = np.asarray(v[:,2], dtype=float) + th = np.asarray(v[:, 0], dtype=float) + v_mag = np.asarray(v[:, 1], dtype=float) + col = np.asarray(v[:, 2], dtype=float) y = np.linspace(*lsy)[yindex] x = np.linspace(*lsx)[xindex] df = pd.DataFrame( - [x, y, np.asarray([1 if i else 0 for i in valid]),v_mag, th, col], - index = ['x', 'y', 'valid', 'v_mag', 'th', 'cols']).T - - v_df = df.pivot_table(values = 'valid', columns = 'x', index = 'y', fill_value = 0) - a_df = df.pivot_table(values = 'v_mag', columns = 'x', index = 'y') - th_df = df.pivot_table(values = 'th', columns = 'x', index = 'y') - col_df = df.pivot_table(values = 'cols', columns = 'x', index = 'y') - bin_sz = self.v_bin+1 + [x, y, np.asarray([1 if i else 0 for i in valid]), v_mag, th, col], + index=['x', 'y', 'valid', 'v_mag', 'th', 'cols'], + ).T + + v_df = df.pivot_table( + values='valid', columns='x', index='y', fill_value=0 + ) + a_df = df.pivot_table(values='v_mag', columns='x', index='y') + th_df = df.pivot_table(values='th', columns='x', index='y') + col_df = df.pivot_table(values='cols', columns='x', index='y') + bin_sz = self.v_bin + 1 if bin_sz > v_df.shape[0] or bin_sz > v_df.shape[1]: bin_sz = v_df.shape[0] if bin_sz > v_df.shape[0] else v_df.shape[1] self.parent.Warning.bin_size_error(bin_sz, bin_sz) x_mod, y_mod = v_df.shape[1] % bin_sz, v_df.shape[0] % bin_sz - st_x_idx = int(np.floor(x_mod/2)) - st_y_idx = int(np.floor(y_mod/2)) - - nvalid = np.zeros((int((v_df.shape[0]-y_mod)/bin_sz), - int((v_df.shape[1]-x_mod)/bin_sz))) - a = np.zeros((int((v_df.shape[0]-y_mod)/bin_sz), - int((v_df.shape[1]-x_mod)/bin_sz))) - th = np.zeros((int((v_df.shape[0]-y_mod)/bin_sz), - int((v_df.shape[1]-x_mod)/bin_sz))) - cols = np.zeros((int((v_df.shape[0]-y_mod)/bin_sz), - int((v_df.shape[1]-x_mod)/bin_sz))) + st_x_idx = int(np.floor(x_mod / 2)) + st_y_idx = int(np.floor(y_mod / 2)) + + nvalid = np.zeros( + ( + int((v_df.shape[0] - y_mod) / bin_sz), + int((v_df.shape[1] - x_mod) / bin_sz), + ) + ) + a = np.zeros( + ( + int((v_df.shape[0] - y_mod) / bin_sz), + int((v_df.shape[1] - x_mod) / bin_sz), + ) + ) + th = np.zeros( + ( + int((v_df.shape[0] - y_mod) / bin_sz), + int((v_df.shape[1] - x_mod) / bin_sz), + ) + ) + cols = np.zeros( + ( + int((v_df.shape[0] - y_mod) / bin_sz), + int((v_df.shape[1] - x_mod) / bin_sz), + ) + ) columns = v_df.columns rows = v_df.index new_xs, new_ys = [], [] - for i in range(st_y_idx, v_df.shape[0]-y_mod, bin_sz): - for j in range(st_x_idx, v_df.shape[1]-x_mod, bin_sz): - nvalid[int(i/bin_sz),int(j/bin_sz)] = \ - np.nanmean(v_df.iloc[i:i+bin_sz,j:j+bin_sz].to_numpy()) - a[int(i/bin_sz),int(j/bin_sz)] = \ - np.nanmean(a_df.iloc[i:i+bin_sz,j:j+bin_sz].to_numpy()) - th[int(i/bin_sz),int(j/bin_sz)] = \ - circular_mean(th_df.iloc[i:i+bin_sz,j:j+bin_sz].to_numpy()) - cols[int(i/bin_sz),int(j/bin_sz)] = \ - np.nanmean(col_df.iloc[i:i+bin_sz,j:j+bin_sz].to_numpy()) - new_xs.append(np.sum(columns[j:j+bin_sz])/bin_sz) - new_ys.append(np.sum(rows[i:i+bin_sz])/bin_sz) + for i in range(st_y_idx, v_df.shape[0] - y_mod, bin_sz): + for j in range(st_x_idx, v_df.shape[1] - x_mod, bin_sz): + nvalid[int(i / bin_sz), int(j / bin_sz)] = np.nanmean( + v_df.iloc[i : i + bin_sz, j : j + bin_sz].to_numpy() + ) + a[int(i / bin_sz), int(j / bin_sz)] = np.nanmean( + a_df.iloc[i : i + bin_sz, j : j + bin_sz].to_numpy() + ) + th[int(i / bin_sz), int(j / bin_sz)] = circular_mean( + th_df.iloc[i : i + bin_sz, j : j + bin_sz].to_numpy() + ) + cols[int(i / bin_sz), int(j / bin_sz)] = np.nanmean( + col_df.iloc[i : i + bin_sz, j : j + bin_sz].to_numpy() + ) + new_xs.append(np.sum(columns[j : j + bin_sz]) / bin_sz) + new_ys.append(np.sum(rows[i : i + bin_sz]) / bin_sz) nvalid = nvalid.flatten() > 0 & ~np.isnan(nvalid.flatten()) self.a = a.flatten()[nvalid] self.th = th.flatten()[nvalid] @@ -676,10 +807,10 @@ def update_binsize(self): class AxesSettingsMixin: - def __init__(self): - self.xy_model = DomainModel(DomainModel.METAS | DomainModel.CLASSES, - valid_types=DomainModel.PRIMITIVE) + self.xy_model = DomainModel( + DomainModel.METAS | DomainModel.CLASSES, valid_types=DomainModel.PRIMITIVE + ) def setup_axes_settings_box(self): box = gui.vBox(self) @@ -687,15 +818,27 @@ def setup_axes_settings_box(self): common_options = { "labelWidth": 50, "orientation": Qt.Horizontal, - "sendSelectedValue": True + "sendSelectedValue": True, } cb_attr_x = gui.comboBox( - box, self, "attr_x", label="Axis x:", callback=self.update_attr, - model=self.xy_model, **common_options) + box, + self, + "attr_x", + label="Axis x:", + callback=self.update_attr, + model=self.xy_model, + **common_options, + ) gui.comboBox( - box, self, "attr_y", label="Axis y:", callback=self.update_attr, - model=self.xy_model, **common_options) + box, + self, + "attr_y", + label="Axis y:", + callback=self.update_attr, + model=self.xy_model, + **common_options, + ) box.setFocusProxy(cb_attr_x) return box @@ -714,9 +857,15 @@ def __init__(self): def setup_color_settings_box(self): box = gui.vBox(self) box.setContentsMargins(0, 0, 0, 5) - self.color_cb = gui.comboBox(box, self, "palette_index", label="Color:", - labelWidth=50, orientation=Qt.Horizontal, - contentsLength=10) + self.color_cb = gui.comboBox( + box, + self, + "palette_index", + label="Color:", + labelWidth=50, + orientation=Qt.Horizontal, + contentsLength=10, + ) self.color_cb.setIconSize(QSize(64, 16)) palettes = _color_palettes model = color_palette_model(palettes, self.color_cb.iconSize()) @@ -725,36 +874,59 @@ def setup_color_settings_box(self): self.palette_index = min(self.palette_index, len(palettes) - 1) self.color_cb.activated.connect(self.update_color_schema) - gui.checkBox(box, self, "show_legend", label="Show legend", - callback=self.update_legend_visible) + gui.checkBox( + box, + self, + "show_legend", + label="Show legend", + callback=self.update_legend_visible, + ) form = QFormLayout( formAlignment=Qt.AlignLeft, labelAlignment=Qt.AlignLeft, - fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow + fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow, ) def limit_changed(): self.update_levels() self.reset_thresholds() - self._level_low_le = lineEditDecimalOrNone(self, self, "level_low", callback=limit_changed) + self._level_low_le = lineEditDecimalOrNone( + self, self, "level_low", callback=limit_changed + ) self._level_low_le.validator().setDefault(0) self._level_low_le.sizeHintFactor = 0.5 form.addRow("Low limit:", self._level_low_le) - self._level_high_le = lineEditDecimalOrNone(self, self, "level_high", callback=limit_changed) + self._level_high_le = lineEditDecimalOrNone( + self, self, "level_high", callback=limit_changed + ) self._level_high_le.validator().setDefault(1) self._level_high_le.sizeHintFactor = 0.5 form.addRow("High limit:", self._level_high_le) self._threshold_low_slider = lowslider = gui.hSlider( - box, self, "threshold_low", minValue=0.0, maxValue=1.0, - step=0.05, intOnly=False, - createLabel=False, callback=self.update_levels) + box, + self, + "threshold_low", + minValue=0.0, + maxValue=1.0, + step=0.05, + intOnly=False, + createLabel=False, + callback=self.update_levels, + ) self._threshold_high_slider = highslider = gui.hSlider( - box, self, "threshold_high", minValue=0.0, maxValue=1.0, - step=0.05, intOnly=False, - createLabel=False, callback=self.update_levels) + box, + self, + "threshold_high", + minValue=0.0, + maxValue=1.0, + step=0.05, + intOnly=False, + createLabel=False, + callback=self.update_levels, + ) form.addRow("Low:", lowslider) form.addRow("High:", highslider) @@ -783,10 +955,12 @@ def compute_palette_min_max_points(self): else: levels = [0, 255] - prec = pixels_to_decimals((levels[1] - levels[0])/1000) + prec = pixels_to_decimals((levels[1] - levels[0]) / 1000) - rounded_levels = [float_to_str_decimals(levels[0], prec), - float_to_str_decimals(levels[1], prec)] + rounded_levels = [ + float_to_str_decimals(levels[0], prec), + float_to_str_decimals(levels[1], prec), + ] self._level_low_le.validator().setDefault(rounded_levels[0]) self._level_high_le.validator().setDefault(rounded_levels[1]) @@ -849,8 +1023,8 @@ def update_color_schema(self): self.legend.set_colors(lut) def reset_thresholds(self): - self.threshold_low = 0. - self.threshold_high = 1. + self.threshold_low = 0.0 + self.threshold_high = 1.0 class ImageRGBSettingMixin: @@ -867,30 +1041,42 @@ def setup_rgb_settings_box(self): form = QFormLayout( formAlignment=Qt.AlignLeft, labelAlignment=Qt.AlignLeft, - fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow + fieldGrowthPolicy=QFormLayout.AllNonFixedFieldsGrow, ) - self._red_level_low_le = lineEditDecimalOrNone(self, self, "red_level_low", callback=self.update_rgb_levels) + self._red_level_low_le = lineEditDecimalOrNone( + self, self, "red_level_low", callback=self.update_rgb_levels + ) self._red_level_low_le.validator().setDefault(0) form.addRow("Red Low limit:", self._red_level_low_le) - self._red_level_high_le = lineEditDecimalOrNone(self, self, "red_level_high", callback=self.update_rgb_levels) + self._red_level_high_le = lineEditDecimalOrNone( + self, self, "red_level_high", callback=self.update_rgb_levels + ) self._red_level_high_le.validator().setDefault(1) form.addRow("Red High limit:", self._red_level_high_le) - self._green_level_low_le = lineEditDecimalOrNone(self, self, "green_level_low", callback=self.update_rgb_levels) + self._green_level_low_le = lineEditDecimalOrNone( + self, self, "green_level_low", callback=self.update_rgb_levels + ) self._green_level_low_le.validator().setDefault(0) form.addRow("Green Low limit:", self._green_level_low_le) - self._green_level_high_le = lineEditDecimalOrNone(self, self, "green_level_high", callback=self.update_rgb_levels) + self._green_level_high_le = lineEditDecimalOrNone( + self, self, "green_level_high", callback=self.update_rgb_levels + ) self._green_level_high_le.validator().setDefault(1) form.addRow("Green High limit:", self._green_level_high_le) - self._blue_level_low_le = lineEditDecimalOrNone(self, self, "blue_level_low", callback=self.update_rgb_levels) + self._blue_level_low_le = lineEditDecimalOrNone( + self, self, "blue_level_low", callback=self.update_rgb_levels + ) self._blue_level_low_le.validator().setDefault(0) form.addRow("Blue Low limit:", self._blue_level_low_le) - self._blue_level_high_le = lineEditDecimalOrNone(self, self, "blue_level_high", callback=self.update_rgb_levels) + self._blue_level_high_le = lineEditDecimalOrNone( + self, self, "blue_level_high", callback=self.update_rgb_levels + ) self._blue_level_high_le.validator().setDefault(1) form.addRow("Blue High limit:", self._blue_level_high_le) @@ -902,20 +1088,25 @@ def compute_rgb_levels(self): return if self.data_values is not None and self.data_values.shape[1] == 3: - levels = [get_levels(self.data_values[:, i]) for i in range(self.img.image.shape[2])] + levels = [ + get_levels(self.data_values[:, i]) + for i in range(self.img.image.shape[2]) + ] else: return rgb_le = [ [self._red_level_low_le, self._red_level_high_le], [self._green_level_low_le, self._green_level_high_le], - [self._blue_level_low_le, self._blue_level_high_le] + [self._blue_level_low_le, self._blue_level_high_le], ] for i, (low_le, high_le) in enumerate(rgb_le): prec = pixels_to_decimals((levels[i][1] - levels[i][0]) / 1000) - rounded_levels = [float_to_str_decimals(levels[i][0], prec), - float_to_str_decimals(levels[i][1], prec)] + rounded_levels = [ + float_to_str_decimals(levels[i][0], prec), + float_to_str_decimals(levels[i][1], prec), + ] low_le.validator().setDefault(rounded_levels[0]) high_le.validator().setDefault(rounded_levels[1]) @@ -923,12 +1114,36 @@ def compute_rgb_levels(self): low_le.setPlaceholderText(rounded_levels[0]) high_le.setPlaceholderText(rounded_levels[1]) - rll = float(self.red_level_low) if self.red_level_low is not None else levels[0][0] - rlh = float(self.red_level_high) if self.red_level_high is not None else levels[0][1] - gll = float(self.green_level_low) if self.green_level_low is not None else levels[1][0] - glh = float(self.green_level_high) if self.green_level_high is not None else levels[1][1] - bll = float(self.blue_level_low) if self.blue_level_low is not None else levels[2][0] - blh = float(self.blue_level_high) if self.blue_level_high is not None else levels[2][1] + rll = ( + float(self.red_level_low) + if self.red_level_low is not None + else levels[0][0] + ) + rlh = ( + float(self.red_level_high) + if self.red_level_high is not None + else levels[0][1] + ) + gll = ( + float(self.green_level_low) + if self.green_level_low is not None + else levels[1][0] + ) + glh = ( + float(self.green_level_high) + if self.green_level_high is not None + else levels[1][1] + ) + bll = ( + float(self.blue_level_low) + if self.blue_level_low is not None + else levels[2][0] + ) + blh = ( + float(self.blue_level_high) + if self.blue_level_high is not None + else levels[2][1] + ) return [[rll, rlh], [gll, glh], [bll, blh]] @@ -940,21 +1155,24 @@ def update_rgb_levels(self): class ImageZoomMixin: - def add_zoom_actions(self, menu): - zoom_in = QAction( - "Zoom in", self, triggered=self.plot.vb.set_mode_zooming - ) + zoom_in = QAction("Zoom in", self, triggered=self.plot.vb.set_mode_zooming) zoom_in.setShortcuts([Qt.Key_Z, QKeySequence(QKeySequence.ZoomIn)]) zoom_in.setShortcutContext(Qt.WidgetWithChildrenShortcut) self.addAction(zoom_in) if menu: menu.addAction(zoom_in) zoom_fit = QAction( - "Zoom to fit", self, - triggered=lambda x: (self.plot.vb.autoRange(), self.plot.vb.set_mode_panning()) + "Zoom to fit", + self, + triggered=lambda x: ( + self.plot.vb.autoRange(), + self.plot.vb.set_mode_panning(), + ), + ) + zoom_fit.setShortcuts( + [Qt.Key_Backspace, QKeySequence(Qt.ControlModifier | Qt.Key_0)] ) - zoom_fit.setShortcuts([Qt.Key_Backspace, QKeySequence(Qt.ControlModifier | Qt.Key_0)]) zoom_fit.setShortcutContext(Qt.WidgetWithChildrenShortcut) self.addAction(zoom_fit) if menu: @@ -962,14 +1180,14 @@ def add_zoom_actions(self, menu): class ImageSelectionMixin: - def __init__(self): self.selection_distances = None def add_selection_actions(self, menu): - select_square = QAction( - "Select (rectangle)", self, triggered=self.plot.vb.set_mode_select_square, + "Select (rectangle)", + self, + triggered=self.plot.vb.set_mode_select_square, ) select_square.setShortcuts([Qt.Key_S]) select_square.setShortcutContext(Qt.WidgetWithChildrenShortcut) @@ -978,7 +1196,9 @@ def add_selection_actions(self, menu): menu.addAction(select_square) select_polygon = QAction( - "Select (polygon)", self, triggered=self.plot.vb.set_mode_select_polygon, + "Select (polygon)", + self, + triggered=self.plot.vb.set_mode_select_polygon, ) select_polygon.setShortcuts([Qt.Key_P]) select_polygon.setShortcutContext(Qt.WidgetWithChildrenShortcut) @@ -987,7 +1207,9 @@ def add_selection_actions(self, menu): menu.addAction(select_polygon) line = QAction( - "Trace line", self, triggered=self.plot.vb.set_mode_select, + "Trace line", + self, + triggered=self.plot.vb.set_mode_select, ) line.setShortcuts([Qt.Key_L]) line.setShortcutContext(Qt.WidgetWithChildrenShortcut) @@ -996,15 +1218,21 @@ def add_selection_actions(self, menu): menu.addAction(line) def select_square(self, p1, p2): - """ Select elements within a square drawn by the user. - A selection needs to contain whole pixels """ + """Select elements within a square drawn by the user. + A selection needs to contain whole pixels""" x1, y1 = p1.x(), p1.y() x2, y2 = p2.x(), p2.y() - polygon = [QPointF(x1, y1), QPointF(x2, y1), QPointF(x2, y2), QPointF(x1, y2), QPointF(x1, y1)] + polygon = [ + QPointF(x1, y1), + QPointF(x2, y1), + QPointF(x2, y2), + QPointF(x1, y2), + QPointF(x1, y1), + ] self.select_polygon(polygon) def select_polygon(self, polygon): - """ Select by a polygon which has to contain whole pixels. """ + """Select by a polygon which has to contain whole pixels.""" if self.data and self.data_points is not None: polygon = [(p.x(), p.y()) for p in polygon] # a polygon should contain data point center @@ -1022,29 +1250,39 @@ def select_line(self, p1, p2): # was selected is the Bresenham's line algorithm. shiftx = _shift(self.lsx) shifty = _shift(self.lsy) - points_edges = [self.data_points + [[shiftx, shifty]], - self.data_points + [[-shiftx, shifty]], - self.data_points + [[-shiftx, -shifty]], - self.data_points + [[shiftx, -shifty]]] + points_edges = [ + self.data_points + [[shiftx, shifty]], + self.data_points + [[-shiftx, shifty]], + self.data_points + [[-shiftx, -shifty]], + self.data_points + [[shiftx, -shifty]], + ] sel = None for i in range(4): - res = intersect_line_segments(points_edges[i - 1][:, 0], points_edges[i - 1][:, 1], - points_edges[i][:, 0], points_edges[i][:, 1], - p1x, p1y, p2x, p2y) + res = intersect_line_segments( + points_edges[i - 1][:, 0], + points_edges[i - 1][:, 1], + points_edges[i][:, 0], + points_edges[i][:, 1], + p1x, + p1y, + p2x, + p2y, + ) if sel is None: sel = res else: sel |= res distances = np.full(self.data_points.shape[0], np.nan) - distances[sel] = np.linalg.norm(self.data_points[sel] - [[p1x, p1y]], axis=1) + distances[sel] = np.linalg.norm( + self.data_points[sel] - [[p1x, p1y]], axis=1 + ) modifiers = (False, False, False) # enforce a new selection self.make_selection(sel, distances=distances, modifiers=modifiers) class ImageColorLegend(GraphicsWidget): - def __init__(self): GraphicsWidget.__init__(self) self.width_bar = 15 @@ -1085,7 +1323,7 @@ def set_colors(self, colors): self.colors = np.round(self.colors).astype(int) positions = np.linspace(0, 1, len(self.colors)) stops = [] - for p, c in zip(positions, self.colors): + for p, c in zip(positions, self.colors): # noqa: B905 stops.append((p, QColor(*c))) self.gradient.setStops(stops) self.update_rect() @@ -1129,10 +1367,12 @@ def update_setters(self): } self._setters[self.IMAGE_ANNOT_BOX] = self._setters[self.ANNOT_BOX] - self._setters[self.IMAGE_ANNOT_BOX].update({ - self.BKG_CBAR: self.update_cbar_label, - self.VECT_CBAR: self.update_vcbar_label, - }) + self._setters[self.IMAGE_ANNOT_BOX].update( + { + self.BKG_CBAR: self.update_cbar_label, + self.VECT_CBAR: self.update_vcbar_label, + } + ) @property def title_item(self): @@ -1140,8 +1380,11 @@ def title_item(self): @property def axis_items(self): - return [value["item"] for value in self.master.plot.axes.values()] \ - + [self.master.legend.axis] + [self.master.vect_legend.axis] + return ( + [value["item"] for value in self.master.plot.axes.values()] + + [self.master.legend.axis] + + [self.master.vect_legend.axis] + ) @property def getAxis(self): @@ -1161,7 +1404,6 @@ def vcolorbar(self): class VectorPlot(pg.GraphicsObject): - def __init__(self): pg.GraphicsObject.__init__(self) self.params = None @@ -1184,8 +1426,9 @@ def viewTransformChanged(self): def paint(self, p, option, widget): if self.params is not None: if isinstance(self.params[3], tuple): - path = pg.arrayToQPath(self.params[0], self.params[1], - connect = 'pairs', finiteCheck=False) + path = pg.arrayToQPath( + self.params[0], self.params[1], connect='pairs', finiteCheck=False + ) pen = QPen(QBrush(QColor(*self.params[3])), self.params[2]) pen.setCosmetic(True) p.setPen(pen) @@ -1194,19 +1437,23 @@ def paint(self, p, option, widget): pen = QPen(QBrush(QColor()), self.params[2]) pen.setCosmetic(True) unique_cols = np.unique(self.params[3][0], return_index=True, axis=0) - irgbx2 = np.hstack((self.params[3][0], - self.params[3][0])).reshape(self.params[3][0].shape[0]*2, 4) + irgbx2 = np.hstack((self.params[3][0], self.params[3][0])).reshape( + self.params[3][0].shape[0] * 2, 4 + ) for i in unique_cols[0]: - path = pg.arrayToQPath(self.params[0][np.where(irgbx2[:,0] == i[0])], - self.params[1][np.where(irgbx2[:,0] == i[0])], - connect = 'pairs', finiteCheck=False) + path = pg.arrayToQPath( + self.params[0][np.where(irgbx2[:, 0] == i[0])], + self.params[1][np.where(irgbx2[:, 0] == i[0])], + connect='pairs', + finiteCheck=False, + ) pen.setColor(QColor(*i[1:], self.params[3][1])) p.setPen(pen) p.drawPath(path) # These functions are the same as pg.plotcurveitem with small adaptations def pixelPadding(self): - self._maxSpotPxWidth = self.params[2]*0.7072 + self._maxSpotPxWidth = self.params[2] * 0.7072 return self._maxSpotPxWidth def boundingRect(self): @@ -1237,18 +1484,26 @@ def boundingRect(self): # return bounds expanded by pixel size px *= pxPad py *= pxPad - #px += self._maxSpotWidth * 0.5 - #py += self._maxSpotWidth * 0.5 - self._boundingRect = QRectF(xmn-px, ymn-py, (2*px)+xmx-xmn, (2*py)+ymx-ymn) + # px += self._maxSpotWidth * 0.5 + # py += self._maxSpotWidth * 0.5 + self._boundingRect = QRectF( + xmn - px, ymn - py, (2 * px) + xmx - xmn, (2 * py) + ymx - ymn + ) return self._boundingRect -class BasicImagePlot(QWidget, OWComponent, SelectionGroupMixin, - AxesSettingsMixin, ImageSelectionMixin, - ImageColorSettingMixin, ImageRGBSettingMixin, - ImageZoomMixin, ConcurrentMixin): - +class BasicImagePlot( + QWidget, + OWComponent, + SelectionGroupMixin, + AxesSettingsMixin, + ImageSelectionMixin, + ImageColorSettingMixin, + ImageRGBSettingMixin, + ImageZoomMixin, + ConcurrentMixin, +): gamma = Setting(0) selection_changed = Signal() @@ -1289,8 +1544,7 @@ def __init__(self, parent): self.legend = ImageColorLegend() ci.addItem(self.legend) - self.plot.scene().installEventFilter( - HelpEventDelegate(self.help_event, self)) + self.plot.scene().installEventFilter(HelpEventDelegate(self.help_event, self)) layout = QVBoxLayout() self.setLayout(layout) @@ -1317,14 +1571,18 @@ def __init__(self, parent): self.button.setMenu(view_menu) # prepare interface according to the new context - self.parent.contextAboutToBeOpened.connect(lambda x: self.init_interface_data(x[0])) + self.parent.contextAboutToBeOpened.connect( + lambda x: self.init_interface_data(x[0]) + ) self.add_zoom_actions(view_menu) self.add_selection_actions(view_menu) if self.saving_enabled: save_graph = QAction( - "Save graph", self, triggered=self.save_graph, + "Save graph", + self, + triggered=self.save_graph, ) save_graph.setShortcuts([QKeySequence(Qt.ControlModifier | Qt.Key_I)]) self.addAction(save_graph) @@ -1363,17 +1621,23 @@ def help_event(self, ev): sel = self._points_at_pos(pos) prepared = [] if sel is not None: - data, vals, points = self.data[sel], self.data_values[sel], self.data_points[sel] - for d, v, p in zip(data, vals, points): + data, vals, points = ( + self.data[sel], + self.data_values[sel], + self.data_points[sel], + ) + for d, v, p in zip(data, vals, points): # noqa: B905 basic = "({}, {}): {}".format(p[0], p[1], v) - variables = [v for v in self.data.domain.metas + self.data.domain.class_vars - if v not in [self.attr_x, self.attr_y]] + variables = [ + v + for v in self.data.domain.metas + self.data.domain.class_vars + if v not in [self.attr_x, self.attr_y] + ] features = ['{} = {}'.format(attr.name, d[attr]) for attr in variables] prepared.append("\n".join([basic] + features)) text = "\n\n".join(prepared) if text: - text = ('{}' - .format(escape(text))) + text = '{}'.format(escape(text)) QToolTip.showText(ev.screenPos(), text, widget=self.plotview) return True else: @@ -1386,8 +1650,7 @@ def init_attr_values(self, data): domain = data.domain if data is not None else None self.xy_model.set_domain(domain) self.attr_x = self.xy_model[0] if self.xy_model else None - self.attr_y = self.xy_model[1] if len(self.xy_model) >= 2 \ - else self.attr_x + self.attr_y = self.xy_model[1] if len(self.xy_model) >= 2 else self.attr_x def save_graph(self): saveplot.save_plot(self.plotview, self.parent.graph_writers) @@ -1406,15 +1669,17 @@ def refresh_img_selection(self): if self.lsx is None or self.lsy is None: return selected_px = np.zeros((self.lsy[2], self.lsx[2]), dtype=np.uint8) - selected_px[self.data_imagepixels[self.data_valid_positions, 0], - self.data_imagepixels[self.data_valid_positions, 1]] = \ - self.selection_group[self.data_valid_positions] + selected_px[ + self.data_imagepixels[self.data_valid_positions, 0], + self.data_imagepixels[self.data_valid_positions, 1], + ] = self.selection_group[self.data_valid_positions] self.img.setSelection(selected_px) def make_selection(self, selected, distances=None, modifiers=None): """Add selected indices to the selection.""" - add_to_group, add_group, remove = \ + add_to_group, add_group, remove = ( selection_modifiers() if modifiers is None else modifiers + ) if self.data and self.lsx and self.lsy: if add_to_group: # both keys - need to test it before add_group selnum = np.max(self.selection_group) @@ -1436,7 +1701,9 @@ def _points_at_pos(self, pos): if self.data and self.lsx and self.lsy and self.img.isVisible(): x, y = pos.x(), pos.y() distance = np.abs(self.data_points - [[x, y]]) - sel = (distance[:, 0] < _shift(self.lsx)) * (distance[:, 1] < _shift(self.lsy)) + sel = (distance[:, 0] < _shift(self.lsx)) * ( + distance[:, 1] < _shift(self.lsy) + ) return sel def select_by_click(self, pos): @@ -1459,9 +1726,14 @@ def update_view(self): self.xindex = None self.yindex = None - self.start(self.compute_image, self.data, self.attr_x, self.attr_y, - self.parent.image_values(), - self.parent.image_values_fixed_levels()) + self.start( + self.compute_image, + self.data, + self.attr_x, + self.attr_y, + self.parent.image_values(), + self.parent.image_values_fixed_levels(), + ) def set_visible_image(self, img: np.ndarray, rect: QRectF): self.vis_img.setImage(img) @@ -1482,10 +1754,14 @@ def set_visible_image_comp_mode(self, comp_mode: QPainter.CompositionMode): self.vis_img.setCompositionMode(comp_mode) @staticmethod - def compute_image(data: Orange.data.Table, attr_x, attr_y, - image_values, image_values_fixed_levels, - state: TaskState): - + def compute_image( + data: Orange.data.Table, + attr_x, + attr_y, + image_values, + image_values_fixed_levels, + state: TaskState, + ): if data is None or attr_x is None or attr_y is None: raise UndefinedImageException @@ -1493,22 +1769,24 @@ def progress_interrupt(i: float): if state.is_interruption_requested(): raise InterruptException - class Result(): + class Result: pass + res = Result() progress_interrupt(0) res.coorx = data.get_column(attr_x.name) res.coory = data.get_column(attr_y.name) - res.data_points = np.hstack([res.coorx.reshape(-1, 1), res.coory.reshape(-1, 1)]) + res.data_points = np.hstack( + [res.coorx.reshape(-1, 1), res.coory.reshape(-1, 1)] + ) res.lsx = lsx = values_to_linspace(res.coorx) res.lsy = lsy = values_to_linspace(res.coory) res.image_values_fixed_levels = image_values_fixed_levels progress_interrupt(0) - if lsx is not None and lsy is not None \ - and lsx[-1] * lsy[-1] > IMAGE_TOO_BIG: + if lsx is not None and lsy is not None and lsx[-1] * lsy[-1] > IMAGE_TOO_BIG: raise ImageTooBigException((lsx[-1], lsy[-1])) ims = image_values(data[:1]).X @@ -1570,8 +1848,8 @@ def draw(self, res, finished=False): shifty = _shift(lsy) left = lsx[0] - shiftx bottom = lsy[0] - shifty - width = (lsx[1]-lsx[0]) + 2*shiftx - height = (lsy[1]-lsy[0]) + 2*shifty + width = (lsx[1] - lsx[0]) + 2 * shiftx + height = (lsy[1] - lsy[0]) + 2 * shifty self.img.setRect(QRectF(left, bottom, width, height)) if finished: @@ -1603,7 +1881,6 @@ def _make_pen(color, width): class ScatterPlotMixin: - draw_as_scatterplot = Setting(False, schema_only=True) def __init__(self): @@ -1614,8 +1891,13 @@ def __init__(self): self.selection_changed.connect(self.draw_scatterplot) # add to a box defined in the parent class - gui.checkBox(self.axes_settings_box, self, "draw_as_scatterplot", - "As Scatter Plot", callback=self._draw_as_points) + gui.checkBox( + self.axes_settings_box, + self, + "draw_as_scatterplot", + "As Scatter Plot", + callback=self._draw_as_points, + ) self.img.setVisible(not self.draw_as_scatterplot) @@ -1653,11 +1935,11 @@ def draw_scatterplot(self): maxv = rgb_levels[:, 1] bins = 256 - intensity = np.round(((vals - minv)/(maxv - minv))*(bins-1)) + intensity = np.round(((vals - minv) / (maxv - minv)) * (bins - 1)) nans = ~np.isfinite(intensity) nans = np.any(nans, axis=1) intensity[nans] = 0 # to prevent artifact later on - binned = np.clip(intensity, 0, bins-1).astype(int) + binned = np.clip(intensity, 0, bins - 1).astype(int) if not is_rgb: colors = lut[binned[:, 0]] @@ -1670,7 +1952,7 @@ def draw_scatterplot(self): colors[nans] = [np.array(NAN_COLOR)] # replace unknown values with a color selection_colors = color_with_selections(colors, self.selection_group, None) - have_selection = not selection_colors is colors + have_selection = selection_colors is not colors @cache def mk_color(*args): @@ -1698,10 +1980,9 @@ def mk_pen_selection(*args): # Defaults from the Scatter Plot widget: # - size : 13.5 # - border is color.darker(120) with width of 1.5 - self.scatterplot_item.setData(x=xy[0], y=xy[1], - data=indexes, - pen=pens, - brush=brushes) + self.scatterplot_item.setData( + x=xy[0], y=xy[1], data=indexes, pen=pens, brush=brushes + ) def select_by_click_scatterplot(self, _, points): selected_indices = np.array([p.data() for p in points], dtype=int) @@ -1710,10 +1991,7 @@ def select_by_click_scatterplot(self, _, points): self.make_selection(sel) -class ImagePlot(BasicImagePlot, - VectorSettingMixin, VectorMixin, - ScatterPlotMixin): - +class ImagePlot(BasicImagePlot, VectorSettingMixin, VectorMixin, ScatterPlotMixin): attr_x = ContextSetting(None, exclude_attributes=True) attr_y = ContextSetting(None, exclude_attributes=True) @@ -1820,7 +2098,9 @@ class Outputs(SelectionOutputsMixin.Outputs): class Warning(OWWidget.Warning): threshold_error = Msg("Low slider should be less than High") - bin_size_error = Msg("Selected bin size larger than image size, bin size {} x {} used") + bin_size_error = Msg( + "Selected bin size larger than image size, bin size {} x {} used" + ) class Error(OWWidget.Error): image_too_big = Msg("Image for chosen features is too big ({} x {}).") @@ -1835,7 +2115,7 @@ def migrate_settings(cls, settings_, version): # delete the saved attr_value to prevent crashes try: del settings_["context_settings"][0].values["attr_value"] - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except # noqa: E722 pass # migrate selection @@ -1845,8 +2125,10 @@ def migrate_settings(cls, settings_, version): selection = getattr(current_context, "selection", None) if selection is not None: selection = [(i, 1) for i in np.flatnonzero(np.array(selection))] - settings_.setdefault("imageplot", {})["selection_group_saved"] = selection - except: # pylint: disable=bare-except + settings_.setdefault("imageplot", {})["selection_group_saved"] = ( + selection + ) + except: # noqa: E722 pylint: disable=bare-except pass if version < 6: @@ -1854,12 +2136,15 @@ def migrate_settings(cls, settings_, version): if version < 7: from orangecontrib.spectroscopy.widgets.owspectra import OWSpectra + OWSpectra.migrate_to_visual_settings(settings_) @classmethod def migrate_context(cls, context, version): if version <= 3 and "curveplot" in context.values: - CurvePlot.migrate_context_sub_feature_color(context.values["curveplot"], version) + CurvePlot.migrate_context_sub_feature_color( + context.values["curveplot"], version + ) def __init__(self): super().__init__() @@ -1870,52 +2155,78 @@ def __init__(self): dbox = gui.widgetBox(self.controlArea, "Image values") icbox = gui.widgetBox(self.controlArea, "Image colors") - + ivbox = gui.widgetBox(self.controlArea, "Vector plot") rbox = gui.radioButtons( - dbox, self, "value_type", callback=self._change_integration) + dbox, self, "value_type", callback=self._change_integration + ) gui.appendRadioButton(rbox, "From spectra") self.box_values_spectra = gui.indentedBox(rbox) gui.comboBox( - self.box_values_spectra, self, "integration_method", + self.box_values_spectra, + self, + "integration_method", items=(a.name for a in self.integration_methods), - callback=self._change_integral_type) + callback=self._change_integral_type, + ) gui.appendRadioButton(rbox, "Use feature") self.box_values_feature = gui.indentedBox(rbox) - self.feature_value_model = DomainModel(DomainModel.SEPARATED, - valid_types=DomainModel.PRIMITIVE) + self.feature_value_model = DomainModel( + DomainModel.SEPARATED, valid_types=DomainModel.PRIMITIVE + ) self.feature_value = gui.comboBox( - self.box_values_feature, self, "attr_value", - contentsLength=12, searchable=True, - callback=self.update_feature_value, model=self.feature_value_model) + self.box_values_feature, + self, + "attr_value", + contentsLength=12, + searchable=True, + callback=self.update_feature_value, + model=self.feature_value_model, + ) gui.appendRadioButton(rbox, "RGB") self.box_values_RGB_feature = gui.indentedBox(rbox) - self.rgb_value_model = DomainModel(DomainModel.SEPARATED, - valid_types=(ContinuousVariable,)) + self.rgb_value_model = DomainModel( + DomainModel.SEPARATED, valid_types=(ContinuousVariable,) + ) self.red_feature_value = gui.comboBox( - self.box_values_RGB_feature, self, "rgb_red_value", - contentsLength=12, searchable=True, - callback=self.update_rgb_value, model=self.rgb_value_model) + self.box_values_RGB_feature, + self, + "rgb_red_value", + contentsLength=12, + searchable=True, + callback=self.update_rgb_value, + model=self.rgb_value_model, + ) self.green_feature_value = gui.comboBox( - self.box_values_RGB_feature, self, "rgb_green_value", - contentsLength=12, searchable=True, - callback=self.update_rgb_value, model=self.rgb_value_model) + self.box_values_RGB_feature, + self, + "rgb_green_value", + contentsLength=12, + searchable=True, + callback=self.update_rgb_value, + model=self.rgb_value_model, + ) self.blue_feature_value = gui.comboBox( - self.box_values_RGB_feature, self, "rgb_blue_value", - contentsLength=12, searchable=True, - callback=self.update_rgb_value, model=self.rgb_value_model) + self.box_values_RGB_feature, + self, + "rgb_blue_value", + contentsLength=12, + searchable=True, + callback=self.update_rgb_value, + model=self.rgb_value_model, + ) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) @@ -1937,15 +2248,20 @@ def __init__(self): self.curveplot = CurvePlotHyper(self, select=SELECTONE) self.curveplot.selection_changed.connect(self.redraw_integral_info) - self.curveplot.plot.vb.x_padding = 0.005 # pad view so that lines are not hidden + self.curveplot.plot.vb.x_padding = ( + 0.005 # pad view so that lines are not hidden + ) splitter.addWidget(self.imageplot) splitter.addWidget(self.curveplot) self.mainArea.layout().addWidget(splitter) self.curveplot.locked_axes_changed.connect( - lambda locked: self.Information.view_locked(shown=locked)) + lambda locked: self.Information.view_locked(shown=locked) + ) self.traceplot = CurvePlotHyper(self) - self.traceplot.plot.vb.x_padding = 0.005 # pad view so that lines are not hidden + self.traceplot.plot.vb.x_padding = ( + 0.005 # pad view so that lines are not hidden + ) splitter.addWidget(self.traceplot) self.traceplot.button.hide() self.traceplot.hide() @@ -1954,15 +2270,25 @@ def __init__(self): self.line1 = MovableVline(position=self.lowlim, label="", report=self.curveplot) self.line1.sigMoved.connect(lambda v: setattr(self, "lowlim", v)) - self.line2 = MovableVline(position=self.highlim, label="", report=self.curveplot) + self.line2 = MovableVline( + position=self.highlim, label="", report=self.curveplot + ) self.line2.sigMoved.connect(lambda v: setattr(self, "highlim", v)) self.line3 = MovableVline(position=self.choose, label="", report=self.curveplot) self.line3.sigMoved.connect(lambda v: setattr(self, "choose", v)) - self.line4 = MovableVline(position=self.choose, label="baseline", report=self.curveplot, - color=(255, 140, 26)) + self.line4 = MovableVline( + position=self.choose, + label="baseline", + report=self.curveplot, + color=(255, 140, 26), + ) self.line4.sigMoved.connect(lambda v: setattr(self, "lowlimb", v)) - self.line5 = MovableVline(position=self.choose, label="baseline", report=self.curveplot, - color=(255, 140, 26)) + self.line5 = MovableVline( + position=self.choose, + label="baseline", + report=self.curveplot, + color=(255, 140, 26), + ) self.line5.sigMoved.connect(lambda v: setattr(self, "highlimb", v)) for line in [self.line1, self.line2, self.line3, self.line4, self.line5]: line.sigMoveFinished.connect(self.changed_integral_range) @@ -1985,12 +2311,15 @@ def __init__(self): gui.rubber(self.controlArea) def _setup_plot_parameters(self): - parts_from_spectra = [SpectraParameterSetter.ANNOT_BOX, - SpectraParameterSetter.LABELS_BOX, - SpectraParameterSetter.VIEW_RANGE_BOX] + parts_from_spectra = [ + SpectraParameterSetter.ANNOT_BOX, + SpectraParameterSetter.LABELS_BOX, + SpectraParameterSetter.VIEW_RANGE_BOX, + ] for cp in parts_from_spectra: - self.imageplot.parameter_setter.initial_settings[cp] = \ + self.imageplot.parameter_setter.initial_settings[cp] = ( self.curveplot.parameter_setter.initial_settings[cp] + ) VisualSettingsDialog(self, self.imageplot.parameter_setter.initial_settings) @@ -1998,35 +2327,55 @@ def setup_visible_image_controls(self): self.visbox = gui.widgetBox(self.controlArea, box="Visible image") gui.checkBox( - self.visbox, self, 'show_visible_image', + self.visbox, + self, + 'show_visible_image', label='Show visible image', - callback=lambda: (self.update_visible_image_interface(), self.update_visible_image())) + callback=lambda: ( + self.update_visible_image_interface(), + self.update_visible_image(), + ), + ) self.visboxhide = gui.widgetBox(self.visbox, box=False) self.visboxhide.hide() self.visible_image_model = VisibleImageListModel() gui.comboBox( - self.visboxhide, self, 'visible_image', + self.visboxhide, + self, + 'visible_image', model=self.visible_image_model, - callback=self.update_visible_image) - - self.visual_image_composition_modes = OrderedDict([ - ('Normal', QPainter.CompositionMode_Source), - ('Overlay', QPainter.CompositionMode_Overlay), - ('Multiply', QPainter.CompositionMode_Multiply), - ('Difference', QPainter.CompositionMode_Difference) - ]) + callback=self.update_visible_image, + ) + + self.visual_image_composition_modes = OrderedDict( + [ + ('Normal', QPainter.CompositionMode_Source), + ('Overlay', QPainter.CompositionMode_Overlay), + ('Multiply', QPainter.CompositionMode_Multiply), + ('Difference', QPainter.CompositionMode_Difference), + ] + ) gui.comboBox( - self.visboxhide, self, 'visible_image_composition', label='Composition mode:', + self.visboxhide, + self, + 'visible_image_composition', + label='Composition mode:', model=PyListModel(self.visual_image_composition_modes.keys()), - callback=self.update_visible_image_composition_mode + callback=self.update_visible_image_composition_mode, ) gui.hSlider( - self.visboxhide, self, 'visible_image_opacity', label='Opacity:', - minValue=0, maxValue=255, step=10, createLabel=False, - callback=self.update_visible_image_opacity + self.visboxhide, + self, + 'visible_image_opacity', + label='Opacity:', + minValue=0, + maxValue=255, + step=10, + createLabel=False, + callback=self.update_visible_image_opacity, ) self.update_visible_image_interface() @@ -2034,14 +2383,19 @@ def setup_visible_image_controls(self): self.update_visible_image_opacity() def update_visible_image_interface(self): - controlled = ['visible_image', 'visible_image_composition', 'visible_image_opacity'] + controlled = [ + 'visible_image', + 'visible_image_composition', + 'visible_image_opacity', + ] self.visboxhide.setVisible(self.show_visible_image) for c in controlled: getattr(self.controls, c).setEnabled(self.show_visible_image) def update_visible_image_composition_mode(self): self.imageplot.set_visible_image_comp_mode( - self.visual_image_composition_modes[self.visible_image_composition]) + self.visual_image_composition_modes[self.visible_image_composition] + ) def update_visible_image_opacity(self): self.imageplot.set_visible_image_opacity(self.visible_image_opacity) @@ -2059,8 +2413,12 @@ def draw_trace(self): distances = self.imageplot.selection_distances sel = self.imageplot.selection_group > 0 self.traceplot.set_data(None) - if distances is not None and self.imageplot.data \ - and self.imageplot.lsx and self.imageplot.lsy: + if ( + distances is not None + and self.imageplot.data + and self.imageplot.lsx + and self.imageplot.lsy + ): distances = distances[sel] sortidx = np.argsort(distances) values = self.imageplot.data_values[sel] @@ -2068,9 +2426,7 @@ def draw_trace(self): y = values[sortidx] # combine xs with the same value - groups, pos, g_count = np.unique(x, - return_index=True, - return_counts=True) + groups, pos, g_count = np.unique(x, return_index=True, return_counts=True) g_sum = np.add.reduceat(y, pos, axis=0) g_mean = g_sum / g_count[:, None] x, y = groups, g_mean @@ -2085,13 +2441,19 @@ def init_attr_values(self, data): domain = data.domain if data is not None else None self.feature_value_model.set_domain(domain) self.rgb_value_model.set_domain(domain) - self.attr_value = self.feature_value_model[0] if self.feature_value_model else None + self.attr_value = ( + self.feature_value_model[0] if self.feature_value_model else None + ) if self.rgb_value_model: # Filter PyListModel.Separator objects - rgb_attrs = [a for a in self.feature_value_model if isinstance(a, ContinuousVariable)] + rgb_attrs = [ + a for a in self.feature_value_model if isinstance(a, ContinuousVariable) + ] if len(rgb_attrs) <= 3: - rgb_attrs = (rgb_attrs + rgb_attrs[-1:]*3)[:3] - self.rgb_red_value, self.rgb_green_value, self.rgb_blue_value = rgb_attrs[:3] + rgb_attrs = (rgb_attrs + rgb_attrs[-1:] * 3)[:3] + self.rgb_red_value, self.rgb_green_value, self.rgb_blue_value = rgb_attrs[ + :3 + ] else: self.rgb_red_value = self.rgb_green_value = self.rgb_blue_value = None @@ -2101,9 +2463,11 @@ def init_visible_images(self, data): self.visbox.setEnabled(True) for img in data.attributes['visible_images']: if not isinstance(img, VisibleImage): - warnings.warn("Visible images need to subclass VisibleImage; " - "Backward compatibility will be removed in the future.", - OrangeDeprecationWarning) + warnings.warn( # noqa: B028 + "Visible images need to subclass VisibleImage; " + "Backward compatibility will be removed in the future.", + OrangeDeprecationWarning, + ) self.visible_image_model.append(img) else: self.visbox.setEnabled(False) @@ -2130,38 +2494,43 @@ def redraw_integral_info(self): # curveplot can have a subset of curves on the input> match IDs ind = np.flatnonzero(self.curveplot.selection_group)[0] dind = self.imageplot.data_ids[self.curveplot.data[ind].id] - dshow = self.data[dind:dind+1] + dshow = self.data[dind : dind + 1] datai = integrate(dshow) draw_info = datai.domain.attributes[0].compute_value.draw_info di = draw_info(dshow) self.refresh_markings(di) def refresh_markings(self, di): - refresh_integral_markings([{"draw": di}], self.markings_integral, self.curveplot) + refresh_integral_markings( + [{"draw": di}], self.markings_integral, self.curveplot + ) def image_values(self): if self.value_type == 0: # integrals imethod = self.integration_methods[self.integration_method] if imethod == Integrate.Separate: - return Integrate(methods=imethod, - limits=[[self.lowlim, self.highlim, - self.lowlimb, self.highlimb]]) + return Integrate( + methods=imethod, + limits=[[self.lowlim, self.highlim, self.lowlimb, self.highlimb]], + ) elif imethod != Integrate.PeakAt: - return Integrate(methods=imethod, - limits=[[self.lowlim, self.highlim]]) + return Integrate(methods=imethod, limits=[[self.lowlim, self.highlim]]) else: - return Integrate(methods=imethod, - limits=[[self.choose, self.choose]]) + return Integrate(methods=imethod, limits=[[self.choose, self.choose]]) elif self.value_type == 1: # feature - return lambda data, attr=self.attr_value: \ - data.transform(Domain([data.domain[attr]])) + return lambda data, attr=self.attr_value: data.transform( + Domain([data.domain[attr]]) + ) elif self.value_type == 2: # RGB red = ContinuousVariable("red", compute_value=Identity(self.rgb_red_value)) - green = ContinuousVariable("green", compute_value=Identity(self.rgb_green_value)) - blue = ContinuousVariable("blue", compute_value=Identity(self.rgb_blue_value)) - return lambda data: \ - data.transform(Domain([red, green, blue])) + green = ContinuousVariable( + "green", compute_value=Identity(self.rgb_green_value) + ) + blue = ContinuousVariable( + "blue", compute_value=Identity(self.rgb_blue_value) + ) + return lambda data: data.transform(Domain([red, green, blue])) def image_values_fixed_levels(self): if self.value_type == 1 and isinstance(self.attr_value, DiscreteVariable): @@ -2230,8 +2599,11 @@ def set_data(self, data): def valid_context(data): if data is None: return False - annotation_features = [v for v in data.domain.metas + data.domain.class_vars - if isinstance(v, (DiscreteVariable, ContinuousVariable))] + annotation_features = [ + v + for v in data.domain.metas + data.domain.class_vars + if isinstance(v, (DiscreteVariable, ContinuousVariable)) + ] return len(annotation_features) >= 1 if valid_context(data): @@ -2250,8 +2622,10 @@ def valid_context(data): def set_visual_settings(self, key, value): im_setter = self.imageplot.parameter_setter cv_setter = self.curveplot.parameter_setter - skip_im_setter = [SpectraParameterSetter.ANNOT_BOX, - SpectraParameterSetter.VIEW_RANGE_BOX] + skip_im_setter = [ + SpectraParameterSetter.ANNOT_BOX, + SpectraParameterSetter.VIEW_RANGE_BOX, + ] if key[0] not in skip_im_setter and key[0] in im_setter.initial_settings: im_setter.set_parameter(key, value) if key[0] in cv_setter.initial_settings: @@ -2265,8 +2639,8 @@ def _init_integral_boundaries(self): minx = self.curveplot.data_x[0] maxx = self.curveplot.data_x[-1] else: - minx = 0. - maxx = 1. + minx = 0.0 + maxx = 1.0 if self.lowlim is None or not minx <= self.lowlim <= maxx: self.lowlim = minx @@ -2277,7 +2651,7 @@ def _init_integral_boundaries(self): self.line2.setValue(self.highlim) if self.choose is None: - self.choose = (minx + maxx)/2 + self.choose = (minx + maxx) / 2 elif self.choose < minx: self.choose = minx elif self.choose > maxx: @@ -2315,10 +2689,16 @@ def update_visible_image(self): else: name = img_info["name"] img = np.array(Image.open(img_info['image_ref']).convert('RGBA')) - width = img_info['img_size_x'] if 'img_size_x' in img_info \ + width = ( + img_info['img_size_x'] + if 'img_size_x' in img_info else img.shape[1] * img_info['pixel_size_x'] - height = img_info['img_size_y'] if 'img_size_y' in img_info \ + ) + height = ( + img_info['img_size_y'] + if 'img_size_y' in img_info else img.shape[0] * img_info['pixel_size_y'] + ) pos_x = img_info['pos_x'] pos_y = img_info['pos_y'] self.visible_image_name = name # save visual image name @@ -2326,10 +2706,7 @@ def update_visible_image(self): # https://github.com/pyqtgraph/pyqtgraph/issues/315#issuecomment-214042453 # Behavior may change at pyqtgraph 1.0 version img = img[::-1] - rect = QRectF(pos_x, - pos_y, - width, - height) + rect = QRectF(pos_x, pos_y, width, height) self.imageplot.set_visible_image(img, rect) self.imageplot.show_visible_image() else: @@ -2338,4 +2715,5 @@ def update_visible_image(self): if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWHyper).run(Orange.data.Table("iris.tab")) diff --git a/orangecontrib/spectroscopy/widgets/owintegrate.py b/orangecontrib/spectroscopy/widgets/owintegrate.py index 7bf842314..5c9481e3b 100644 --- a/orangecontrib/spectroscopy/widgets/owintegrate.py +++ b/orangecontrib/spectroscopy/widgets/owintegrate.py @@ -8,7 +8,12 @@ import Orange.data from Orange import preprocess from Orange.widgets.data.owpreprocess import ( - PreprocessAction, Description, icon_path, DescriptionRole, ParametersRole, blocked, + PreprocessAction, + Description, + icon_path, + DescriptionRole, + ParametersRole, + blocked, ) from Orange.widgets import gui, settings from Orange.widgets.widget import Output, Msg @@ -19,16 +24,19 @@ from orangecontrib.spectroscopy.widgets.owspectra import SELECTONE from orangecontrib.spectroscopy.widgets.owhyper import refresh_integral_markings from orangecontrib.spectroscopy.widgets.owpreprocess import ( - SpectralPreprocess, create_preprocessor, InterruptException + SpectralPreprocess, + create_preprocessor, + InterruptException, +) +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + BaseEditorOrange, + SetXDoubleSpinBox, ) -from orangecontrib.spectroscopy.widgets.preprocessors.utils import BaseEditorOrange, \ - SetXDoubleSpinBox from orangecontrib.spectroscopy.widgets.gui import MovableVline class IntegrateOneEditor(BaseEditorOrange): - class Warning(BaseEditorOrange.Warning): out_of_range = Msg("Limit out of range.") @@ -44,17 +52,18 @@ def __init__(self, parent=None, **kwargs): self.__editors = {} self.__lines = {} - for name, longname in self.integrator.parameters(): - v = 0. + for name, _longname in self.integrator.parameters(): + v = 0.0 self.__values[name] = v - e = SetXDoubleSpinBox(minimum=minf, maximum=maxf, - singleStep=0.5, value=v) + e = SetXDoubleSpinBox(minimum=minf, maximum=maxf, singleStep=0.5, value=v) e.focusIn = self.activateOptions e.editingFinished.connect(self.edited) + def cf(x, name=name): self.edited.emit() return self.set_value(name, x) + e.valueChanged[float].connect(cf) self.__editors[name] = e layout.addRow(name, e) @@ -64,8 +73,10 @@ def cf(x, name=name): color = (255, 140, 26) l = MovableVline(position=v, label=name, color=color) + def set_rounded(_, line=l, name=name): cf(float(line.rounded_value()), name) + l.sigMoved.connect(set_rounded) self.__lines[name] = l @@ -95,7 +106,7 @@ def setParameters(self, params): if params: # parameters were set manually set self.user_changed = True for name, _ in self.integrator.parameters(): - self.set_value(name, params.get(name, 0.), user=False) + self.set_value(name, params.get(name, 0.0), user=False) def parameters(self): return self.__values @@ -104,8 +115,8 @@ def parameters(self): def createinstance(cls, params): params = dict(params) values = [] - for ind, (name, _) in enumerate(cls.integrator.parameters()): - values.append(params.get(name, 0.)) + for _ind, (name, _) in enumerate(cls.integrator.parameters()): + values.append(params.get(name, 0.0)) return Integrate(methods=cls.integrator, limits=[values], metas=True) def set_preview_data(self, data): @@ -115,8 +126,10 @@ def set_preview_data(self, data): if len(xs): minx = np.min(xs) maxx = np.max(xs) - limits = [self.__values.get(name, 0.) - for ind, (name, _) in enumerate(self.integrator.parameters())] + limits = [ + self.__values.get(name, 0.0) + for ind, (name, _) in enumerate(self.integrator.parameters()) + ] for v in limits: if v < minx or v > maxx: self.parent_widget.Warning.preprocessor() @@ -198,10 +211,13 @@ def set_preview_data(self, data): PREPROCESSORS = [ PreprocessAction( - "Integrate", c.qualname, "Integration", + "Integrate", + c.qualname, + "Integration", Description(c.integrator.name, icon_path("Discretize.svg")), - c - ) for c in [ + c, + ) + for c in [ IntegrateSimpleEditor, IntegrateBaselineEditor, IntegrateBaselineAbsoluteEditor, @@ -239,7 +255,13 @@ class Outputs: def __init__(self): self.markings_list = [] super().__init__() - cb = gui.checkBox(self.output_box, self, "output_metas", "Output as metas", callback=self.commit.deferred) + cb = gui.checkBox( + self.output_box, + self, + "output_metas", + "Output as metas", + callback=self.commit.deferred, + ) self.output_box.layout().insertWidget(0, cb) # move to top of the box self.curveplot.selection_type = SELECTONE self.curveplot.select_at_least_1 = True @@ -251,7 +273,7 @@ def redraw_integral(self): if np.any(self.curveplot.selection_group) and self.curveplot.data: # select data ind = np.flatnonzero(self.curveplot.selection_group)[0] - show = self.curveplot.data[ind:ind+1] + show = self.curveplot.data[ind : ind + 1] previews = self.flow_view.preview_n() for i in range(self.preprocessormodel.rowCount()): @@ -274,12 +296,14 @@ def show_preview(self, show_info_anyway=False): super().show_preview(False) def create_outputs(self): - pp_def = [self.preprocessormodel.item(i) for i in range(self.preprocessormodel.rowCount())] + pp_def = [ + self.preprocessormodel.item(i) + for i in range(self.preprocessormodel.rowCount()) + ] self.start(self.run_task, self.data, pp_def, self.output_metas) @staticmethod def run_task(data: Orange.data.Table, pp_def, output_metas, state): - def progress_interrupt(i: float): state.set_progress_value(i) if state.is_interruption_requested(): @@ -289,7 +313,7 @@ def progress_interrupt(i: float): # happen when adding a preprocessor (there, commit() is called twice). # Wait 100 ms before processing - if a new task is started in meanwhile, # allow that is easily` cancelled. - for i in range(10): + for _i in range(10): time.sleep(0.005) progress_interrupt(0) @@ -303,7 +327,9 @@ def progress_interrupt(i: float): preprocessor = None if plist: - preprocessor = PreprocessorListMoveMetas(not output_metas, preprocessors=plist) + preprocessor = PreprocessorListMoveMetas( + not output_metas, preprocessors=plist + ) if data is not None and preprocessor is not None: data = preprocessor(data) @@ -325,12 +351,14 @@ def __call__(self, data): if self.move_metas: oldmetas = set(data.domain.metas) newmetas = [m for m in tdata.domain.metas if m not in oldmetas] - domain = Orange.data.Domain(newmetas, data.domain.class_vars, - metas=data.domain.metas) + domain = Orange.data.Domain( + newmetas, data.domain.class_vars, metas=data.domain.metas + ) tdata = tdata.transform(domain) return tdata if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWIntegrate).run(Orange.data.Table("collagen.csv")) diff --git a/orangecontrib/spectroscopy/widgets/owinterpolate.py b/orangecontrib/spectroscopy/widgets/owinterpolate.py index 7b3d11003..c41a04de4 100644 --- a/orangecontrib/spectroscopy/widgets/owinterpolate.py +++ b/orangecontrib/spectroscopy/widgets/owinterpolate.py @@ -1,4 +1,3 @@ -import sys import math import numpy as np @@ -9,8 +8,11 @@ from AnyQt.QtWidgets import QFormLayout, QWidget from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.preprocess import Interpolate, InterpolateToDomain, \ - NotAllContinuousException +from orangecontrib.spectroscopy.preprocess import ( + Interpolate, + InterpolateToDomain, + NotAllContinuousException, +) from orangecontrib.spectroscopy.widgets.gui import lineEditFloatOrNone @@ -34,7 +36,7 @@ class Outputs: # specification of linear space xmin = settings.Setting(None) xmax = settings.Setting(None) - dx = settings.Setting(10.) + dx = settings.Setting(10.0) autocommit = settings.Setting(True) @@ -57,8 +59,7 @@ def __init__(self): dbox = gui.widgetBox(self.controlArea, "Interpolation") - rbox = gui.radioButtons( - dbox, self, "input_radio", callback=self._change_input) + rbox = gui.radioButtons(dbox, self, "input_radio", callback=self._change_input) gui.appendRadioButton(rbox, "Enable automatic interpolation") @@ -71,11 +72,17 @@ def __init__(self): form.setLayout(formlayout) ibox.layout().addWidget(form) - self.xmin_edit = lineEditFloatOrNone(ibox, self, "xmin", callback=self.commit.deferred) + self.xmin_edit = lineEditFloatOrNone( + ibox, self, "xmin", callback=self.commit.deferred + ) formlayout.addRow("Min", self.xmin_edit) - self.xmax_edit = lineEditFloatOrNone(ibox, self, "xmax", callback=self.commit.deferred) + self.xmax_edit = lineEditFloatOrNone( + ibox, self, "xmax", callback=self.commit.deferred + ) formlayout.addRow("Max", self.xmax_edit) - self.dx_edit = lineEditFloatOrNone(ibox, self, "dx", callback=self.commit.deferred) + self.dx_edit = lineEditFloatOrNone( + ibox, self, "dx", callback=self.commit.deferred + ) formlayout.addRow("Δ", self.dx_edit) gui.appendRadioButton(rbox, "Reference data") @@ -102,7 +109,7 @@ def commit(self): xmin = self.xmin if self.xmin is not None else np.min(xs) xmax = self.xmax if self.xmax is not None else np.max(xs) xmin, xmax = min(xmin, xmax), max(xmin, xmax) - reslength = abs(math.ceil((xmax - xmin)/self.dx)) + reslength = abs(math.ceil((xmax - xmin) / self.dx)) if reslength < 10002: points = np.arange(xmin, xmax, self.dx) out = Interpolate(points)(self.data) @@ -153,6 +160,7 @@ def handleNewSignals(self): self.commit.now() -if __name__=="__main__": +if __name__ == "__main__": from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWInterpolate).run(Orange.data.Table("collagen")) diff --git a/orangecontrib/spectroscopy/widgets/owmultifile.py b/orangecontrib/spectroscopy/widgets/owmultifile.py index 8beecd58f..2e36ded88 100644 --- a/orangecontrib/spectroscopy/widgets/owmultifile.py +++ b/orangecontrib/spectroscopy/widgets/owmultifile.py @@ -8,26 +8,39 @@ import numpy as np from AnyQt.QtCore import Qt -from AnyQt.QtWidgets import QSizePolicy as Policy, QGridLayout, QLabel, \ - QFileDialog, QStyle, QListWidget - -from Orange.data import Domain, Table, DiscreteVariable, Variable, ContinuousVariable, StringVariable +from AnyQt.QtWidgets import ( + QSizePolicy as Policy, + QGridLayout, + QLabel, + QFileDialog, + QStyle, + QListWidget, +) + +from Orange.data import Domain, Table, Variable, ContinuousVariable, StringVariable from Orange.data.io import FileFormat, class_from_qualified_name from Orange.data.util import get_unique_names_duplicates, get_unique_names from Orange.widgets import widget, gui -from Orange.widgets.settings import Setting, ContextSetting, \ - PerfectDomainContextHandler, SettingProvider +from Orange.widgets.settings import ( + Setting, + ContextSetting, + PerfectDomainContextHandler, + SettingProvider, +) from Orange.widgets.utils.annotated_data import add_columns from Orange.widgets.utils.domaineditor import DomainEditor -from Orange.widgets.utils.filedialogs import RecentPathsWidgetMixin, \ - RecentPath, open_filename_dialog +from Orange.widgets.utils.filedialogs import ( + RecentPathsWidgetMixin, + RecentPath, + open_filename_dialog, +) from Orange.widgets.utils.signals import Output from orangecontrib.spectroscopy.io.util import SpectralFileFormat def numpy_union_keep_order(A, B): - """ Union of A and B. Elements not in A are + """Union of A and B. Elements not in A are added in the same order as in B.""" # sorted list of elements missing in A to_add = np.setdiff1d(B, A) @@ -57,7 +70,7 @@ def decimals_neeeded_for_unique_str(l): def wns_to_unique_str(l): - """ Convert a list on wns to a list of unique strings. + """Convert a list on wns to a list of unique strings. Use 6 decimal places by default. If that is not sufficient, increase precision as needed for wns in conflict. @@ -93,15 +106,20 @@ def concatenate_data(tables, filenames, label): # prepare xs from the spectral specific tables for join into a common domain spectral_specific_domains = [] - xss = [t.special_spectral_data[0] for t in tables - if hasattr(t, "special_spectral_data")] + xss = [ + t.special_spectral_data[0] + for t in tables + if hasattr(t, "special_spectral_data") + ] xs = reduce(numpy_union_keep_order, xss, np.array([])) if len(xs): names = wns_to_unique_str(xs) attrs = [ContinuousVariable(n) for n in names] spectral_specific_domains = [Domain(attrs, None, None)] - domain = _merge_domains(spectral_specific_domains + [table.domain for table in tables]) + domain = _merge_domains( + spectral_specific_domains + [table.domain for table in tables] + ) name = get_unique_names(domain, "Filename") source_var = StringVariable(name) name = get_unique_names(domain, "Label") @@ -121,17 +139,21 @@ def concatenate_data(tables, filenames, label): if hasattr(table, "special_spectral_data"): special = table.special_spectral_data indices = xs_sind[np.searchsorted(xs_sorted, special[0])] - data.X[pos:pos+len(table), indices] = special[1] + data.X[pos : pos + len(table), indices] = special[1] pos += len(table) - data[:, source_var] = np.array(list( - chain(*(repeat(fn, len(table)) - for fn, table in zip(filenames, tables))) - )).reshape(-1, 1) - data[:, label_var] = np.array(list( - chain(*(repeat(label, len(table)) - for _, table in zip(filenames, tables))) - )).reshape(-1, 1) + data[:, source_var] = np.array( + list( + chain(*(repeat(fn, len(table)) for fn, table in zip(filenames, tables))) # noqa: B905 + ) + ).reshape(-1, 1) + data[:, label_var] = np.array( + list( + chain( + *(repeat(label, len(table)) for _, table in zip(filenames, tables)) # noqa: B905 + ) + ) + ).reshape(-1, 1) return data @@ -142,8 +164,10 @@ def fix_names(part): if attr.name != name: part[i] = attr.renamed(name) - parts = [_get_part(domains, set.union, part) - for part in ("attributes", "class_vars", "metas")] + parts = [ + _get_part(domains, set.union, part) + for part in ("attributes", "class_vars", "metas") + ] all_names = [var.name for var in chain(*parts)] name_iter = iter(get_unique_names_duplicates(all_names)) for part in parts: @@ -162,33 +186,33 @@ def _get_part(domains, oper, part): def _unique_vars(seq: List[Variable]): AttrDesc = namedtuple( - "AttrDesc", - ("template", "original", "values", "number_of_decimals") + "AttrDesc", ("template", "original", "values", "number_of_decimals") ) attrs = {} for el in seq: desc = attrs.get(el) if desc is None: - attrs[el] = AttrDesc(el, True, - el.is_discrete and el.values, - el.is_continuous and el.number_of_decimals) + attrs[el] = AttrDesc( + el, + True, + el.is_discrete and el.values, + el.is_continuous and el.number_of_decimals, + ) continue if desc.template.is_discrete: sattr_values = set(desc.values) # don't use sets: keep the order - missing_values = tuple( - val for val in el.values if val not in sattr_values - ) + missing_values = tuple(val for val in el.values if val not in sattr_values) if missing_values: attrs[el] = attrs[el]._replace( - original=False, - values=desc.values + missing_values) + original=False, values=desc.values + missing_values + ) elif desc.template.is_continuous: if el.number_of_decimals > desc.number_of_decimals: attrs[el] = attrs[el]._replace( - original=False, - number_of_decimals=el.number_of_decimals) + original=False, number_of_decimals=el.number_of_decimals + ) new_attrs = [] for desc in attrs.values(): @@ -197,7 +221,7 @@ def _unique_vars(seq: List[Variable]): new_attr = attr elif desc.template.is_discrete: new_attr = attr.copy() - for val in desc.values[len(attr.values):]: + for val in desc.values[len(attr.values) :]: new_attr.add_value(val) else: assert desc.template.is_continuous @@ -227,14 +251,14 @@ class OWMultifile(widget.OWWidget, RelocatablePathsWidgetMixin): name = "Multifile" id = "orangecontrib.spectroscopy.widgets.files" icon = "icons/multifile.svg" - description = "Read data from input files " \ - "and send a data table to the output." + description = "Read data from input files and send a data table to the output." priority = 10000 - replaces = ["orangecontrib.infrared.widgets.owfiles.OWFiles", - "orangecontrib.infrared.widgets.owmultifile.OWMultifile", - # next file: a file unintentionally added in one version - "orangecontrib.spectroscopy.widgets.owmultifile_vesna.OWMultifile", - ] + replaces = [ + "orangecontrib.infrared.widgets.owfiles.OWFiles", + "orangecontrib.infrared.widgets.owmultifile.OWMultifile", + # next file: a file unintentionally added in one version + "orangecontrib.spectroscopy.widgets.owmultifile_vesna.OWMultifile", + ] keywords = ["file", "files", "multiple"] class Outputs: @@ -271,40 +295,40 @@ def __init__(self): self.loaded_file = "" self.sheets = [] - self.lb = gui.listBox(self.controlArea, self, "file_idx", - selectionMode=QListWidget.MultiSelection) + self.lb = gui.listBox( + self.controlArea, self, "file_idx", selectionMode=QListWidget.MultiSelection + ) self.default_foreground = None layout = QGridLayout() gui.widgetBox(self.controlArea, margin=0, orientation=layout) file_button = gui.button( - None, self, ' ...', callback=self.browse_files, autoDefault=False) - file_button.setIcon(self.style().standardIcon( - QStyle.SP_DirOpenIcon)) + None, self, ' ...', callback=self.browse_files, autoDefault=False + ) + file_button.setIcon(self.style().standardIcon(QStyle.SP_DirOpenIcon)) file_button.setSizePolicy(Policy.Maximum, Policy.Fixed) layout.addWidget(file_button, 0, 0) - remove_button = gui.button( - None, self, 'Remove', callback=self.remove_item) + remove_button = gui.button(None, self, 'Remove', callback=self.remove_item) - clear_button = gui.button( - None, self, 'Clear', callback=self.clear) + clear_button = gui.button(None, self, 'Clear', callback=self.clear) layout.addWidget(remove_button, 0, 1) layout.addWidget(clear_button, 0, 2) reload_button = gui.button( - None, self, "Reload", callback=self.load_data, autoDefault=False) - reload_button.setIcon( - self.style().standardIcon(QStyle.SP_BrowserReload)) + None, self, "Reload", callback=self.load_data, autoDefault=False + ) + reload_button.setIcon(self.style().standardIcon(QStyle.SP_BrowserReload)) reload_button.setSizePolicy(Policy.Fixed, Policy.Fixed) layout.addWidget(reload_button, 0, 7) self.sheet_box = gui.hBox(None, addToLayout=False, margin=0) self.sheet_index = 0 - self.sheet_combo = gui.comboBox(None, self, "sheet_index", - callback=self.select_sheet) + self.sheet_combo = gui.comboBox( + None, self, "sheet_index", callback=self.select_sheet + ) self.sheet_combo.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed) self.sheet_label = QLabel() self.sheet_label.setText('Sheet') @@ -317,8 +341,14 @@ def __init__(self): layout.addWidget(self.sheet_box, 0, 5) label_box = gui.hBox(None, addToLayout=False, margin=0) - gui.lineEdit(label_box, self, "label", callback=self.set_label, - label="Label", orientation=Qt.Horizontal) + gui.lineEdit( + label_box, + self, + "label", + callback=self.set_label, + label="Label", + orientation=Qt.Horizontal, + ) layout.addWidget(label_box, 0, 6) layout.setColumnStretch(3, 2) @@ -336,11 +366,13 @@ def __init__(self): gui.button(box, self, "Reset", callback=self.reset_domain_edit) self.apply_button = gui.button( - box, self, "Apply", callback=self.apply_domain_edit) + box, self, "Apply", callback=self.apply_domain_edit + ) self.apply_button.setEnabled(False) self.apply_button.setFixedWidth(170) self.editor_model.dataChanged.connect( - lambda: self.apply_button.setEnabled(True)) + lambda: self.apply_button.setEnabled(True) + ) self._update_sheet_combo() self.load_data() @@ -367,7 +399,7 @@ def _update_sheet_combo(self): try: reader = _get_reader(rp) sheets.update(reader.sheets) - except: + except: # noqa: E722 pass sheets = sorted(sheets.items(), key=lambda x: x[0]) @@ -406,11 +438,14 @@ def clear(self): def browse_files(self): start_file = self.last_path() or os.path.expanduser("~/") - readers = [f for f in FileFormat.formats if - getattr(f, 'read', None) and getattr(f, "EXTENSIONS", None)] - filenames, reader, _ = \ - open_filename_dialog(start_file, None, readers, - dialog=QFileDialog.getOpenFileNames) + readers = [ + f + for f in FileFormat.formats + if getattr(f, 'read', None) and getattr(f, "EXTENSIONS", None) + ] + filenames, reader, _ = open_filename_dialog( + start_file, None, readers, dialog=QFileDialog.getOpenFileNames + ) self.load_files(filenames, reader) @@ -478,9 +513,12 @@ def show_error(li, msg): show_error(li, "Read error:\n" + str(ex)) self.Error.read_error() - if not data_list or self.Error.file_not_found.is_shown() \ - or self.Error.missing_reader.is_shown() \ - or self.Error.read_error.is_shown(): + if ( + not data_list + or self.Error.file_not_found.is_shown() + or self.Error.missing_reader.is_shown() + or self.Error.read_error.is_shown() + ): self.data = None self.domain_editor.set_domain(None) else: @@ -501,8 +539,7 @@ def apply_domain_edit(self): if self.data is None: table = None else: - domain, cols = self.domain_editor.get_domain(self.data.domain, - self.data) + domain, cols = self.domain_editor.get_domain(self.data.domain, self.data) if not (domain.variables or domain.metas): table = None else: @@ -564,4 +601,5 @@ def _get_reader(rp): if __name__ == "__main__": # pragma: no cover # pylint: disable=ungrouped-imports from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWMultifile).run() diff --git a/orangecontrib/spectroscopy/widgets/owoverlay.py b/orangecontrib/spectroscopy/widgets/owoverlay.py index 8d0c60e13..41f7b6579 100644 --- a/orangecontrib/spectroscopy/widgets/owoverlay.py +++ b/orangecontrib/spectroscopy/widgets/owoverlay.py @@ -11,7 +11,7 @@ from Orange.widgets.utils.itemmodels import DomainModel from Orange.widgets.widget import OWWidget, Input, Output, Msg from Orange.widgets import gui, settings -from Orange.widgets.utils.concurrent import TaskState, ConcurrentWidgetMixin +from Orange.widgets.utils.concurrent import ConcurrentWidgetMixin from orangewidget.settings import SettingProvider, ContextSetting @@ -148,8 +148,8 @@ def __init__(self): gui.comboBox( box, self, - f"attr_x", - label=f"Axis X:", + "attr_x", + label="Axis X:", callback=self._attr_changed, model=self.xy_model, **common_options, @@ -157,8 +157,8 @@ def __init__(self): gui.comboBox( box, self, - f"attr_y", - label=f"Axis Y:", + "attr_y", + label="Axis Y:", callback=self._attr_changed, model=self.xy_model, **common_options, @@ -213,7 +213,6 @@ def redraw_data(self): @staticmethod def with_overlay(imageplot, data, maindata, state): - if data is None or maindata is None: return None @@ -229,9 +228,7 @@ def progress_interrupt(): break # the following could raise an InvalidAxisException - hypercube, ls = get_ndim_hyperspec( - data, (imageplot.attr_x, imageplot.attr_y) - ) + hypercube, ls = get_ndim_hyperspec(data, (imageplot.attr_x, imageplot.attr_y)) # if drawing failed for some reason if imageplot.img.image is None: @@ -260,9 +257,7 @@ def progress_interrupt(): # Update name allnames = [] if "visible_images" in newmaindata.attributes: - allnames = [ - im.name for im in newmaindata.attributes["visible_images"] - ] + allnames = [im.name for im in newmaindata.attributes["visible_images"]] basename = "Overlay Image" name = get_unique_names(names=allnames, proposed=basename) # Need to modify the position and the scale since visual imageplot diff --git a/orangecontrib/spectroscopy/widgets/owpeakfit.py b/orangecontrib/spectroscopy/widgets/owpeakfit.py index ae942e834..5e4976d65 100644 --- a/orangecontrib/spectroscopy/widgets/owpeakfit.py +++ b/orangecontrib/spectroscopy/widgets/owpeakfit.py @@ -4,7 +4,7 @@ import multiprocessing import os -from lmfit import Parameters, Model +from lmfit import Parameters from lmfit.model import ModelResult import numpy as np @@ -22,26 +22,59 @@ from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.preprocess import Cut -from orangecontrib.spectroscopy.preprocess.integrate import INTEGRATE_DRAW_CURVE_PENARGS, \ - INTEGRATE_DRAW_BASELINE_PENARGS +from orangecontrib.spectroscopy.preprocess.integrate import ( + INTEGRATE_DRAW_CURVE_PENARGS, + INTEGRATE_DRAW_BASELINE_PENARGS, +) from orangecontrib.spectroscopy.widgets.owhyper import refresh_integral_markings -from orangecontrib.spectroscopy.widgets.owpreprocess import SpectralPreprocess, \ - InterruptException, PreviewRunner +from orangecontrib.spectroscopy.widgets.owpreprocess import ( + SpectralPreprocess, + InterruptException, + PreviewRunner, +) from orangecontrib.spectroscopy.widgets.owspectra import SELECTONE -from orangecontrib.spectroscopy.widgets.peak_editors import GaussianModelEditor, \ - LorentzianModelEditor, SplitLorentzianModelEditor, VoigtModelEditor, PseudoVoigtModelEditor, \ - MoffatModelEditor, Pearson7ModelEditor, StudentsTModelEditor, BreitWignerModelEditor, \ - LognormalModelEditor, DampedOscillatorModelEditor, DampedHarmOscillatorModelEditor, \ - ExponentialGaussianModelEditor, SkewedGaussianModelEditor, SkewedVoigtModelEditor, \ - ThermalDistributionModelEditor, DoniachModelEditor, ConstantModelEditor, \ - LinearModelEditor, QuadraticModelEditor, PolynomialModelEditor, set_default_vary -from orangecontrib.spectroscopy.widgets.peakfit_compute import n_best_fit_parameters, \ - best_fit_results, pool_initializer, pool_fit, pool_fit2 +from orangecontrib.spectroscopy.widgets.peak_editors import ( + GaussianModelEditor, + LorentzianModelEditor, + SplitLorentzianModelEditor, + VoigtModelEditor, + PseudoVoigtModelEditor, + MoffatModelEditor, + Pearson7ModelEditor, + StudentsTModelEditor, + BreitWignerModelEditor, + LognormalModelEditor, + DampedOscillatorModelEditor, + DampedHarmOscillatorModelEditor, + ExponentialGaussianModelEditor, + SkewedGaussianModelEditor, + SkewedVoigtModelEditor, + ThermalDistributionModelEditor, + DoniachModelEditor, + ConstantModelEditor, + LinearModelEditor, + QuadraticModelEditor, + PolynomialModelEditor, + set_default_vary, +) +from orangecontrib.spectroscopy.widgets.peakfit_compute import ( + n_best_fit_parameters, + best_fit_results, + pool_initializer, + pool_fit, + pool_fit2, +) # number of processes used for computation # Use 2 or less unless overridden by QUASAR_N_PROCESSES env_proc = os.getenv('QUASAR_N_PROCESSES') -N_PROCESSES = None if env_proc == "all" else int(env_proc) if env_proc else min(2, multiprocessing.cpu_count()) +N_PROCESSES = ( + None + if env_proc == "all" + else int(env_proc) + if env_proc + else min(2, multiprocessing.cpu_count()) +) def fit_results_table(output, model_result, orig_data): @@ -55,9 +88,7 @@ def fit_results_table(output, model_result, orig_data): features.append(ContinuousVariable(name=param.replace("_", " "))) features.append(ContinuousVariable(name="Reduced chi-square")) - domain = Domain(features, - orig_data.domain.class_vars, - orig_data.domain.metas) + domain = Domain(features, orig_data.domain.class_vars, orig_data.domain.metas) out = orig_data.transform(domain) with out.unlocked_reference(out.X): out.X = output @@ -94,34 +125,37 @@ def pack_model_editor(editor): name=editor.name, qualname=f"orangecontrib.spectroscopy.widgets.peak_editors.{editor.prefix_generic}", category=editor.category, - description=Description(getattr(editor, 'description', editor.name), - icon_path(editor.icon)), + description=Description( + getattr(editor, 'description', editor.name), icon_path(editor.icon) + ), viewclass=editor, ) -PREPROCESSORS = [pack_model_editor(e) for e in [ - GaussianModelEditor, - LorentzianModelEditor, - SplitLorentzianModelEditor, - VoigtModelEditor, - PseudoVoigtModelEditor, - MoffatModelEditor, - Pearson7ModelEditor, - StudentsTModelEditor, - BreitWignerModelEditor, - LognormalModelEditor, - DampedOscillatorModelEditor, - DampedHarmOscillatorModelEditor, - ExponentialGaussianModelEditor, - SkewedGaussianModelEditor, - SkewedVoigtModelEditor, - ThermalDistributionModelEditor, - DoniachModelEditor, - ConstantModelEditor, - LinearModelEditor, - QuadraticModelEditor, - PolynomialModelEditor, +PREPROCESSORS = [ + pack_model_editor(e) + for e in [ + GaussianModelEditor, + LorentzianModelEditor, + SplitLorentzianModelEditor, + VoigtModelEditor, + PseudoVoigtModelEditor, + MoffatModelEditor, + Pearson7ModelEditor, + StudentsTModelEditor, + BreitWignerModelEditor, + LognormalModelEditor, + DampedOscillatorModelEditor, + DampedHarmOscillatorModelEditor, + ExponentialGaussianModelEditor, + SkewedGaussianModelEditor, + SkewedVoigtModelEditor, + ThermalDistributionModelEditor, + DoniachModelEditor, + ConstantModelEditor, + LinearModelEditor, + QuadraticModelEditor, + PolynomialModelEditor, ] ] @@ -169,7 +203,6 @@ def create_composite_model(m_def): class PeakPreviewRunner(PreviewRunner): - def __init__(self, master): super().__init__(master=master) self.pool = pebble.ProcessPool(max_workers=1) @@ -201,15 +234,17 @@ def on_done(self, result): self.preview_updated.emit() def show_preview(self, show_info_anyway=False): - """ Shows preview and also passes preview data to the widgets """ + """Shows preview and also passes preview data to the widgets""" master = self.master self.preview_pos = master.flow_view.preview_n() self.last_partial = None self.show_info_anyway = show_info_anyway self.preview_data = None self.after_data = None - pp_def = [master.preprocessormodel.item(i) - for i in range(master.preprocessormodel.rowCount())] + pp_def = [ + master.preprocessormodel.item(i) + for i in range(master.preprocessormodel.rowCount()) + ] if master.data is not None: # Clear markings to indicate preview is running refresh_integral_markings([], master.markings_list, master.curveplot) @@ -228,9 +263,7 @@ def shutdown(self): self.pool.join() @staticmethod - def run_preview(data: Table, - m_def, pool, state: TaskState): - + def run_preview(data: Table, m_def, pool, state: TaskState): def progress_interrupt(_: float): if state.is_interruption_requested(): raise InterruptException @@ -247,11 +280,9 @@ def progress_interrupt(_: float): model, parameters = create_composite_model(m_def) - model_result = {} x = getx(data) if data is not None and model is not None: - for row in data: progress_interrupt(0) res = pool.schedule(pool_fit2, (row.x, model.dumps(), parameters, x)) @@ -260,7 +291,10 @@ def progress_interrupt(_: float): progress_interrupt(0) except InterruptException: # CANCEL - if multiprocessing.get_start_method() != "fork" and res.running(): + if ( + multiprocessing.get_start_method() != "fork" + and res.running() + ): # If slower start methods are used, give the current computation # some time to exit gracefully; this avoids reloading processes concurrent.futures.wait([res], 1.0) @@ -296,10 +330,9 @@ class Outputs: class Warning(SpectralPreprocess.Warning): subset_not_subset = Msg( - "Subset data contains some instances that do not appear in " - "input data") - subset_independent = Msg( - "No subset data instances appear in input data") + "Subset data contains some instances that do not appear in input data" + ) + subset_independent = Msg("No subset data instances appear in input data") preview_on_image = True @@ -354,13 +387,16 @@ def _handle_subset_data(self): def get_subset_mask(self): if not self.subset_indices: return None - return np.fromiter((ex.id in self.subset_indices for ex in self.data), - dtype=bool, count=len(self.data)) + return np.fromiter( + (ex.id in self.subset_indices for ex in self.data), + dtype=bool, + count=len(self.data), + ) def sample_data(self, data): subset = self.get_subset_mask() if subset is not None: - return data[subset][:self.preview_curves] + return data[subset][: self.preview_curves] return super().sample_data(data) def redraw_integral(self): @@ -379,8 +415,11 @@ def redraw_integral(self): color = self.flow_view.preview_color(i) dis.append({"draw": di, "color": color}) result = None - if np.any(self.curveplot.selection_group) and self.curveplot.data \ - and self.preview_runner.preview_model_result: + if ( + np.any(self.curveplot.selection_group) + and self.curveplot.data + and self.preview_runner.preview_model_result + ): # select result ind = np.flatnonzero(self.curveplot.selection_group)[0] row_id = self.curveplot.data[ind].id @@ -405,12 +444,14 @@ def redraw_integral(self): refresh_integral_markings(dis, self.markings_list, self.curveplot) def create_outputs(self): - m_def = [self.preprocessormodel.item(i) for i in range(self.preprocessormodel.rowCount())] + m_def = [ + self.preprocessormodel.item(i) + for i in range(self.preprocessormodel.rowCount()) + ] self.start(self.run_task, self.data, m_def) @staticmethod def run_task(data: Table, m_def, state: TaskState): - def progress_interrupt(i: float): state.set_progress_value(i) if state.is_interruption_requested(): @@ -435,9 +476,11 @@ def progress_interrupt(i: float): fits = [] residuals = [] - with multiprocessing.Pool(processes=N_PROCESSES, - initializer=pool_initializer, - initargs=(model.dumps(), parameters, x)) as p: + with multiprocessing.Pool( + processes=N_PROCESSES, + initializer=pool_initializer, + initargs=(model.dumps(), parameters, x), + ) as p: res = p.map_async(pool_fit, data.X, chunksize=1) def done(): @@ -467,13 +510,14 @@ def done(): data_resid = orig_data.from_table_rows(orig_data, ...) # a shallow copy with data_resid.unlocked_reference(data_resid.X): data_resid.X = np.vstack(residuals) - dom_anno = Domain(orig_data.domain.attributes, - orig_data.domain.class_vars, - orig_data.domain.metas + data.domain.attributes, - ) + dom_anno = Domain( + orig_data.domain.attributes, + orig_data.domain.class_vars, + orig_data.domain.metas + data.domain.attributes, + ) data_anno = orig_data.transform(dom_anno) with data_anno.unlocked(data_anno.metas): - data_anno.metas[:, len(orig_data.domain.metas):] = data.X + data_anno.metas[:, len(orig_data.domain.metas) :] = data.X progress_interrupt(100) diff --git a/orangecontrib/spectroscopy/widgets/owpls.py b/orangecontrib/spectroscopy/widgets/owpls.py index 64cd0fb82..76081ff49 100644 --- a/orangecontrib/spectroscopy/widgets/owpls.py +++ b/orangecontrib/spectroscopy/widgets/owpls.py @@ -3,7 +3,7 @@ from Orange.widgets import gui from Orange.widgets.widget import Msg -from Orange.data import Table, Domain, ContinuousVariable, StringVariable +from Orange.data import Table from Orange.widgets.settings import Setting from Orange.widgets.utils.owlearnerwidget import OWBaseLearner from Orange.widgets.utils.signals import Output @@ -14,7 +14,9 @@ class OWPLS(OWBaseLearner): name = 'PLS' - description = "Partial Least Squares Regression widget for multivariate data analysis" + description = ( + "Partial Least Squares Regression widget for multivariate data analysis" + ) icon = "icons/PLS.svg" keywords = ["partial least squares"] @@ -27,8 +29,10 @@ class Outputs(OWBaseLearner.Outputs): class Warning(OWBaseLearner.Warning): sparse_data = Msg('Sparse input data: default preprocessing is to scale it.') - deprecated = Msg('The PLS widget is deprecated and will be removed in the future.\n' - 'Please use the PLS widget from the Model category instead.') + deprecated = Msg( + 'The PLS widget is deprecated and will be removed in the future.\n' + 'Please use the PLS widget from the Model category instead.' + ) n_components = Setting(2) max_iter = Setting(500) @@ -36,19 +40,32 @@ class Warning(OWBaseLearner.Warning): def add_main_layout(self): self.Warning.deprecated() - self.optimization_box = gui.vBox( - self.controlArea, "Optimization Parameters") + self.optimization_box = gui.vBox(self.controlArea, "Optimization Parameters") self.ncomps_spin = gui.spin( - self.optimization_box, self, "n_components", 1, 50, 1, + self.optimization_box, + self, + "n_components", + 1, + 50, + 1, label="Components: ", - alignment=Qt.AlignRight, controlWidth=100, - callback=self.settings_changed) + alignment=Qt.AlignRight, + controlWidth=100, + callback=self.settings_changed, + ) self.n_iters = gui.spin( - self.optimization_box, self, "max_iter", 5, 1000000, 50, + self.optimization_box, + self, + "max_iter", + 5, + 1000000, + 50, label="Iteration limit: ", - alignment=Qt.AlignRight, controlWidth=100, + alignment=Qt.AlignRight, + controlWidth=100, callback=self.settings_changed, - checkCallback=self.settings_changed) + checkCallback=self.settings_changed, + ) def update_model(self): super().update_model() @@ -73,10 +90,15 @@ def set_data(self, data): self.Error.data_error.clear() self.data = data - if data is not None and data.domain.class_var is None and not data.domain.class_vars: + if ( + data is not None + and data.domain.class_var is None + and not data.domain.class_vars + ): self.Error.data_error( "Data has no target variable.\n" - "Select one with the Select Columns widget.") + "Select one with the Select Columns widget." + ) self.data = None # invalidate the model so that handleNewSignals will update it @@ -87,9 +109,9 @@ def set_data(self, data): def create_learner(self): common_args = {'preprocessors': self.preprocessors} - return PLSRegressionLearner(n_components=self.n_components, - max_iter=self.max_iter, - **common_args) + return PLSRegressionLearner( + n_components=self.n_components, max_iter=self.max_iter, **common_args + ) if __name__ == "__main__": # pragma: no cover diff --git a/orangecontrib/spectroscopy/widgets/owpolar.py b/orangecontrib/spectroscopy/widgets/owpolar.py index 0dc945ecf..c4cdb94f6 100644 --- a/orangecontrib/spectroscopy/widgets/owpolar.py +++ b/orangecontrib/spectroscopy/widgets/owpolar.py @@ -20,16 +20,15 @@ from Orange.widgets.widget import OWWidget, Msg, Output, MultiInput from Orange.widgets import gui, settings -from Orange.widgets.settings import \ - Setting, ContextSetting, DomainContextHandler +from Orange.widgets.settings import Setting, ContextSetting, DomainContextHandler from Orange.widgets.utils.itemmodels import DomainModel from Orange.widgets.utils.concurrent import TaskState, ConcurrentWidgetMixin from Orange.widgets.data import owconcatenate from Orange.widgets.data.oweditdomain import disconnected from orangewidget.utils.listview import ListViewSearch -class DiscDomainModel(DomainModel): +class DiscDomainModel(DomainModel): def data(self, index, role=Qt.DisplayRole): value = super().data(index, role) if role == Qt.DisplayRole: @@ -41,12 +40,12 @@ def data(self, index, role=Qt.DisplayRole): if type(value) is str: value += f" ({values})" return value - + + def _restore_selected_items(model, view, setting, connector): selection = QItemSelection() sel_model: QItemSelectionModel = view.selectionModel() - with disconnected(sel_model.selectionChanged, - connector): + with disconnected(sel_model.selectionChanged, connector): valid = [] model_values = model[:] for var in setting: @@ -62,6 +61,7 @@ class Results(SimpleNamespace): model = None errorstate = 0 + def sort_domain(domain): dom = [domain.metas, domain.attributes, domain.class_vars] sorted_dom_lst = [] @@ -79,15 +79,18 @@ def sort_domain(domain): cvs.pop(j) cvs_arr = np.array(cvs) if cvs_arr.shape[0] > 0: - cvs_arr_sorted = cvs_arr[cvs_arr[:,2].argsort()] + cvs_arr_sorted = cvs_arr[cvs_arr[:, 2].argsort()] odom_cv = [i[1] for i in cvs_arr_sorted] odom = rcvs + odom_cv else: odom = rcvs sorted_dom_lst.append(tuple(odom)) - out = Domain(sorted_dom_lst[1], class_vars=sorted_dom_lst[2], metas=sorted_dom_lst[0]) + out = Domain( + sorted_dom_lst[1], class_vars=sorted_dom_lst[2], metas=sorted_dom_lst[0] + ) return out + def combine_visimg(data, polangles): atts = [] for k, i in enumerate(data): @@ -103,28 +106,48 @@ def combine_visimg(data, polangles): attsdict = {'visible_images': atts} return attsdict -def run(data, feature, alphas, map_x, map_y, invert_angles, polangles, average, - sep, state: TaskState): +def run( + data, + feature, + alphas, + map_x, + map_y, + invert_angles, + polangles, + average, + sep, + state: TaskState, +): results = Results() - output, model, spectra, origmetas, errorstate = process_polar_abs(data, alphas, feature, map_x, - map_y, invert_angles, polangles, average, state) - - - tempoutaddmetas = [[ContinuousVariable.make('Azimuth Angle (' + i.name + ')'), - ContinuousVariable.make('Hermans Orientation Function (' + i.name + ')'), - ContinuousVariable.make('Intensity (' + i.name + ')'), - ContinuousVariable.make('Amplitude (' + i.name + ')'), - ContinuousVariable.make('R-squared (' + i.name + ')')] for i in feature] + output, model, spectra, origmetas, errorstate = process_polar_abs( + data, alphas, feature, map_x, map_y, invert_angles, polangles, average, state + ) + + tempoutaddmetas = [ + [ + ContinuousVariable.make('Azimuth Angle (' + i.name + ')'), + ContinuousVariable.make('Hermans Orientation Function (' + i.name + ')'), + ContinuousVariable.make('Intensity (' + i.name + ')'), + ContinuousVariable.make('Amplitude (' + i.name + ')'), + ContinuousVariable.make('R-squared (' + i.name + ')'), + ] + for i in feature + ] outaddmetas = [] for i in tempoutaddmetas: outaddmetas = outaddmetas + i - tempmodaddmetas = [[ContinuousVariable.make('R-squared (' + i.name + ')'), - ContinuousVariable.make('a0 (' + i.name + ')'), - ContinuousVariable.make('a1 (' + i.name + ')'), - ContinuousVariable.make('a2 (' + i.name + ')')] for i in feature] + tempmodaddmetas = [ + [ + ContinuousVariable.make('R-squared (' + i.name + ')'), + ContinuousVariable.make('a0 (' + i.name + ')'), + ContinuousVariable.make('a1 (' + i.name + ')'), + ContinuousVariable.make('a2 (' + i.name + ')'), + ] + for i in feature + ] modaddmetas = [] for i in tempmodaddmetas: modaddmetas = modaddmetas + i @@ -132,8 +155,8 @@ def run(data, feature, alphas, map_x, map_y, invert_angles, polangles, average, if average is False: values = tuple(f'{i} Degrees' for i in polangles) PolAng = DiscreteVariable.make('Polarization Angle', values=values) - outmetadom = (ometadom + tuple([PolAng]) + tuple(outaddmetas)) - modmetadom = (ometadom + tuple([PolAng]) + tuple(modaddmetas)) + outmetadom = ometadom + tuple([PolAng]) + tuple(outaddmetas) + modmetadom = ometadom + tuple([PolAng]) + tuple(modaddmetas) output_stack = tuple(output for i in polangles) model_stack = tuple(model for i in polangles) output = np.vstack(output_stack) @@ -142,20 +165,20 @@ def run(data, feature, alphas, map_x, map_y, invert_angles, polangles, average, if sep is not None: sep_idx = ometadom.index(sep) try: - origmetas = np.c_[origmetas[:,0:sep_idx], origmetas[:,sep_idx+1:]] + origmetas = np.c_[origmetas[:, 0:sep_idx], origmetas[:, sep_idx + 1 :]] except ValueError: - origmetas = np.r_[origmetas[:,0:sep_idx]] + origmetas = np.r_[origmetas[:, 0:sep_idx]] ometadom = tuple(i for i in ometadom if i is not sep) - outmetadom = (ometadom + tuple(outaddmetas)) - modmetadom = (ometadom + tuple(modaddmetas)) + outmetadom = ometadom + tuple(outaddmetas) + modmetadom = ometadom + tuple(modaddmetas) if state.is_interruption_requested(): results = None return results ofeatdom = data[0].domain.attributes - datadomain = Domain(ofeatdom, metas = outmetadom) - moddomain = Domain(ofeatdom, metas = modmetadom) + datadomain = Domain(ofeatdom, metas=outmetadom) + moddomain = Domain(ofeatdom, metas=modmetadom) outmetas = np.hstack((origmetas, output)) modmetas = np.hstack((origmetas, model)) @@ -176,40 +199,49 @@ def run(data, feature, alphas, map_x, map_y, invert_angles, polangles, average, results.model.attributes = attsdict return results -#Calculate by fitting to function -def azimuth(x,a0,a1,a2): - t = 2*np.radians(x) - return a0*np.sin(t)+a1*np.cos(t)+a2 + +# Calculate by fitting to function +def azimuth(x, a0, a1, a2): + t = 2 * np.radians(x) + return a0 * np.sin(t) + a1 * np.cos(t) + a2 + def azimuth_jac(x, a0, a1, a2): - t = 2*np.radians(x).reshape(-1, 1) + t = 2 * np.radians(x).reshape(-1, 1) da0 = np.sin(t) da1 = np.cos(t) da2 = np.ones(t.shape) return np.hstack((da0, da1, da2)) -def calc_angles(a0,a1): - return np.degrees(0.5*np.arctan(a0/a1)) -def ampl2(a0,a1): - return (2*(math.sqrt(a0**2+a1**2))) +def calc_angles(a0, a1): + return np.degrees(0.5 * np.arctan(a0 / a1)) + + +def ampl2(a0, a1): + return 2 * (math.sqrt(a0**2 + a1**2)) + -def orfunc(alpha,a0,a1,a2): +def orfunc(alpha, a0, a1, a2): if alpha < 54.73: - Dmax = (2*a2+2*math.sqrt(a0**2+a1**2))/(2*a2-2*math.sqrt(a0**2+a1**2)) - return ((Dmax-1)/(Dmax+2)*(2/(3*np.cos(np.radians(alpha))**2-1))) + Dmax = (2 * a2 + 2 * math.sqrt(a0**2 + a1**2)) / ( + 2 * a2 - 2 * math.sqrt(a0**2 + a1**2) + ) + return (Dmax - 1) / (Dmax + 2) * (2 / (3 * np.cos(np.radians(alpha)) ** 2 - 1)) elif alpha >= 54.73: - Dmin = (2*a2-2*math.sqrt(a0**2+a1**2))/(2*a2+2*math.sqrt(a0**2+a1**2)) - return ((Dmin-1)/(Dmin+2)*(2/(3*np.cos(np.radians(alpha))**2-1))) + Dmin = (2 * a2 - 2 * math.sqrt(a0**2 + a1**2)) / ( + 2 * a2 + 2 * math.sqrt(a0**2 + a1**2) + ) + return (Dmin - 1) / (Dmin + 2) * (2 / (3 * np.cos(np.radians(alpha)) ** 2 - 1)) return None + def find_az(params): - Az0 = calc_angles(params[0],params[1]) + Az0 = calc_angles(params[0], params[1]) Abs0 = azimuth(Az0, *params) - Az1 = calc_angles(params[0],params[1])+90 + Az1 = calc_angles(params[0], params[1]) + 90 Abs1 = azimuth(Az1, *params) - Az2 = calc_angles(params[0],params[1])-90 - + Az2 = calc_angles(params[0], params[1]) - 90 if Abs0 > Abs1: Az = Az0 @@ -220,6 +252,7 @@ def find_az(params): Az = Az2 return Az + def compute(xys, yidx, smms, shapes, dtypes, polangles): tcvs = smms[0] cvs = np.ndarray(shapes[0], dtype=dtypes[0], buffer=tcvs.buf) @@ -234,39 +267,39 @@ def compute(xys, yidx, smms, shapes, dtypes, polangles): x = np.asarray(polangles) - for i in range(yidx[0], yidx[1]):#y-values(rows) + for i in range(yidx[0], yidx[1]): # y-values(rows) if vars[-1] != 0: break - for j in enumerate(xys[0]):#x-values(cols) + for j in enumerate(xys[0]): # x-values(cols) if vars[-1] != 0: break for l in range(cvs.shape[2]): - if np.any(np.isnan(cvs[i,j[0],l,:]), axis=0): + if np.any(np.isnan(cvs[i, j[0], l, :]), axis=0): continue - out[i,j[0],l,0] = coords[i,j[0],1]#x-map - mod[i,j[0],l,0] = coords[i,j[0],1] - out[i,j[0],l,1] = coords[i,j[0],0]#y-map - mod[i,j[0],l,1] = coords[i,j[0],0] + out[i, j[0], l, 0] = coords[i, j[0], 1] # x-map + mod[i, j[0], l, 0] = coords[i, j[0], 1] + out[i, j[0], l, 1] = coords[i, j[0], 0] # y-map + mod[i, j[0], l, 1] = coords[i, j[0], 0] - temp = list(cvs[i,j[0],l,:]) + temp = list(cvs[i, j[0], l, :]) params = curve_fit(azimuth, x, temp, jac=azimuth_jac)[0] residuals = temp - azimuth(x, *params) ss_res = np.sum(residuals**2) - ss_tot = np.sum((temp-np.mean(temp))**2) + ss_tot = np.sum((temp - np.mean(temp)) ** 2) if ss_tot == 0: vars[-1] = 1 break - out[i,j[0],l,6] = 1-(ss_res/ss_tot) - mod[i,j[0],l,2] = 1-(ss_res/ss_tot) - out[i,j[0],l,2] = find_az(params) - out[i,j[0],l,3] = orfunc(vars[l], *params) - out[i,j[0],l,4] = params[2] - out[i,j[0],l,5] = ampl2(params[0],params[1]) - mod[i,j[0],l,3] = params[0] - mod[i,j[0],l,4] = params[1] - mod[i,j[0],l,5] = params[2] + out[i, j[0], l, 6] = 1 - (ss_res / ss_tot) + mod[i, j[0], l, 2] = 1 - (ss_res / ss_tot) + out[i, j[0], l, 2] = find_az(params) + out[i, j[0], l, 3] = orfunc(vars[l], *params) + out[i, j[0], l, 4] = params[2] + out[i, j[0], l, 5] = ampl2(params[0], params[1]) + mod[i, j[0], l, 3] = params[0] + mod[i, j[0], l, 4] = params[1] + mod[i, j[0], l, 5] = params[2] def unique_xys(images, map_x, map_y): @@ -274,8 +307,8 @@ def unique_xys(images, map_x, map_y): lsys = np.empty(0) for i in enumerate(images): tempdata = i[1].transform(Domain([map_x, map_y])) - lsx = np.unique(tempdata.X[:,0]) - lsy = np.unique(tempdata.X[:,1]) + lsx = np.unique(tempdata.X[:, 0]) + lsy = np.unique(tempdata.X[:, 1]) lsxs = np.append(lsxs, lsx) lsys = np.append(lsys, lsy) @@ -283,24 +316,32 @@ def unique_xys(images, map_x, map_y): ulsys = np.unique(lsys) return ulsxs, ulsys + def start_compute(ulsxs, ulsys, names, shapes, dtypes, polangles, state): # single core processing is faster for small data sets and small number of selected features # if > x: # ncpu = os.cpu_count() # ncpu = 6 env_proc = os.getenv('QUASAR_N_PROCESSES') - ncpu = os.cpu_count() if env_proc == "all" else int(env_proc) if env_proc else min(2, os.cpu_count()) + ncpu = ( + os.cpu_count() + if env_proc == "all" + else int(env_proc) + if env_proc + else min(2, os.cpu_count()) + ) tulsys = np.array_split(ulsys, ncpu) state.set_status("Calculating...") - threads=[] + threads = [] cumu = 0 for i in range(ncpu): - tlsxys = [ulsxs,tulsys[i]] - yidx = [cumu, cumu+len(tulsys[i])] + tlsxys = [ulsxs, tulsys[i]] + yidx = [cumu, cumu + len(tulsys[i])] cumu += len(tulsys[i]) # compute(tlsxys, yidx, shapes, dtypes, polangles, i) - t = multiprocessing.Process(target=compute, - args=(tlsxys, yidx, names, shapes, dtypes, polangles)) + t = multiprocessing.Process( + target=compute, args=(tlsxys, yidx, names, shapes, dtypes, polangles) + ) threads.append(t) t.start() @@ -308,44 +349,55 @@ def start_compute(ulsxs, ulsys, names, shapes, dtypes, polangles, state): # t.join() # else: - # ncpu = 1 - # tulsys = np.array_split(ulsys, ncpu) - # state.set_status("Calculating...") - # threads=[] - # cumu = 0 - # for i in range(ncpu): - # tlsxys = [ulsxs,tulsys[i]] - # yidx = [cumu, cumu+len(tulsys[i])] - # cumu += len(tulsys[i]) - # compute(tlsxys, yidx, shapes, dtypes, polangles, i) + # ncpu = 1 + # tulsys = np.array_split(ulsys, ncpu) + # state.set_status("Calculating...") + # threads=[] + # cumu = 0 + # for i in range(ncpu): + # tlsxys = [ulsxs,tulsys[i]] + # yidx = [cumu, cumu+len(tulsys[i])] + # cumu += len(tulsys[i]) + # compute(tlsxys, yidx, shapes, dtypes, polangles, i) return threads -def process_polar_abs(images, alphas, feature, map_x, map_y, invert, polangles, average, state): + +def process_polar_abs( + images, alphas, feature, map_x, map_y, invert, polangles, average, state +): state.set_status("Preparing...") ulsxs, ulsys = unique_xys(images, map_x, map_y) if len(ulsxs) > 1: - dx = np.sum(np.diff(ulsxs))/(len(ulsxs)-1) + dx = np.sum(np.diff(ulsxs)) / (len(ulsxs) - 1) else: dx = 1 if len(ulsys) > 1: - dy = np.sum(np.diff(ulsys))/(len(ulsys)-1) + dy = np.sum(np.diff(ulsys)) / (len(ulsys) - 1) else: dy = 1 minx = np.min(ulsxs) miny = np.min(ulsys) featnames = [i.name for i in feature] - cvs = np.full((np.shape(ulsys)[0], np.shape(ulsxs)[0], len(featnames), len(images)), np.nan) - spec = np.full((np.shape(ulsys)[0], np.shape(ulsxs)[0], - images[0].X.shape[1], len(images)), np.nan, dtype=object) - metas = np.full((np.shape(ulsys)[0], np.shape(ulsxs)[0], - images[0].metas.shape[1], len(images)), np.nan, dtype=object) + cvs = np.full( + (np.shape(ulsys)[0], np.shape(ulsxs)[0], len(featnames), len(images)), np.nan + ) + spec = np.full( + (np.shape(ulsys)[0], np.shape(ulsxs)[0], images[0].X.shape[1], len(images)), + np.nan, + dtype=object, + ) + metas = np.full( + (np.shape(ulsys)[0], np.shape(ulsxs)[0], images[0].metas.shape[1], len(images)), + np.nan, + dtype=object, + ) out = np.full((np.shape(ulsys)[0], np.shape(ulsxs)[0], len(featnames), 7), np.nan) mod = np.full((np.shape(ulsys)[0], np.shape(ulsxs)[0], len(featnames), 6), np.nan) coords = np.full((np.shape(ulsys)[0], np.shape(ulsxs)[0], 2), np.nan) - vars = np.hstack((np.asarray(alphas),0)) + vars = np.hstack((np.asarray(alphas), 0)) fill = np.full((np.shape(ulsys)[0], np.shape(ulsxs)[0]), np.nan) for i, j in enumerate(images): @@ -354,18 +406,18 @@ def process_polar_abs(images, alphas, feature, map_x, map_y, invert, polangles, cv = [j.domain[k] for k in featnames] doms = [map_x, map_y] + cv tempdata: Table = j.transform(Domain(doms)) - temp_xy = tempdata.X[:,0:2].copy() - temp_xy[:,0] = np.rint(((temp_xy[:,0]-minx)/dx)) - temp_xy[:,1] = np.rint(((temp_xy[:,1]-miny)/dy)) + temp_xy = tempdata.X[:, 0:2].copy() + temp_xy[:, 0] = np.rint(((temp_xy[:, 0] - minx) / dx)) + temp_xy[:, 1] = np.rint(((temp_xy[:, 1] - miny) / dy)) temp_xy = np.array(temp_xy, dtype=np.int_) - cvs[temp_xy[:,1],temp_xy[:,0],:,i] = tempdata[:,2:] - spec[temp_xy[:,1],temp_xy[:,0],:,i] = j.X - metas[temp_xy[:,1],temp_xy[:,0],:,i] = j.metas + cvs[temp_xy[:, 1], temp_xy[:, 0], :, i] = tempdata[:, 2:] + spec[temp_xy[:, 1], temp_xy[:, 0], :, i] = j.X + metas[temp_xy[:, 1], temp_xy[:, 0], :, i] = j.metas xys = pd.DataFrame(fill, index=ulsys, columns=ulsxs, dtype=object) for k, i in enumerate(xys.index): for l, j in enumerate(xys.columns): - coords[k,l,0] = i - coords[k,l,1] = j + coords[k, l, 0] = i + coords[k, l, 1] = j if state.is_interruption_requested(): return None, None, None, None, 2 @@ -373,23 +425,39 @@ def process_polar_abs(images, alphas, feature, map_x, map_y, invert, polangles, with SharedMemoryManager() as smm: tcvs = smm.SharedMemory(size=cvs.nbytes) scvs = np.ndarray(cvs.shape, dtype=cvs.dtype, buffer=tcvs.buf) - scvs[:,:,:] = cvs[:,:,:] + scvs[:, :, :] = cvs[:, :, :] tout = smm.SharedMemory(size=out.nbytes) sout = np.ndarray(out.shape, dtype=out.dtype, buffer=tout.buf) - sout[:,:,:,:] = out[:,:,:,:] + sout[:, :, :, :] = out[:, :, :, :] tmod = smm.SharedMemory(size=mod.nbytes) smod = np.ndarray(mod.shape, dtype=mod.dtype, buffer=tmod.buf) - smod[:,:,:,:] = mod[:,:,:,:] + smod[:, :, :, :] = mod[:, :, :, :] tcoords = smm.SharedMemory(size=coords.nbytes) scoords = np.ndarray(coords.shape, dtype=coords.dtype, buffer=tcoords.buf) - scoords[:,:,:] = coords[:,:,:] + scoords[:, :, :] = coords[:, :, :] tvars = smm.SharedMemory(size=vars.nbytes) svars = np.ndarray(vars.shape, dtype=vars.dtype, buffer=tvars.buf) svars[:] = vars[:] smms = [tcvs, None, None, tout, tmod, tcoords, tvars] - shapes = [cvs.shape, spec.shape, metas.shape, out.shape, mod.shape, coords.shape, vars.shape] - dtypes = [cvs.dtype, spec.dtype, metas.dtype, out.dtype, mod.dtype, coords.dtype, vars.dtype] + shapes = [ + cvs.shape, + spec.shape, + metas.shape, + out.shape, + mod.shape, + coords.shape, + vars.shape, + ] + dtypes = [ + cvs.dtype, + spec.dtype, + metas.dtype, + out.dtype, + mod.dtype, + coords.dtype, + vars.dtype, + ] if state.is_interruption_requested(): return None, None, None, None, 2 @@ -398,16 +466,22 @@ def process_polar_abs(images, alphas, feature, map_x, map_y, invert, polangles, for t in threads: t.join(0) - while any([i.exitcode == None for i in threads]): + while any([i.exitcode is None for i in threads]): if state.is_interruption_requested(): svars[1] = 2 time.sleep(0.10) state.set_status("Finishing...") if invert is True: - sout[:,:,:,2] = sout[:,:,:,2]*-1 - outputs = np.reshape(sout[:,:,:,2:], (np.shape(ulsys)[0]*np.shape(ulsxs)[0], 5*len(featnames))) - model = np.reshape(smod[:,:,:,2:], (np.shape(ulsys)[0]*np.shape(ulsxs)[0], 4*len(featnames))) + sout[:, :, :, 2] = sout[:, :, :, 2] * -1 + outputs = np.reshape( + sout[:, :, :, 2:], + (np.shape(ulsys)[0] * np.shape(ulsxs)[0], 5 * len(featnames)), + ) + model = np.reshape( + smod[:, :, :, 2:], + (np.shape(ulsys)[0] * np.shape(ulsxs)[0], 4 * len(featnames)), + ) vars[:] = svars[:] if state.is_interruption_requested(): @@ -417,14 +491,20 @@ def process_polar_abs(images, alphas, feature, map_x, map_y, invert, polangles, met = [] if average is False: for i in range(len(polangles)): - spectratemp = np.reshape(spec[:,:,:,i], - (np.shape(ulsys)[0]*np.shape(ulsxs)[0], images[0].X.shape[1])) + spectratemp = np.reshape( + spec[:, :, :, i], + (np.shape(ulsys)[0] * np.shape(ulsxs)[0], images[0].X.shape[1]), + ) spectratemp = spectratemp[~np.isnan(model).any(axis=1)] spectra.append(spectratemp) - metatemp = np.reshape(metas[:,:,:,i], - (np.shape(ulsys)[0]*np.shape(ulsxs)[0], images[0].metas.shape[1])) + metatemp = np.reshape( + metas[:, :, :, i], + (np.shape(ulsys)[0] * np.shape(ulsxs)[0], images[0].metas.shape[1]), + ) metatemp = metatemp[~np.isnan(model).any(axis=1)] - metatemp = np.append(metatemp, np.full((np.shape(metatemp)[0],1), i), axis=1) + metatemp = np.append( + metatemp, np.full((np.shape(metatemp)[0], 1), i), axis=1 + ) met.append(metatemp) if state.is_interruption_requested(): @@ -432,12 +512,16 @@ def process_polar_abs(images, alphas, feature, map_x, map_y, invert, polangles, elif average is True: average_spec = np.average(spec, axis=3) - spectratemp = np.reshape(average_spec, - (np.shape(ulsys)[0]*np.shape(ulsxs)[0], images[0].X.shape[1])) + spectratemp = np.reshape( + average_spec, + (np.shape(ulsys)[0] * np.shape(ulsxs)[0], images[0].X.shape[1]), + ) spectratemp = spectratemp[~np.isnan(model).any(axis=1)] spectra.append(spectratemp) - metatemp = np.reshape(metas[:,:,:,0], - (np.shape(ulsys)[0]*np.shape(ulsxs)[0], images[0].metas.shape[1])) + metatemp = np.reshape( + metas[:, :, :, 0], + (np.shape(ulsys)[0] * np.shape(ulsxs)[0], images[0].metas.shape[1]), + ) metatemp = metatemp[~np.isnan(model).any(axis=1)] met.append(metatemp) @@ -451,14 +535,14 @@ def process_polar_abs(images, alphas, feature, map_x, map_y, invert, polangles, class OWPolar(OWWidget, ConcurrentWidgetMixin): - # Widget's name as displayed in the canvas name = "4+ Angle Polarization" # Short widget description description = ( "Calculate Azimuth Angle, Orientation function, Amplitude and Intensity of " - "vibrational mode(s) using polarized data measured at 4 or more polarization angles.") + "vibrational mode(s) using polarized data measured at 4 or more polarization angles." + ) icon = "icons/polar.svg" @@ -503,7 +587,9 @@ class Warning(OWWidget.Warning): XYfeat = Msg("Selected feature(s) cannot be the same as XY selection") class Information(OWWidget.Information): - meta_calc = Msg("Meta and Target variables are not transformed to absorptance during calculations") + meta_calc = Msg( + "Meta and Target variables are not transformed to absorptance during calculations" + ) def __init__(self): super().__init__() @@ -526,7 +612,7 @@ def __init__(self): self._data_inputs: List[Optional[Table]] = [] hbox = gui.hBox(self.controlArea) - #col 1 + # col 1 vbox2 = gui.vBox(hbox, None) @@ -534,106 +620,163 @@ def __init__(self): formlayout2 = QFormLayout() form2.setLayout(formlayout2) - self.multifile = gui.widgetBox(vbox2, "Single input (with all angles)", - sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed)) + self.multifile = gui.widgetBox( + vbox2, + "Single input (with all angles)", + sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed), + ) self.anglemetas = DomainModel(DomainModel.METAS, valid_types=DiscreteVariable) - self.anglesel = gui.comboBox(self.multifile, self, 'angles', searchable=True, - label='Select Angles by:', callback=self._change_angles, - model=self.anglemetas) + self.anglesel = gui.comboBox( + self.multifile, + self, + 'angles', + searchable=True, + label='Select Angles by:', + callback=self._change_angles, + model=self.anglemetas, + ) self.anglesel.setDisabled(True) - - self.multiin = gui.widgetBox(vbox2, "Multiple inputs (single angle per input)", - sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed)) + self.multiin = gui.widgetBox( + vbox2, + "Multiple inputs (single angle per input)", + sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed), + ) vbox2.layout().addWidget(form2) - #col 2 + # col 2 vbox1 = gui.vBox(hbox, "Features") - self.featureselect = DiscDomainModel(DomainModel.SEPARATED, - valid_types=ContinuousVariable) + self.featureselect = DiscDomainModel( + DomainModel.SEPARATED, valid_types=ContinuousVariable + ) self.feat_view = ListViewSearch(selectionMode=QListView.ExtendedSelection) self.feat_view.setModel(self.featureselect) self.feat_view.selectionModel().selectionChanged.connect(self._feat_changed) vbox1.layout().addWidget(self.feat_view) vbox1.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum)) self.contextOpened.connect( - lambda: _restore_selected_items(model=self.featureselect, - view=self.feat_view, - setting=self.feats, - connector=self._feat_changed)) - - gui.button(vbox1, self, "Don't use selected features", - callback=self.remove_feat) - - #col 3 + lambda: _restore_selected_items( + model=self.featureselect, + view=self.feat_view, + setting=self.feats, + connector=self._feat_changed, + ) + ) + + gui.button( + vbox1, self, "Don't use selected features", callback=self.remove_feat + ) + + # col 3 vbox = gui.vBox(hbox, None) form = QWidget() formlayout = QFormLayout() form.setLayout(formlayout) - xybox = gui.widgetBox(vbox, "Data XY Selection", - sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed)) + xybox = gui.widgetBox( + vbox, + "Data XY Selection", + sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed), + ) self.x_axis = DomainModel(DomainModel.METAS, valid_types=ContinuousVariable) self.y_axis = DomainModel(DomainModel.METAS, valid_types=ContinuousVariable) - gui.comboBox(xybox, self, 'map_x', searchable=True, label="X Axis", - callback=self._change_input, model=self.x_axis) - gui.comboBox(xybox, self, 'map_y', searchable=True, label="Y Axis", - callback=self._change_input, model=self.y_axis) + gui.comboBox( + xybox, + self, + 'map_x', + searchable=True, + label="X Axis", + callback=self._change_input, + model=self.x_axis, + ) + gui.comboBox( + xybox, + self, + 'map_y', + searchable=True, + label="Y Axis", + callback=self._change_input, + model=self.y_axis, + ) vbox.layout().addWidget(form) - specbox = gui.widgetBox(vbox, "Spectra Type", - sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed)) + specbox = gui.widgetBox( + vbox, "Spectra Type", sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed) + ) - self.spec_b1 = gui.radioButtons(specbox, self, 'spec_type', ['Absorptance', 'Absorbance', 'Transmittance'], - callback=self._process_spectype) + self.spec_b1 = gui.radioButtons( + specbox, + self, + 'spec_type', + ['Absorptance', 'Absorbance', 'Transmittance'], + callback=self._process_spectype, + ) vbox.layout().addWidget(form) - pbox = gui.widgetBox(vbox, "Parameters", sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed)) - self.alphaedit = gui.lineEdit(pbox, self, "alpha", u"TDM Tilt (\N{DEGREE SIGN})", - callback=self.change_alphas, valueType=float, - validator=QDoubleValidator(0.00, 90.00, 2), tooltip= \ - "The angle (in degrees) between the long axis of the molecule and the transition dipole moment") - - gui.checkBox(pbox, self, 'invert_angles', label="Invert Angles", - callback=self._change_input) - - gui.checkBox(pbox, self, 'average', label='Average Spectra', - callback=self._change_input) - - gui.auto_commit(self.controlArea, self, "autocommit", "Apply", commit=self.commit) + pbox = gui.widgetBox( + vbox, "Parameters", sizePolicy=(QSizePolicy.Minimum, QSizePolicy.Fixed) + ) + self.alphaedit = gui.lineEdit( + pbox, + self, + "alpha", + "TDM Tilt (\N{DEGREE SIGN})", + callback=self.change_alphas, + valueType=float, + validator=QDoubleValidator(0.00, 90.00, 2), + tooltip="The angle (in degrees) between the long axis of the molecule and the transition dipole moment", + ) + + gui.checkBox( + pbox, + self, + 'invert_angles', + label="Invert Angles", + callback=self._change_input, + ) + + gui.checkBox( + pbox, self, 'average', label='Average Spectra', callback=self._change_input + ) + + gui.auto_commit( + self.controlArea, self, "autocommit", "Apply", commit=self.commit + ) gui.rubber(vbox2) cbox = gui.widgetBox(vbox2, "Citations") citation = gui.widgetLabel(cbox) citation.setOpenExternalLinks(True) - citation.setText('\ + citation.setText( + '\ \ When publishing results, consider citing:
\ - Hikima et al. (2013)
\ - Gassner et al. (2025) \ - ') + ' + ) self._change_input() self.contextAboutToBeOpened.connect(lambda x: self.init_attr_values(x[0])) def sort_row(self, unsorted): - row, feats, alphas = list(zip(*[unsorted])) + row, feats, alphas = list(zip(*[unsorted])) # noqa: B905 return row def sort_feats(self): model = self.feat_view.model() rows = [model.indexOf(row) for row in self.feats] - featalphas = list(zip(rows, self.feats, self.alphas)) + featalphas = list(zip(rows, self.feats, self.alphas)) # noqa: B905 temp = sorted(featalphas, key=self.sort_row) - rows, feats, alphas = list(zip(*temp)) + rows, feats, alphas = list(zip(*temp)) # noqa: B905 self.feats = list(feats) self.alphas = list(alphas) @@ -645,9 +788,11 @@ def _feat_changed(self): if model[:][row.row()] not in self.feats: self.feats.append(model[:][row.row()]) self.alphas.append(self.alpha) - model.setData(model.index(row.row()), - f'TDM = {self.alpha}\N{DEGREE SIGN}', - role=Qt.UserRole) + model.setData( + model.index(row.row()), + f'TDM = {self.alpha}\N{DEGREE SIGN}', + role=Qt.UserRole, + ) self.sort_feats() if len(rows) > 0: self.Warning.nofeat.clear() @@ -676,7 +821,11 @@ def change_alphas(self): self._feat_changed() idx = self.feats.index(model[:][row.row()]) self.alphas[idx] = self.alpha - model.setData(model.index(row.row()), f"TDM = {self.alpha}\N{DEGREE SIGN}", role=Qt.UserRole) + model.setData( + model.index(row.row()), + f"TDM = {self.alpha}\N{DEGREE SIGN}", + role=Qt.UserRole, + ) self.commit.deferred() def restore_alphas(self): @@ -684,7 +833,11 @@ def restore_alphas(self): model = self.feat_view.model() for row in rows: idx = self.feats.index(model[:][row.row()]) - model.setData(model.index(row.row()), f"TDM = {self.alphas[idx]}\N{DEGREE SIGN}", role=Qt.UserRole) + model.setData( + model.index(row.row()), + f"TDM = {self.alphas[idx]}\N{DEGREE SIGN}", + role=Qt.UserRole, + ) def init_attr_values(self, data): domain = data.domain if data is not None else None @@ -694,8 +847,7 @@ def init_attr_values(self, data): self.anglemetas.set_domain(domain) self.angles = self.anglemetas[0] if self.anglemetas else None self.map_x = self.x_axis[0] if self.x_axis else None - self.map_y = self.y_axis[1] if len(self.y_axis) >= 2 \ - else self.map_x + self.map_y = self.y_axis[1] if len(self.y_axis) >= 2 else self.map_x self.restore_alphas() def _change_input(self): @@ -712,10 +864,18 @@ def _change_angles(self): if len(self.angles.values) < 4: self.Warning.notenough() else: - tempangles = np.linspace(0, 180, len(self.angles.values)+1) + tempangles = np.linspace(0, 180, len(self.angles.values) + 1) for i, j in enumerate(self.angles.values): - self.add_angles(self.anglst, j, self.labels, self.lines, self.multifile, - i, tempangles[i], self._send_angles) + self.add_angles( + self.anglst, + j, + self.labels, + self.lines, + self.multifile, + i, + tempangles[i], + self._send_angles, + ) self._send_angles() for i in self.labels: i.setDisabled(False) @@ -723,11 +883,12 @@ def _change_angles(self): i.setDisabled(False) self.commit.deferred() - def add_angles(self, anglst, lab, labels, lines, widget, - i, place, callback): #to be used in a loop + def add_angles( + self, anglst, lab, labels, lines, widget, i, place, callback + ): # to be used in a loop file = os.path.basename(lab) anglst.append(lab) - ledit = gui.lineEdit(widget, self, None, label = file, callback = callback) + ledit = gui.lineEdit(widget, self, None, label=file, callback=callback) ledit.setText(str(place)) lines.append(ledit) for j in ledit.parent().children(): @@ -852,16 +1013,24 @@ def handleNewSignals(self): self.data = self.more_data self.clear_angles(self.anglst, self.lines, self.labels, self.multifile) - self.clear_angles(self.multiin_anglst, self.multiin_lines, - self.multiin_labels, self.multiin) + self.clear_angles( + self.multiin_anglst, self.multiin_lines, self.multiin_labels, self.multiin + ) names = [i.name for i in self.data] - tempangles = np.linspace(0, 180, len(self.data)+1) + tempangles = np.linspace(0, 180, len(self.data) + 1) for i in range(len(self.data)): - self.add_angles(self.multiin_anglst, names[i], self.multiin_labels, - self.multiin_lines, self.multiin, i, tempangles[i], - self._send_ind_angles) + self.add_angles( + self.multiin_anglst, + names[i], + self.multiin_labels, + self.multiin_lines, + self.multiin, + i, + tempangles[i], + self._send_ind_angles, + ) self.input_select() @@ -873,7 +1042,9 @@ def handleNewSignals(self): if len(self.data) == 1: self.openContext(self.data[0]) - self.angles = self.anglemetas[0] if self.anglemetas else None #Fixes self.angles being None if it was first a text variable and then changed to categorical, but I feel this could be done better... + self.angles = ( + self.anglemetas[0] if self.anglemetas else None + ) # Fixes self.angles being None if it was first a text variable and then changed to categorical, but I feel this could be done better... self._change_angles() elif 1 < len(self.data) < 4 or len(self.data) == 0: self.Warning.notenough() @@ -893,7 +1064,9 @@ def handleNewSignals(self): self.openContext(Table.from_domain(domain2)) for i in range(len(self.feat_view.model())): - self.feat_view.model().setData(self.feat_view.model().index(i), 'Not used', role=Qt.UserRole) + self.feat_view.model().setData( + self.feat_view.model().index(i), 'Not used', role=Qt.UserRole + ) self.restore_alphas() self.commit.now() @@ -926,16 +1099,25 @@ def commit(self): if self.spec_type != 0: if self.spec_type == 1: for i, j in enumerate(sorted_data): - sorted_data[i].X = 1-np.power(10, j.X*-1) + sorted_data[i].X = 1 - np.power(10, j.X * -1) elif self.spec_type == 2: for i, j in enumerate(sorted_data): - sorted_data[i].X = 1-j.X + sorted_data[i].X = 1 - j.X if any(i not in sorted_data[0].domain.attributes for i in self.feats): self.Information.meta_calc() - self.start(run, sorted_data, list(self.feats), self.alphas, self.map_x, - self.map_y, self.invert_angles, list(self.polangles), - self.average, self.angles) + self.start( + run, + sorted_data, + list(self.feats), + self.alphas, + self.map_x, + self.map_y, + self.invert_angles, + list(self.polangles), + self.average, + self.angles, + ) def on_done(self, result: Results): if result is None: @@ -947,11 +1129,11 @@ def on_done(self, result: Results): else: if self.spec_type != 0: if self.spec_type == 1: - result.out.X = -np.log10(1-result.out.X) - result.model.X = -np.log10(1-result.model.X) + result.out.X = -np.log10(1 - result.out.X) + result.model.X = -np.log10(1 - result.model.X) elif self.spec_type == 2: - result.out.X = 1-result.out.X - result.model.X = 1-result.model.X + result.out.X = 1 - result.out.X + result.model.X = 1 - result.model.X self.Outputs.polar.send(result.out) self.Outputs.model.send(result.model) @@ -973,5 +1155,8 @@ def check_and_close_context(self): if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview - import orangecontrib.spectroscopy # so that can be loaded - WidgetPreview(OWPolar).run(insert_data=[(0, Orange.data.Table("polar/4-angle-ftir_multifile.tab"))]) + import orangecontrib.spectroscopy # so that can be loaded # noqa: F401 + + WidgetPreview(OWPolar).run( + insert_data=[(0, Orange.data.Table("polar/4-angle-ftir_multifile.tab"))] + ) diff --git a/orangecontrib/spectroscopy/widgets/owpreprocess.py b/orangecontrib/spectroscopy/widgets/owpreprocess.py index b4098e322..3a3d97fc2 100644 --- a/orangecontrib/spectroscopy/widgets/owpreprocess.py +++ b/orangecontrib/spectroscopy/widgets/owpreprocess.py @@ -11,43 +11,75 @@ from Orange.widgets import gui, settings from Orange.widgets.settings import SettingsHandler from Orange.widgets.widget import OWWidget, Msg, Input, Output -from Orange.widgets.data.utils.preprocess import SequenceFlow, Controller, \ - StandardItemModel +from Orange.widgets.data.utils.preprocess import ( + SequenceFlow, + Controller, + StandardItemModel, +) from Orange.widgets.data.owpreprocess import ( - PreprocessAction, Description, icon_path, DescriptionRole, ParametersRole + PreprocessAction, + Description, + icon_path, + DescriptionRole, + ParametersRole, ) from Orange.widgets.utils.sql import check_sql_input from Orange.widgets.utils.overlay import OverlayWidget -from Orange.widgets.utils.concurrent import TaskState, ConcurrentWidgetMixin, ConcurrentMixin - -from AnyQt.QtCore import ( - Qt, QEvent, QSize, QMimeData, QTimer +from Orange.widgets.utils.concurrent import ( + TaskState, + ConcurrentWidgetMixin, + ConcurrentMixin, ) + +from AnyQt.QtCore import Qt, QEvent, QSize, QMimeData, QTimer from AnyQt.QtWidgets import ( - QWidget, QListView, QVBoxLayout, QSizePolicy, QStyle, - QPushButton, QLabel, QMenu, QAction, QScrollArea, QGridLayout, - QToolButton, QSplitter + QWidget, + QListView, + QVBoxLayout, + QSizePolicy, + QStyle, + QPushButton, + QLabel, + QMenu, + QAction, + QScrollArea, + QGridLayout, + QToolButton, + QSplitter, ) from AnyQt.QtGui import ( - QIcon, QStandardItemModel, QStandardItem, - QKeySequence, QFont, QColor + QIcon, + QStandardItemModel, + QStandardItem, + QKeySequence, + QFont, + QColor, ) from AnyQt.QtCore import pyqtSignal as Signal, pyqtSlot as Slot, QObject from orangecontrib.spectroscopy.preprocess.utils import PreprocessException from orangecontrib.spectroscopy.widgets.owspectra import CurvePlot, NoSuchCurve -from orangecontrib.spectroscopy.widgets.preprocessors.misc import SavitzkyGolayFilteringEditor +from orangecontrib.spectroscopy.widgets.preprocessors.misc import ( + SavitzkyGolayFilteringEditor, +) from orangecontrib.spectroscopy.widgets.preprocessors.utils import REFERENCE_DATA_PARAM from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors -BREWER_PALETTE8 = [(127, 201, 127), (190, 174, 212), (253, 192, 134), (255, 255, 153), - (56, 108, 176), (240, 2, 127), (191, 91, 23), (102, 102, 102)] +BREWER_PALETTE8 = [ + (127, 201, 127), + (190, 174, 212), + (253, 192, 134), + (255, 255, 153), + (56, 108, 176), + (240, 2, 127), + (191, 91, 23), + (102, 102, 102), +] PREVIEW_COLORS = [QColor(*a).name() for a in BREWER_PALETTE8] class ViewController(Controller): - def createWidgetFor(self, index): w = super().createWidgetFor(index) w.parent_widget = self.parent() @@ -98,13 +130,22 @@ def _build_tw(self): self.title_label.setMinimumWidth(100) tl.addWidget(self.title_label, 0, 1) close_button = QToolButton(self) - ca = QAction("close", self, triggered=self.closeRequested, - icon=QIcon(self.style().standardPixmap(QStyle.SP_DockWidgetCloseButton))) + ca = QAction( + "close", + self, + triggered=self.closeRequested, + icon=QIcon(self.style().standardPixmap(QStyle.SP_DockWidgetCloseButton)), + ) close_button.setDefaultAction(ca) self.preview_button = QToolButton(self) - pa = QAction("preview", self, triggered=self.toggle_preview, checkable=True, - icon=QIcon(self.style().standardPixmap(QStyle.SP_MediaPlay)), - shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_P)) + pa = QAction( + "preview", + self, + triggered=self.toggle_preview, + checkable=True, + icon=QIcon(self.style().standardPixmap(QStyle.SP_MediaPlay)), + shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_P), + ) pa.setShortcutContext(Qt.WidgetWithChildrenShortcut) self.addAction(pa) self.preview_button.setDefaultAction(pa) @@ -134,7 +175,7 @@ def toggle_preview(self): def focusInEvent(self, event): super().focusInEvent(event) - try: #active selection on preview + try: # active selection on preview self.widget().activateOptions() except AttributeError: pass @@ -150,6 +191,7 @@ class SequenceFlow(SequenceFlow): """ FIXME Ugly hack: using the same name for access to private variables! """ + def __init__(self, *args, preview_callback=None, multiple_previews=False, **kwargs): super().__init__(*args, **kwargs) self.preview_callback = preview_callback @@ -158,7 +200,11 @@ def __init__(self, *args, preview_callback=None, multiple_previews=False, **kwar def preview_n(self): """How many preprocessors to apply for the preview?""" - ppos = [i for i, item in enumerate(self.layout_iter(self.__flowlayout)) if item.widget().preview] + ppos = [ + i + for i, item in enumerate(self.layout_iter(self.__flowlayout)) + if item.widget().preview + ] # if any, show the chosen preview if not self.multiple_previews: return ppos[-1] if ppos else None @@ -183,15 +229,14 @@ def preview_changed(self): self.preview_callback() def insertWidget(self, index, widget, title): - """ Mostly copied to get different kind of frame """ - frame = FocusFrame(widget=widget, title=title) #changed + """Mostly copied to get different kind of frame""" + frame = FocusFrame(widget=widget, title=title) # changed frame.closeRequested.connect(self.__closeRequested) frame.preview_changed.connect(self.preview_changed) layout = self.__flowlayout - frames = [item.widget() for item in self.layout_iter(layout) - if item.widget()] + frames = [item.widget() for item in self.layout_iter(layout) if item.widget()] if 0 < index < len(frames): # find the layout index of a widget occupying the current @@ -202,7 +247,7 @@ def insertWidget(self, index, widget, title): elif index < 0 or index >= len(frames): insert_index = layout.count() else: - assert False + raise AssertionError layout.insertWidget(insert_index, frame) frame.installEventFilter(self) @@ -212,7 +257,7 @@ def __closeRequested(self): super().__closeRequested() def minimumSizeHint(self): - """ Add space below so that dragging to bottom works """ + """Add space below so that dragging to bottom works""" psh = super().minimumSizeHint() return QSize(psh.width(), psh.height() + 100) @@ -223,13 +268,13 @@ def reset_preview_colors(self): item.set_color(PREVIEW_COLORS[i % len(PREVIEW_COLORS)]) def preview_color(self, i): - """ Return preview color of a specific widget. """ + """Return preview color of a specific widget.""" w = self.__flowlayout.itemAt(i).widget() return w.color class TimeoutLabel(QLabel): - """ A label that disappears out after two seconds. """ + """A label that disappears out after two seconds.""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -258,7 +303,9 @@ def transfer_highlight(from_: CurvePlot, to: CurvePlot): if len(index_with_same_id): highlight = index_with_same_id[0] try: - to.highlight_index_in_data(highlight, emit=False) # do not emit to avoid recursion + to.highlight_index_in_data( + highlight, emit=False + ) # do not emit to avoid recursion except NoSuchCurve: pass @@ -292,7 +339,6 @@ class InterruptException(Exception): class PreviewRunner(QObject, ConcurrentMixin): - preview_updated = Signal() def __init__(self, master): @@ -313,7 +359,7 @@ def on_partial_result(self, result): self.last_partial = i if self.preview_pos == i: self.preview_data = data - if self.preview_pos == i-1: + if self.preview_pos == i - 1: self.after_data = data widgets = self.master.flow_view.widgets() if i < len(widgets): @@ -377,27 +423,38 @@ def show_image_info(self, final_preview): self.last_text = new_text def show_preview(self, show_info_anyway=False): - """ Shows preview and also passes preview data to the widgets """ + """Shows preview and also passes preview data to the widgets""" master = self.master self.preview_pos = master.flow_view.preview_n() self.last_partial = None self.show_info_anyway = show_info_anyway self.preview_data = None self.after_data = None - pp_def = [master.preprocessormodel.item(i) - for i in range(master.preprocessormodel.rowCount())] + pp_def = [ + master.preprocessormodel.item(i) + for i in range(master.preprocessormodel.rowCount()) + ] if master.data is not None: data = master.sample_data(master.data) - self.start(self.run_preview, data, master.reference_data, - pp_def, master.process_reference) + self.start( + self.run_preview, + data, + master.reference_data, + pp_def, + master.process_reference, + ) else: master.curveplot.set_data(None) master.curveplot_after.set_data(None) @staticmethod - def run_preview(data: Orange.data.Table, reference: Orange.data.Table, - pp_def, process_reference, state: TaskState): - + def run_preview( + data: Orange.data.Table, + reference: Orange.data.Table, + pp_def, + process_reference, + state: TaskState, + ): def progress_interrupt(i: float): if state.is_interruption_requested(): raise InterruptException @@ -419,29 +476,32 @@ def progress_interrupt(i: float): class SpectraPreviews: - curveplot = settings.SettingProvider(CurvePlot) curveplot_after = settings.SettingProvider(CurvePlot) def __init__(self): self.curveplot = CurvePlot(self) self.curveplot_after = CurvePlot(self) - self.curveplot.plot.vb.x_padding = 0.005 # pad view so that lines are not hidden - self.curveplot_after.plot.vb.x_padding = 0.005 # pad view so that lines are not hidden + self.curveplot.plot.vb.x_padding = ( + 0.005 # pad view so that lines are not hidden + ) + self.curveplot_after.plot.vb.x_padding = ( + 0.005 # pad view so that lines are not hidden + ) self.curveplot.highlight_changed.connect( - lambda: transfer_highlight(self.curveplot, self.curveplot_after)) + lambda: transfer_highlight(self.curveplot, self.curveplot_after) + ) self.curveplot_after.highlight_changed.connect( - lambda: transfer_highlight(self.curveplot_after, self.curveplot)) + lambda: transfer_highlight(self.curveplot_after, self.curveplot) + ) def shutdown(self): self.curveplot.shutdown() self.curveplot_after.shutdown() -class GeneralPreprocess(OWWidget, ConcurrentWidgetMixin, - openclass=True): - +class GeneralPreprocess(OWWidget, ConcurrentWidgetMixin, openclass=True): class Inputs: data = Input("Data", Orange.data.Table, default=True) @@ -473,9 +533,11 @@ class Error(OWWidget.Error): preprocessor = Msg("Preprocessor error: see the widget for details.") class Warning(OWWidget.Warning): - reference_compat = Msg("Reference is not processed for compatibility with the loaded " - "workflow. New instances of this widget will also process " - "the reference input.") + reference_compat = Msg( + "Reference is not processed for compatibility with the loaded " + "workflow. New instances of this widget will also process " + "the reference input." + ) preprocessor = Msg("Preprocessor warning: see the widget for details.") def _build_PREPROCESSORS(self): @@ -484,13 +546,16 @@ def _build_PREPROCESSORS(self): for editor in self.editor_registry.sorted(): assert editor.qualname is not None assert editor.qualname not in qualnames - pa = PreprocessAction(editor.name, - editor.qualname, - editor.name, - Description(editor.name, - editor.icon if editor.icon else - icon_path("Discretize.svg")), - editor) + pa = PreprocessAction( + editor.name, + editor.qualname, + editor.name, + Description( + editor.name, + editor.icon if editor.icon else icon_path("Discretize.svg"), + ), + editor, + ) qualnames.add(editor.qualname) self.PREPROCESSORS.append(pa) @@ -516,6 +581,7 @@ def mimeData(indexlist): m = QMimeData() m.setData("application/x-qwidget-ref", qname) return m + # TODO: Fix this (subclass even if just to pass a function # for mimeData delegate) self.preprocessors.mimeData = mimeData @@ -526,10 +592,10 @@ def mimeData(indexlist): self.button.setMenu(self.preprocessor_menu) self.button.setAutoDefault(False) - self.preprocessorsView = view = QListView( + self.preprocessorsView = QListView( selectionMode=QListView.SingleSelection, dragEnabled=True, - dragDropMode=QListView.DragOnly + dragDropMode=QListView.DragOnly, ) self._qname2ppdef = {ppdef.qualname: ppdef for ppdef in self.PREPROCESSORS} @@ -537,19 +603,21 @@ def mimeData(indexlist): # List of 'selected' preprocessors and their parameters. self.preprocessormodel = None - self.flow_view = SequenceFlow(preview_callback=self._show_preview_info, - multiple_previews=self.preview_on_image) + self.flow_view = SequenceFlow( + preview_callback=self._show_preview_info, + multiple_previews=self.preview_on_image, + ) self.controler = ViewController(self.flow_view, parent=self) - self.scroll_area = QScrollArea( - verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn - ) + self.scroll_area = QScrollArea(verticalScrollBarPolicy=Qt.ScrollBarAlwaysOn) self.scroll_area.viewport().setAcceptDrops(True) self.scroll_area.setWidget(self.flow_view) self.scroll_area.setWidgetResizable(True) self.flow_view.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) - self.scroll_area.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Expanding) + self.scroll_area.setSizePolicy( + QSizePolicy.MinimumExpanding, QSizePolicy.Expanding + ) splitter = QSplitter(self) splitter.setOrientation(Qt.Vertical) @@ -588,10 +656,23 @@ def overlay(widget): self.final_preview_toggle = False if not self.preview_on_image: self.final_preview = gui.button( - box, self, "Final preview", self.flow_view.preview_changed, - toggleButton=True, value="final_preview_toggle", autoDefault=False) - gui.spin(box, self, "preview_curves", 1, self._max_preview_spectra, label="Show spectra", - callback=self._update_preview_number) + box, + self, + "Final preview", + self.flow_view.preview_changed, + toggleButton=True, + value="final_preview_toggle", + autoDefault=False, + ) + gui.spin( + box, + self, + "preview_curves", + 1, + self._max_preview_spectra, + label="Show spectra", + callback=self._update_preview_number, + ) self.output_box = gui.widgetBox(self.controlArea, "Output") b = gui.auto_commit(self.output_box, self, "autocommit", "Commit", box=False) @@ -607,7 +688,9 @@ def _update_preview_number(self): def sample_data(self, data): if data is not None and len(data) > self.preview_curves: - sampled_indices = random.Random(0).sample(range(len(data)), self.preview_curves) + sampled_indices = random.Random(0).sample( + range(len(data)), self.preview_curves + ) return data[sampled_indices] else: return self.data @@ -618,12 +701,12 @@ def _reference_compat_warning(self): self.Warning.reference_compat() def show_preview(self, show_info_anyway=False): - """ Shows preview and also passes preview data to the widgets """ + """Shows preview and also passes preview data to the widgets""" self._reference_compat_warning() self.Warning.preprocessor.clear() self.Error.preprocessor.clear() self.Error.preview.clear() - for w in self.flow_view.widgets(): + for w in self.flow_view.widgets(): if getattr(w, "Error", None): # only BaseEditorOrange supports errors w.Error.exception.clear() @@ -636,7 +719,9 @@ def _create_preprocessor_action(self, pp_def): else: icon = QIcon() action = QAction( - description.title, self, triggered=lambda x, p=pp_def: self.add_preprocessor(p) + description.title, + self, + triggered=lambda x, p=pp_def: self.add_preprocessor(p), ) action.setToolTip(description.summary or "") action.setIcon(icon) @@ -649,9 +734,11 @@ def _init_menu_flat(self): def _init_menu_registry(self): for category in self.editor_registry.categories(): - category_menu = self.preprocessor_menu \ - if category == "" \ + category_menu = ( + self.preprocessor_menu + if category == "" else self.preprocessor_menu.addMenu(category) + ) for editor in self.editor_registry.sorted(category): pa = self._qname2ppdef[editor.qualname] action = self._create_preprocessor_action(pa) @@ -667,8 +754,7 @@ def _initialize(self): item = QStandardItem(icon, description.title) item.setToolTip(description.summary or "") item.setData(pp_def, DescriptionRole) - item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | - Qt.ItemIsDragEnabled) + item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled) self.preprocessors.appendRow([item]) if self.editor_registry is None: @@ -676,7 +762,6 @@ def _initialize(self): else: self._init_menu_registry() - try: model = self.load(self.storedsettings) except Exception as ex: @@ -701,13 +786,12 @@ def _initialize(self): def load(self, saved): """Load a preprocessor list from a dict.""" - name = saved.get("name", "") + name = saved.get("name", "") # noqa: F841 preprocessors = saved.get("preprocessors", []) model = StandardItemModel() def dropMimeData(data, action, row, column, parent): - if data.hasFormat("application/x-qwidget-ref") and \ - action == Qt.CopyAction: + if data.hasFormat("application/x-qwidget-ref") and action == Qt.CopyAction: qname = bytes(data.data("application/x-qwidget-ref")).decode() ppdef = self._qname2ppdef[qname] @@ -850,7 +934,7 @@ def sizeHint(self): @classmethod def migrate_preprocessor(cls, preprocessor, version): - """ Migrate a preprocessor. A preprocessor should migrate into a list of preprocessors. """ + """Migrate a preprocessor. A preprocessor should migrate into a list of preprocessors.""" name, settings = preprocessor return [((name, settings), version)] @@ -866,21 +950,23 @@ def migrate_preprocessor_list(cls, preprocessors): @classmethod def migrate_preprocessors(cls, preprocessors, version): - input = list(zip(preprocessors, [version]*len(preprocessors))) + input = list(zip(preprocessors, [version] * len(preprocessors))) # noqa: B905 migrated = cls.migrate_preprocessor_list(input) return [p[0] for p in migrated], cls.settings_version @classmethod def migrate_settings(cls, settings_, version): # migrate individual preprocessors - if "storedsettings" in settings_ and "preprocessors" in settings_["storedsettings"]: - settings_["storedsettings"]["preprocessors"], _ = \ - cls.migrate_preprocessors(settings_["storedsettings"]["preprocessors"], version) - + if ( + "storedsettings" in settings_ + and "preprocessors" in settings_["storedsettings"] + ): + settings_["storedsettings"]["preprocessors"], _ = cls.migrate_preprocessors( + settings_["storedsettings"]["preprocessors"], version + ) -class SpectralPreprocess(GeneralPreprocess, - SpectraPreviews, openclass=True): +class SpectralPreprocess(GeneralPreprocess, SpectraPreviews, openclass=True): def __init__(self): SpectraPreviews.__init__(self) super().__init__() @@ -891,7 +977,6 @@ def onDeleteWidget(self): class SpectralPreprocessReference(SpectralPreprocess, openclass=True): - class Inputs(SpectralPreprocess.Inputs): reference = Input("Reference", Orange.data.Table) @@ -901,13 +986,14 @@ def set_reference(self, reference): class OWPreprocess(SpectralPreprocessReference): - name = "Preprocess Spectra" description = "Construct a data preprocessing pipeline." icon = "icons/preprocess.svg" priority = 1000 - replaces = ["orangecontrib.infrared.widgets.owpreproc.OWPreprocess", - "orangecontrib.infrared.widgets.owpreprocess.OWPreprocess"] + replaces = [ + "orangecontrib.infrared.widgets.owpreproc.OWPreprocess", + "orangecontrib.infrared.widgets.owpreprocess.OWPreprocess", + ] settings_version = 9 @@ -923,13 +1009,26 @@ class OWPreprocess(SpectralPreprocessReference): def create_outputs(self): self._reference_compat_warning() - pp_def = [self.preprocessormodel.item(i) for i in range(self.preprocessormodel.rowCount())] - self.start(self.run_task, self.data, self.reference_data, pp_def, self.process_reference) + pp_def = [ + self.preprocessormodel.item(i) + for i in range(self.preprocessormodel.rowCount()) + ] + self.start( + self.run_task, + self.data, + self.reference_data, + pp_def, + self.process_reference, + ) @staticmethod - def run_task(data: Orange.data.Table, reference: Orange.data.Table, - pp_def, process_reference, state: TaskState): - + def run_task( + data: Orange.data.Table, + reference: Orange.data.Table, + pp_def, + process_reference, + state: TaskState, + ): def progress_interrupt(i: float): state.set_progress_value(i) if state.is_interruption_requested(): @@ -939,20 +1038,20 @@ def progress_interrupt(i: float): # happen when adding a preprocessor (there, commit() is called twice). # Wait 100 ms before processing - if a new task is started in meanwhile, # allow that is easily` cancelled. - for i in range(10): + for _i in range(10): time.sleep(0.010) progress_interrupt(0) n = len(pp_def) plist = [] for i in range(n): - progress_interrupt(i/n*100) + progress_interrupt(i / n * 100) item = pp_def[i] pp = create_preprocessor(item, reference) plist.append(pp) if data is not None: data = pp(data) - progress_interrupt((i/n + 0.5/n)*100) + progress_interrupt((i / n + 0.5 / n) * 100) if process_reference and reference is not None and i != n - 1: reference = pp(reference) # if there are no preprocessors, return None instead of an empty list @@ -994,9 +1093,14 @@ def migrate_preprocessor(cls, preprocessor, version): settings["from_type"] = 0 settings["to_type"] = 1 version = 5 - if name in ["orangecontrib.spectroscopy.preprocess.emsc", - "orangecontrib.spectroscopy.preprocess.me_emsc.me_emsc"] \ - and version < 7: + if ( + name + in [ + "orangecontrib.spectroscopy.preprocess.emsc", + "orangecontrib.spectroscopy.preprocess.me_emsc.me_emsc", + ] + and version < 7 + ): ranges = settings.get("ranges", []) new_ranges = [[l, r, w, 0.0] for l, r, w in ranges] settings["ranges"] = new_ranges @@ -1009,8 +1113,8 @@ def migrate_preprocessor(cls, preprocessor, version): settings["inverse"] = True if name == "orangecontrib.infrared.curveshift": name = "orangecontrib.spectroscopy.shiftandscale" - settings["offset"] = settings.get("amount", 0.) - settings["scale"] = 1. + settings["offset"] = settings.get("amount", 0.0) + settings["scale"] = 1.0 return [((name, settings), version)] @classmethod @@ -1018,7 +1122,7 @@ def migrate_settings(cls, settings_, version): # For backwards compatibility, set process_reference=False # but only if there were multiple preprocessors if "process_reference" not in settings_: - settings_["process_reference"] = not( + settings_["process_reference"] = not ( version <= 5 and "storedsettings" in settings_ and "preprocessors" in settings_["storedsettings"] @@ -1030,5 +1134,6 @@ def migrate_settings(cls, settings_, version): if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview + data = Orange.data.Table("collagen.csv") WidgetPreview(OWPreprocess).run(set_data=data[:30], set_reference=data[10:11]) diff --git a/orangecontrib/spectroscopy/widgets/owreshape.py b/orangecontrib/spectroscopy/widgets/owreshape.py index 82dc71dfe..b6ada86aa 100644 --- a/orangecontrib/spectroscopy/widgets/owreshape.py +++ b/orangecontrib/spectroscopy/widgets/owreshape.py @@ -17,12 +17,15 @@ class OWReshape(OWWidget): # Short widget description description = ( "Builds or modifies the shape of the input dataset to create 2D maps " - "from series data or change the dimensions of existing 2D datasets.") + "from series data or change the dimensions of existing 2D datasets." + ) icon = "icons/reshape.svg" - replaces = ["orangecontrib.infrared.widgets.owmapbuilder.OWMapBuilder", - "orangecontrib.infrared.widgets.owreshape.OWReshape"] + replaces = [ + "orangecontrib.infrared.widgets.owmapbuilder.OWMapBuilder", + "orangecontrib.infrared.widgets.owreshape.OWReshape", + ] # Define inputs and outputs class Inputs: @@ -62,10 +65,20 @@ def __init__(self): self.le3 = lineEditIntOrNone(box, self, "ypoints", callback=self.le3_changed) formlayout.addRow("Y dimension", self.le3) - gui.checkBox(box, self, "invert_x", "Invert X axis", - callback=lambda: self.commit.deferred()) - gui.checkBox(box, self, "invert_y", "Invert Y axis", - callback=lambda: self.commit.deferred()) + gui.checkBox( + box, + self, + "invert_x", + "Invert X axis", + callback=lambda: self.commit.deferred(), + ) + gui.checkBox( + box, + self, + "invert_y", + "Invert Y axis", + callback=lambda: self.commit.deferred(), + ) gui.auto_commit(self.controlArea, self, "autocommit", "Send Data") @@ -83,20 +96,20 @@ def set_data(self, dataset): self.commit.now() # maybe doable with one callback... - def le1_changed(self): # X dimension + def le1_changed(self): # X dimension if self.data is not None and self.xpoints: self.Warning.wrong_div.clear() - ytemp = len(self.data.X)//self.xpoints + ytemp = len(self.data.X) // self.xpoints if len(self.data.X) % self.xpoints == 0: self.ypoints = ytemp self.commit.deferred() else: self.Warning.wrong_div(len(self.data.X)) - def le3_changed(self): # Y dimension + def le3_changed(self): # Y dimension if self.data is not None and self.ypoints: self.Warning.wrong_div.clear() - xtemp = len(self.data.X)//self.ypoints + xtemp = len(self.data.X) // self.ypoints if len(self.data.X) % self.ypoints == 0: self.xpoints = xtemp self.commit.deferred() @@ -107,14 +120,26 @@ def le3_changed(self): # Y dimension @gui.deferred def commit(self): map_data = None - if self.data and self.xpoints is not None and self.ypoints is not None \ - and self.xpoints * self.ypoints == len(self.data): - used_names = [var.name for var in self.data.domain.variables + self.data.domain.metas] - xmeta = Orange.data.ContinuousVariable.make(get_unique_names(used_names, "X")) - ymeta = Orange.data.ContinuousVariable.make(get_unique_names(used_names, "Y")) + if ( + self.data + and self.xpoints is not None + and self.ypoints is not None + and self.xpoints * self.ypoints == len(self.data) + ): + used_names = [ + var.name for var in self.data.domain.variables + self.data.domain.metas + ] + xmeta = Orange.data.ContinuousVariable.make( + get_unique_names(used_names, "X") + ) + ymeta = Orange.data.ContinuousVariable.make( + get_unique_names(used_names, "Y") + ) # add new variables for X and Y dimension ot the data domain metas = self.data.domain.metas + (xmeta, ymeta) - domain = Orange.data.Domain(self.data.domain.attributes, self.data.domain.class_vars, metas) + domain = Orange.data.Domain( + self.data.domain.attributes, self.data.domain.class_vars, metas + ) map_data = self.data.transform(domain) xpoints = np.arange(self.xpoints) ypoints = np.arange(self.ypoints) @@ -123,20 +148,27 @@ def commit(self): if self.invert_y: ypoints = np.flip(ypoints) with map_data.unlocked(map_data.metas): - map_data[:, xmeta] = np.tile(xpoints, len(self.data)//self.xpoints).reshape(-1, 1) - map_data[:, ymeta] = np.repeat(ypoints, len(self.data)//self.ypoints).reshape(-1, 1) + map_data[:, xmeta] = np.tile( + xpoints, len(self.data) // self.xpoints + ).reshape(-1, 1) + map_data[:, ymeta] = np.repeat( + ypoints, len(self.data) // self.ypoints + ).reshape(-1, 1) self.Outputs.map.send(map_data) def send_report(self): if self.xpoints and self.ypoints is not None: - self.report_items(( - ("Number of points in the X direction", int(self.xpoints)), - ("Number of points in the Y direction", int(self.ypoints)) - )) + self.report_items( + ( + ("Number of points in the X direction", int(self.xpoints)), + ("Number of points in the Y direction", int(self.ypoints)), + ) + ) else: return -if __name__=="__main__": +if __name__ == "__main__": from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWReshape).run(Orange.data.Table("collagen")) diff --git a/orangecontrib/spectroscopy/widgets/owsnr.py b/orangecontrib/spectroscopy/widgets/owsnr.py index 4fae53f95..b69795edd 100644 --- a/orangecontrib/spectroscopy/widgets/owsnr.py +++ b/orangecontrib/spectroscopy/widgets/owsnr.py @@ -13,8 +13,7 @@ class OWSNR(OWWidget): name = "SNR" # Short widget description - description = ( - "Calculates Signal-to-Noise Ratio (SNR), Averages or Standard Deviation by coordinates.") + description = "Calculates Signal-to-Noise Ratio (SNR), Averages or Standard Deviation by coordinates." icon = "icons/snr.svg" keywords = ["signal", "noise", "standard", "deviation", "average"] @@ -26,15 +25,19 @@ class Inputs: class Outputs: final_data = Output("SNR", Orange.data.Table, default=True) - OUT_OPTIONS = {'Signal-to-noise ratio': 0, #snr - 'Average': 1, # average - 'Standard Deviation': 2} # std + OUT_OPTIONS = { + 'Signal-to-noise ratio': 0, # snr + 'Average': 1, # average + 'Standard Deviation': 2, + } # std settingsHandler = settings.DomainContextHandler() - group_x = settings.ContextSetting(None, exclude_attributes=True, - exclude_class_vars=True) - group_y = settings.ContextSetting(None, exclude_attributes=True, - exclude_class_vars=True) + group_x = settings.ContextSetting( + None, exclude_attributes=True, exclude_class_vars=True + ) + group_y = settings.ContextSetting( + None, exclude_attributes=True, exclude_class_vars=True + ) out_choiced = settings.Setting(0) autocommit = settings.Setting(True) @@ -54,20 +57,38 @@ def __init__(self): self.group_y = None # methods in this widget assume group axes in metas - self.xy_model = DomainModel(DomainModel.METAS, - placeholder="None", separators=False, - valid_types=Orange.data.ContinuousVariable) + self.xy_model = DomainModel( + DomainModel.METAS, + placeholder="None", + separators=False, + valid_types=Orange.data.ContinuousVariable, + ) self.group_view_x = gui.comboBox( - self.controlArea, self, "group_x", box="Select axis: x", - model=self.xy_model, callback=self.grouping_changed) + self.controlArea, + self, + "group_x", + box="Select axis: x", + model=self.xy_model, + callback=self.grouping_changed, + ) self.group_view_y = gui.comboBox( - self.controlArea, self, "group_y", box="Select axis: y", - model=self.xy_model, callback=self.grouping_changed) + self.controlArea, + self, + "group_y", + box="Select axis: y", + model=self.xy_model, + callback=self.grouping_changed, + ) self.selected_out = gui.comboBox( - self.controlArea, self, "out_choiced", box="Select Output:", - items=self.OUT_OPTIONS, callback=self.out_choice_changed) + self.controlArea, + self, + "out_choiced", + box="Select Output:", + items=self.OUT_OPTIONS, + callback=self.out_choice_changed, + ) gui.auto_commit(self.controlArea, self, "autocommit", "Apply") @@ -97,22 +118,30 @@ def set_data(self, dataset): def calc_table_np(self, array): if len(array) == 0: return array - if self.out_choiced == 0: #snr + if self.out_choiced == 0: # snr return self.make_table( - (bottleneck.nanmean(array, axis=0) / - bottleneck.nanstd(array, axis=0)).reshape(1, -1), self.data) - elif self.out_choiced == 1: #avg - return self.make_table(bottleneck.nanmean(array, axis=0).reshape(1, -1), self.data) - else: # std - return self.make_table(bottleneck.nanstd(array, axis=0).reshape(1, -1), self.data) - + ( + bottleneck.nanmean(array, axis=0) / bottleneck.nanstd(array, axis=0) + ).reshape(1, -1), + self.data, + ) + elif self.out_choiced == 1: # avg + return self.make_table( + bottleneck.nanmean(array, axis=0).reshape(1, -1), self.data + ) + else: # std + return self.make_table( + bottleneck.nanstd(array, axis=0).reshape(1, -1), self.data + ) @staticmethod def make_table(array, data_table): - new_table = Orange.data.Table.from_numpy(data_table.domain, - X=array.copy(), - Y=np.atleast_2d(data_table.Y[0]).copy(), - metas=np.atleast_2d(data_table.metas[0]).copy()) + new_table = Orange.data.Table.from_numpy( + data_table.domain, + X=array.copy(), + Y=np.atleast_2d(data_table.Y[0]).copy(), + metas=np.atleast_2d(data_table.metas[0]).copy(), + ) cont_vars = data_table.domain.class_vars + data_table.domain.metas with new_table.unlocked(): for var in cont_vars: @@ -155,14 +184,14 @@ def extract_col(data, var): sortidx = np.lexsort(coo.T) sorted_coo = coo[sortidx] unqID_mask = np.append(True, np.any(np.diff(sorted_coo, axis=0), axis=1)) - ID = unqID_mask.cumsum()-1 + ID = unqID_mask.cumsum() - 1 unq_coo = sorted_coo[unqID_mask] unique, counts = np.unique(ID, return_counts=True) pos = 0 bins = [] for size in counts: - bins.append(sortidx[pos:pos+size]) + bins.append(sortidx[pos : pos + size]) pos += size matrix = [] @@ -192,14 +221,14 @@ def extract_col(data, var): sortidx = np.lexsort(coo.T) sorted_coo = coo[sortidx] unqID_mask = np.append(True, np.any(np.diff(sorted_coo, axis=0), axis=1)) - ID = unqID_mask.cumsum()-1 + ID = unqID_mask.cumsum() - 1 unq_coo = sorted_coo[unqID_mask] unique, counts = np.unique(ID, return_counts=True) pos = 0 bins = [] for size in counts: - bins.append(sortidx[pos:pos+size]) + bins.append(sortidx[pos : pos + size]) pos += size matrix = [] @@ -239,4 +268,5 @@ def commit(self): if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWSNR).run(Orange.data.Table("three_coordinates_data.csv")) diff --git a/orangecontrib/spectroscopy/widgets/owspectra.py b/orangecontrib/spectroscopy/widgets/owspectra.py index 314f68f2d..c1ab78e41 100644 --- a/orangecontrib/spectroscopy/widgets/owspectra.py +++ b/orangecontrib/spectroscopy/widgets/owspectra.py @@ -12,11 +12,29 @@ except ImportError: dask = None -from AnyQt.QtWidgets import QWidget, QGraphicsItem, QPushButton, QMenu, \ - QGridLayout, QAction, QVBoxLayout, QApplication, QWidgetAction, \ - QShortcut, QToolTip, QGraphicsRectItem, QGraphicsTextItem -from AnyQt.QtGui import QColor, QPixmapCache, QPen, QKeySequence, QFontDatabase, \ - QPalette +from AnyQt.QtWidgets import ( + QWidget, + QGraphicsItem, + QPushButton, + QMenu, + QGridLayout, + QAction, + QVBoxLayout, + QApplication, + QWidgetAction, + QShortcut, + QToolTip, + QGraphicsRectItem, + QGraphicsTextItem, +) +from AnyQt.QtGui import ( + QColor, + QPixmapCache, + QPen, + QKeySequence, + QFontDatabase, + QPalette, +) from AnyQt.QtCore import Qt, QRectF, QPointF, QObject from AnyQt.QtCore import pyqtSignal @@ -32,26 +50,40 @@ from Orange.data import DiscreteVariable from Orange.widgets.widget import OWWidget, Msg, OWComponent, Input from Orange.widgets import gui -from Orange.widgets.settings import \ - Setting, ContextSetting, DomainContextHandler, SettingProvider +from Orange.widgets.settings import ( + Setting, + ContextSetting, + DomainContextHandler, + SettingProvider, +) from Orange.widgets.utils.itemmodels import DomainModel -from Orange.widgets.utils.plot import \ - SELECT, PANNING, ZOOMING +from Orange.widgets.utils.plot import SELECT, PANNING, ZOOMING from Orange.widgets.utils import saveplot from Orange.widgets.visualize.owscatterplotgraph import LegendItem from Orange.widgets.utils.concurrent import TaskState, ConcurrentMixin -from Orange.widgets.visualize.utils.plotutils import HelpEventDelegate, PlotWidget, AxisItem +from Orange.widgets.visualize.utils.plotutils import ( + HelpEventDelegate, + PlotWidget, + AxisItem, +) from Orange.widgets.visualize.utils.customizableplot import CommonParameterSetter from orangecontrib.spectroscopy import dask_client from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.utils import apply_columns_numpy -from orangecontrib.spectroscopy.widgets.line_geometry import \ - distance_curves, intersect_curves_chunked -from orangecontrib.spectroscopy.widgets.gui import pixel_decimals, \ - VerticalPeakLine, float_to_str_decimals as strdec -from orangecontrib.spectroscopy.widgets.utils import \ - SelectionGroupMixin, SelectionOutputsMixin +from orangecontrib.spectroscopy.widgets.line_geometry import ( + distance_curves, + intersect_curves_chunked, +) +from orangecontrib.spectroscopy.widgets.gui import ( + pixel_decimals, + VerticalPeakLine, + float_to_str_decimals as strdec, +) +from orangecontrib.spectroscopy.widgets.utils import ( + SelectionGroupMixin, + SelectionOutputsMixin, +) from orangecontrib.spectroscopy.widgets.visual_settings import FloatOrUndefined SELECT_SQUARE = 123 @@ -73,9 +105,17 @@ # distance to the first point in pixels that finishes the polygon SELECT_POLYGON_TOLERANCE = 10 -COLORBREWER_SET1 = [(228, 26, 28), (55, 126, 184), (77, 175, 74), (152, 78, 163), (255, 127, 0), - (255, 255, 51), (166, 86, 40), (247, 129, 191), (153, 153, 153)] - +COLORBREWER_SET1 = [ + (228, 26, 28), + (55, 126, 184), + (77, 175, 74), + (152, 78, 163), + (255, 127, 0), + (255, 255, 51), + (166, 86, 40), + (247, 129, 191), + (153, 153, 153), +] def selection_modifiers(): @@ -87,7 +127,6 @@ def selection_modifiers(): class ParameterSetter(CommonParameterSetter): - VIEW_RANGE_BOX = "View Range" DEFAULT_LINE_WIDTH = 1 SPECTRA_BOX = "Spectra" @@ -112,13 +151,20 @@ def update_setters(self): self.LEGEND_LABEL: self.FONT_SETTING, }, self.VIEW_RANGE_BOX: { - "X": {"xMin": (FloatOrUndefined(), None), "xMax": (FloatOrUndefined(), None)}, - "Y": {"yMin": (FloatOrUndefined(), None), "yMax": (FloatOrUndefined(), None)} + "X": { + "xMin": (FloatOrUndefined(), None), + "xMax": (FloatOrUndefined(), None), + }, + "Y": { + "yMin": (FloatOrUndefined(), None), + "yMax": (FloatOrUndefined(), None), + }, }, self.SPECTRA_BOX: { - self.LINE_WIDTH_LABEL: {self.LINE_WIDTH_LABEL: - (range(1, 10), self.DEFAULT_LINE_WIDTH)} - } + self.LINE_WIDTH_LABEL: { + self.LINE_WIDTH_LABEL: (range(1, 10), self.DEFAULT_LINE_WIDTH) + } + }, } def set_limits(**args): @@ -208,7 +254,7 @@ def setData(self, *args, **kargs): class PlotCurvesItem(GraphicsObject): - """ Multiple curves on a single plot that can be cached together. """ + """Multiple curves on a single plot that can be cached together.""" def __init__(self): pg.GraphicsObject.__init__(self) @@ -243,9 +289,13 @@ def add_curve(self, c, ignore_bounds=False): def boundingRect(self): # replace undefined (NaN) elements with defaults - bounds = [d if np.isnan(b) else b \ - for b, d in zip(self.bounds, self.default_bounds)] - return QRectF(bounds[0], bounds[1], bounds[2] - bounds[0], bounds[3] - bounds[1]) + bounds = [ + d if np.isnan(b) else b + for b, d in zip(self.bounds, self.default_bounds) # noqa: B905 + ] + return QRectF( + bounds[0], bounds[1], bounds[2] - bounds[0], bounds[3] - bounds[1] + ) def closestindex(array, v, side="left"): @@ -274,8 +324,8 @@ def distancetocurves(array, x, y, xpixel, ypixel, r=5, cache=None): if cache is not None: cache[id(x)] = xmin, xmax xmin = max(0, xmin - 1) - xp = array[0][xmin:xmax + 2] - yp = array[1][:, xmin:xmax + 2] + xp = array[0][xmin : xmax + 2] + yp = array[1][:, xmin : xmax + 2] # convert to distances in pixels xp = (xp - x) / xpixel @@ -293,7 +343,6 @@ class InterruptException(Exception): class ShowAverage(QObject, ConcurrentMixin): - shown = pyqtSignal() def __init__(self, master): @@ -311,13 +360,24 @@ def show(self): self.shown.emit() else: color_var = master.feature_color - self.start(self.compute_averages, master.data, color_var, master.subset_indices, - master.selection_group, master.selection_type) + self.start( + self.compute_averages, + master.data, + color_var, + master.subset_indices, + master.selection_group, + master.selection_type, + ) @staticmethod - def compute_averages(data: Orange.data.Table, color_var, subset_indices, - selection_group, selection_type, state: TaskState): - + def compute_averages( + data: Orange.data.Table, + color_var, + subset_indices, + selection_group, + selection_type, + state: TaskState, + ): def progress_interrupt(i: float): if state.is_interruption_requested(): if future: @@ -360,18 +420,23 @@ def _split_by_color_value(data, color_var): if np.any(part_selection): if is_dask: subset = data.X[part_selection] - compute_dask.extend([da.nanstd(subset, axis=0), - da.nanmean(subset, axis=0)]) + compute_dask.extend( + [da.nanstd(subset, axis=0), da.nanmean(subset, axis=0)] + ) std, mean = None, None else: - std = apply_columns_numpy(data.X, - lambda x: bottleneck.nanstd(x, axis=0), - part_selection, - callback=progress_interrupt) - mean = apply_columns_numpy(data.X, - lambda x: bottleneck.nanmean(x, axis=0), - part_selection, - callback=progress_interrupt) + std = apply_columns_numpy( + data.X, + lambda x: bottleneck.nanstd(x, axis=0), + part_selection, + callback=progress_interrupt, + ) + mean = apply_columns_numpy( + data.X, + lambda x: bottleneck.nanmean(x, axis=0), + part_selection, + callback=progress_interrupt, + ) results.append([colorv, part, mean, std, part_selection]) if is_dask: @@ -383,8 +448,8 @@ def _split_by_color_value(data, color_var): return computed = future.result() for i, lr in enumerate(results): - lr[2] = computed[i*2] - lr[3] = computed[i*2+1] + lr[2] = computed[i * 2] + lr[3] = computed[i * 2 + 1] progress_interrupt(0) return results @@ -401,7 +466,11 @@ def on_done(self, res): std = std[master.data_xsind] mean = mean[master.data_xsind] if part is None: - pen = master.pen_normal if np.any(master.subset_indices) else master.pen_subset + pen = ( + master.pen_normal + if np.any(master.subset_indices) + else master.pen_subset + ) elif part == "selection" and master.selection_type: pen = master.pen_selected elif part == "subset": @@ -425,7 +494,6 @@ def on_partial_result(self, result): class ShowIndividual(QObject, ConcurrentMixin): - shown = pyqtSignal() def __init__(self, master): @@ -442,8 +510,7 @@ def show(self): if not master.data: return sampled_indices = master._compute_sample(master.data.X) - self.start(self.compute_curves, master.data_x, master.data.X, - sampled_indices) + self.start(self.compute_curves, master.data_x, master.data.X, sampled_indices) @staticmethod def compute_curves(x, ys, sampled_indices, state: TaskState): @@ -483,7 +550,7 @@ def on_done(self, res): miny = bottleneck.nanmin(ys) maxy = bottleneck.nanmax(ys) space = (maxy - miny) * waterfall_constant - mul = (np.arange(len(ys))*space + 1).reshape(-1, 1) + mul = (np.arange(len(ys)) * space + 1).reshape(-1, 1) ys = ys * mul # shuffle the data before drawing because classes often appear sequentially @@ -492,7 +559,9 @@ def on_done(self, res): random.Random(master.sample_seed).shuffle(indices) sampled_indices = [sampled_indices[i] for i in indices] master.sampled_indices = sampled_indices - master.sampled_indices_inverse = {s: i for i, s in enumerate(master.sampled_indices)} + master.sampled_indices_inverse = { + s: i for i, s in enumerate(master.sampled_indices) + } master.new_sampling.emit(len(master.sampled_indices)) ys = ys[indices] # ys was already subsampled @@ -503,9 +572,12 @@ def on_done(self, res): master.add_curve(x, y, ignore_bounds=True) if x.size and ys.size: - bounding_rect = QGraphicsRectItem(QRectF( - QPointF(bottleneck.nanmin(x), bottleneck.nanmin(ys)), - QPointF(bottleneck.nanmax(x), bottleneck.nanmax(ys)))) + bounding_rect = QGraphicsRectItem( + QRectF( + QPointF(bottleneck.nanmin(x), bottleneck.nanmin(ys)), + QPointF(bottleneck.nanmax(x), bottleneck.nanmax(ys)), + ) + ) bounding_rect.setPen(QPen(Qt.NoPen)) # prevents border of 1 master.curves_cont.add_bounds(bounding_rect) @@ -533,7 +605,9 @@ def __init__(self, graph): # line for marking selection self.selection_line = pg.PlotCurveItem() - self.selection_line.setPen(pg.mkPen(color=QColor(Qt.black), width=2, style=Qt.DotLine)) + self.selection_line.setPen( + pg.mkPen(color=QColor(Qt.black), width=2, style=Qt.DotLine) + ) self.selection_line.setZValue(1e9) self.selection_line.hide() self.addItem(self.selection_line, ignoreBounds=True) @@ -541,9 +615,9 @@ def __init__(self, graph): # yellow marker for ending the polygon self.selection_poly_marker = pg.ScatterPlotItem() self.selection_poly_marker.setPen(pg.mkPen(color=QColor(Qt.yellow), width=2)) - self.selection_poly_marker.setSize(SELECT_POLYGON_TOLERANCE*2) + self.selection_poly_marker.setSize(SELECT_POLYGON_TOLERANCE * 2) self.selection_poly_marker.setBrush(None) - self.selection_poly_marker.setZValue(1e9+1) + self.selection_poly_marker.setZValue(1e9 + 1) self.selection_poly_marker.hide() self.selection_poly_marker.mouseClickEvent = lambda x: x # ignore mouse clicks self.addItem(self.selection_poly_marker, ignoreBounds=True) @@ -570,9 +644,7 @@ def update_selection_tooltip(self, modifiers=Qt.NoModifier): if not self.tiptexts: self._create_select_tooltip() text = self.tiptexts[Qt.NoModifier] - for mod in [Qt.ControlModifier, - Qt.ShiftModifier, - Qt.AltModifier]: + for mod in [Qt.ControlModifier, Qt.ShiftModifier, Qt.AltModifier]: if modifiers & mod: text = self.tiptexts.get(mod) break @@ -585,15 +657,16 @@ def update_selection_tooltip(self, modifiers=Qt.NoModifier): def _create_select_tooltip(self): scene = self.scene() tip_parts = [ - (Qt.ControlModifier, - "{}: Append to group". - format("Cmd" if sys.platform == "darwin" else "Ctrl")), + ( + Qt.ControlModifier, + "{}: Append to group".format( + "Cmd" if sys.platform == "darwin" else "Ctrl" + ), + ), (Qt.ShiftModifier, "Shift: Add group"), - (Qt.AltModifier, "Alt: Remove") + (Qt.AltModifier, "Alt: Remove"), ] - all_parts = "
" + \ - ", ".join(part for _, part in tip_parts) + \ - "
" + all_parts = "
" + ", ".join(part for _, part in tip_parts) + "
" self.tiptexts = { modifier: all_parts.replace(part, "{}".format(part)) for modifier, part in tip_parts @@ -635,9 +708,12 @@ def mouseDragEvent(self, ev, axis=None): elif self.action == PANNING: ev.ignore() super().mouseDragEvent(ev, axis=axis) - elif self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] \ - and ev.button() == Qt.LeftButton \ - and hasattr(self.graph, "selection_type") and self.graph.selection_type: + elif ( + self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] + and ev.button() == Qt.LeftButton + and hasattr(self.graph, "selection_type") + and self.graph.selection_type + ): pos = self.childGroup.mapFromParent(ev.pos()) start = self.childGroup.mapFromParent(ev.buttonDownPos()) if self.current_selection is None: @@ -649,15 +725,20 @@ def mouseDragEvent(self, ev, axis=None): ev.ignore() def suggestPadding(self, axis): - return 0. + return 0.0 def mouseMovedEvent(self, ev): # not a Qt event! if self.action == ZOOMING and self.zoomstartpoint: pos = self.mapFromView(self.mapSceneToView(ev)) self.updateScaleBox(self.zoomstartpoint, pos) - if self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] and self.current_selection: + if ( + self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] + and self.current_selection + ): # ev is a position of the whole component (with axes) - pos = self.childGroup.mapFromParent(self.mapFromView(self.mapSceneToView(ev))) + pos = self.childGroup.mapFromParent( + self.mapFromView(self.mapSceneToView(ev)) + ) if self.action == SELECT: self.updateSelectionLine(pos) elif self.action == SELECT_SQUARE: @@ -672,21 +753,24 @@ def updateSelectionLine(self, p2): def updateSelectionSquare(self, p2): p1 = self.current_selection[0] - self.selection_line.setData(x=[p1.x(), p1.x(), p2.x(), p2.x(), p1.x()], - y=[p1.y(), p2.y(), p2.y(), p1.y(), p1.y()]) + self.selection_line.setData( + x=[p1.x(), p1.x(), p2.x(), p2.x(), p1.x()], + y=[p1.y(), p2.y(), p2.y(), p1.y(), p1.y()], + ) self.selection_line.show() def _distance_pixels(self, p1, p2): xpixel, ypixel = self.viewPixelSize() dx = (p1.x() - p2.x()) / xpixel dy = (p1.y() - p2.y()) / ypixel - return (dx**2 + dy**2)**0.5 + return (dx**2 + dy**2) ** 0.5 def updateSelectionPolygon(self, p): first = self.current_selection[0] polygon = self.current_selection + [p] - self.selection_line.setData(x=[e.x() for e in polygon], - y=[e.y() for e in polygon]) + self.selection_line.setData( + x=[e.x() for e in polygon], y=[e.y() for e in polygon] + ) self.selection_line.show() if self._distance_pixels(first, p) < SELECT_POLYGON_TOLERANCE: self.selection_poly_marker.setData(x=[first.x()], y=[first.y()]) @@ -696,8 +780,9 @@ def updateSelectionPolygon(self, p): def keyPressEvent(self, ev): # cancel current selection process - if self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] and \ - ev.key() in [Qt.Key_Escape]: + if self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] and ev.key() in [ + Qt.Key_Escape + ]: self.set_mode_panning() ev.accept() else: @@ -709,16 +794,22 @@ def keyReleaseEvent(self, event): self.update_selection_tooltip(event.modifiers()) def mouseClickEvent(self, ev): - if ev.button() == Qt.RightButton and \ - (self.action == ZOOMING or self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON]): + if ev.button() == Qt.RightButton and ( + self.action == ZOOMING + or self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] + ): ev.accept() self.set_mode_panning() elif ev.button() == Qt.RightButton: ev.accept() self.autoRange() - if self.action != ZOOMING and self.action not in [SELECT, SELECT_SQUARE, SELECT_POLYGON] \ - and ev.button() == Qt.LeftButton and \ - hasattr(self.graph, "selection_type") and self.graph.selection_type: + if ( + self.action != ZOOMING + and self.action not in [SELECT, SELECT_SQUARE, SELECT_POLYGON] + and ev.button() == Qt.LeftButton + and hasattr(self.graph, "selection_type") + and self.graph.selection_type + ): pos = self.childGroup.mapFromParent(ev.pos()) self.graph.select_by_click(pos) ev.accept() @@ -732,11 +823,14 @@ def mouseClickEvent(self, ev): ax = self.childGroup.mapRectFromParent(ax) self.showAxRect(ax) self.axHistoryPointer += 1 - self.axHistory = self.axHistory[:self.axHistoryPointer] + [ax] + self.axHistory = self.axHistory[: self.axHistoryPointer] + [ax] self.set_mode_panning() ev.accept() - if self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] \ - and ev.button() == Qt.LeftButton and self.graph.selection_type: + if ( + self.action in [SELECT, SELECT_SQUARE, SELECT_POLYGON] + and ev.button() == Qt.LeftButton + and self.graph.selection_type + ): pos = self.childGroup.mapFromParent(ev.pos()) if self.current_selection is None: self.current_selection = [pos] @@ -788,7 +882,7 @@ def is_view_locked(self): return not all(a is None for a in self.fixed_range_x + self.fixed_range_y) def setRange(self, rect=None, xRange=None, yRange=None, **kwargs): - """ Always respect limitations in fixed_range_x and _y. """ + """Always respect limitations in fixed_range_x and _y.""" if not self.is_view_locked(): super().setRange(rect=rect, xRange=xRange, yRange=yRange, **kwargs) @@ -875,7 +969,6 @@ def set_mode_select_polygon(self): class InteractiveViewBoxC(InteractiveViewBox): - def __init__(self, graph): super().__init__(graph) self.y_padding = 0.02 @@ -903,11 +996,12 @@ class NoSuchCurve(ValueError): class CurvePlot(QWidget, OWComponent, SelectionGroupMixin): - sample_seed = Setting(0, schema_only=True) peak_labels_saved = Setting([], schema_only=True) feature_color = ContextSetting(None, exclude_attributes=True) - color_individual = Setting(False) # color individual curves (in a cycle) if no feature_color + color_individual = Setting( + False + ) # color individual curves (in a cycle) if no feature_color invertX = Setting(False) viewtype = Setting(INDIVIDUAL) waterfall = Setting(False) @@ -943,19 +1037,25 @@ def __init__(self, parent: OWWidget, select=SELECTNONE): self.line_width = 1 - axis_items = {"left": AxisItem("left"), - "bottom": AxisItem("bottom"), - "right": AxisItem("right", showValues=False), - "top": AxisItem("top", showValues=False)} + axis_items = { + "left": AxisItem("left"), + "bottom": AxisItem("bottom"), + "right": AxisItem("right", showValues=False), + "top": AxisItem("top", showValues=False), + } - self.plotview = PlotWidget(viewBox=InteractiveViewBoxC(self), axisItems=axis_items) + self.plotview = PlotWidget( + viewBox=InteractiveViewBoxC(self), axisItems=axis_items + ) self.plot = self.plotview.getPlotItem() self.plot.hideButtons() # hide the autorange button self.plot.setDownsampling(auto=True, mode="peak") # show the whole top and right axis (PyQtGraphs sets it to 0) self.plot.layout.setRowSpacing(0, max(self.plot.layout.rowSpacing(0), 1)) - self.plot.layout.setColumnMinimumWidth(2, max(self.plot.layout.columnMinimumWidth(2), 1)) + self.plot.layout.setColumnMinimumWidth( + 2, max(self.plot.layout.columnMinimumWidth(2), 1) + ) self.connected_views = [] self.plot.vb.sigResized.connect(self._update_connected_views) @@ -967,8 +1067,12 @@ def __init__(self, parent: OWWidget, select=SELECTNONE): self.hLine = pg.InfiniteLine(angle=0, movable=False) self.plot.scene().sigMouseMoved.connect(self.mouse_moved_viewhelpers) self.plot.scene().sigMouseMoved.connect(self.plot.vb.mouseMovedEvent) - self.proxy = pg.SignalProxy(self.plot.scene().sigMouseMoved, rateLimit=20, - slot=self.mouse_moved_closest, delay=0.1) + self.proxy = pg.SignalProxy( + self.plot.scene().sigMouseMoved, + rateLimit=20, + slot=self.mouse_moved_closest, + delay=0.1, + ) self.plot.vb.sigRangeChanged.connect(self.resized) self.plot.vb.sigResized.connect(self.resized) @@ -985,8 +1089,7 @@ def __init__(self, parent: OWWidget, select=SELECTNONE): self.curves_cont = PlotCurvesItem() self.important_decimals = 10, 10 - self.plot.scene().installEventFilter( - HelpEventDelegate(self.help_event, self)) + self.plot.scene().installEventFilter(HelpEventDelegate(self.help_event, self)) # whether to rescale at next update self.rescale_next = True @@ -1009,87 +1112,114 @@ def __init__(self, parent: OWWidget, select=SELECTNONE): self.layout().addWidget(self.plotview) # prepare interface according to the new context - self.parent.contextAboutToBeOpened.connect(lambda x: self.init_interface_data(x[0])) + self.parent.contextAboutToBeOpened.connect( + lambda x: self.init_interface_data(x[0]) + ) actions = [] resample_curves = QAction( - "Resample curves", self, shortcut=Qt.Key_R, - triggered=lambda x: self.resample_curves(self.sample_seed+1) + "Resample curves", + self, + shortcut=Qt.Key_R, + triggered=lambda x: self.resample_curves(self.sample_seed + 1), ) resample_curves.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(resample_curves) reset_curves = QAction( - "Resampling reset", self, shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_R), - triggered=lambda x: self.resample_curves(0) + "Resampling reset", + self, + shortcut=QKeySequence(Qt.ControlModifier | Qt.Key_R), + triggered=lambda x: self.resample_curves(0), ) reset_curves.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(reset_curves) - zoom_in = QAction( - "Zoom in", self, triggered=self.plot.vb.set_mode_zooming - ) + zoom_in = QAction("Zoom in", self, triggered=self.plot.vb.set_mode_zooming) zoom_in.setShortcuts([Qt.Key_Z, QKeySequence(QKeySequence.ZoomIn)]) zoom_in.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(zoom_in) zoom_fit = QAction( - "Zoom to fit", self, - triggered=lambda x: (self.plot.vb.autoRange(), self.plot.vb.set_mode_panning()) + "Zoom to fit", + self, + triggered=lambda x: ( + self.plot.vb.autoRange(), + self.plot.vb.set_mode_panning(), + ), + ) + zoom_fit.setShortcuts( + [Qt.Key_Backspace, QKeySequence(Qt.ControlModifier | Qt.Key_0)] ) - zoom_fit.setShortcuts([Qt.Key_Backspace, QKeySequence(Qt.ControlModifier | Qt.Key_0)]) zoom_fit.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(zoom_fit) rescale_y = QAction( - "Rescale Y to fit", self, shortcut=Qt.Key_D, - triggered=self.rescale_current_view_y + "Rescale Y to fit", + self, + shortcut=Qt.Key_D, + triggered=self.rescale_current_view_y, ) rescale_y.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(rescale_y) self.view_average_menu = QAction( - "Show averages", self, shortcut=Qt.Key_A, checkable=True, - triggered=lambda x: self.viewtype_changed() + "Show averages", + self, + shortcut=Qt.Key_A, + checkable=True, + triggered=lambda x: self.viewtype_changed(), ) self.view_average_menu.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(self.view_average_menu) self.view_waterfall_menu = QAction( - "Waterfall plot", self, shortcut=Qt.Key_W, checkable=True, - triggered=lambda x: self.waterfall_changed() + "Waterfall plot", + self, + shortcut=Qt.Key_W, + checkable=True, + triggered=lambda x: self.waterfall_changed(), ) self.view_waterfall_menu.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(self.view_waterfall_menu) self.show_grid_a = QAction( - "Show grid", self, shortcut=Qt.Key_G, checkable=True, - triggered=self.grid_changed + "Show grid", + self, + shortcut=Qt.Key_G, + checkable=True, + triggered=self.grid_changed, ) self.show_grid_a.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(self.show_grid_a) self.invertX_menu = QAction( - "Invert X", self, shortcut=Qt.Key_X, checkable=True, - triggered=self.invertX_changed + "Invert X", + self, + shortcut=Qt.Key_X, + checkable=True, + triggered=self.invertX_changed, ) self.invertX_menu.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(self.invertX_menu) single_peak = QAction( - "Add Peak Label", self, shortcut=Qt.Key_P, - triggered=self.add_peak_label + "Add Peak Label", self, shortcut=Qt.Key_P, triggered=self.add_peak_label ) actions.append(single_peak) single_peak.setShortcutContext(Qt.WidgetWithChildrenShortcut) if self.selection_type == SELECTMANY: select_curves = QAction( - "Select (line)", self, triggered=self.line_select_start, + "Select (line)", + self, + triggered=self.line_select_start, ) select_curves.setShortcuts([Qt.Key_S]) select_curves.setShortcutContext(Qt.WidgetWithChildrenShortcut) actions.append(select_curves) if self.saving_enabled: save_graph = QAction( - "Save graph", self, triggered=self.save_graph, + "Save graph", + self, + triggered=self.save_graph, ) save_graph.setShortcuts([QKeySequence(Qt.ControlModifier | Qt.Key_S)]) save_graph.setShortcutContext(Qt.WidgetWithChildrenShortcut) @@ -1110,8 +1240,11 @@ def __init__(self, parent: OWWidget, select=SELECTNONE): a.setShortcutVisibleInContextMenu(True) self.color_individual_menu = QAction( - "Color individual curves", self, shortcut=Qt.Key_I, checkable=True, - triggered=lambda x: self.color_individual_changed() + "Color individual curves", + self, + shortcut=Qt.Key_I, + checkable=True, + triggered=lambda x: self.color_individual_changed(), ) self.color_individual_menu.setShortcutContext(Qt.WidgetWithChildrenShortcut) self.color_individual_menu.setShortcutVisibleInContextMenu(True) @@ -1123,16 +1256,25 @@ def __init__(self, parent: OWWidget, select=SELECTNONE): choose_color_box.setFocusPolicy(Qt.TabFocus) label = gui.label(choose_color_box, self, "Color by") label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) - self.feature_color_model = DomainModel(DomainModel.METAS | DomainModel.CLASSES, - valid_types=(DiscreteVariable,), placeholder="None") + self.feature_color_model = DomainModel( + DomainModel.METAS | DomainModel.CLASSES, + valid_types=(DiscreteVariable,), + placeholder="None", + ) self.feature_color_combo = gui.comboBox( - choose_color_box, self, "feature_color", - callback=self.update_view, model=self.feature_color_model) + choose_color_box, + self, + "feature_color", + callback=self.update_view, + model=self.feature_color_model, + ) choose_color_box.setFocusProxy(self.feature_color_combo) choose_color_action.setDefaultWidget(choose_color_box) view_menu.addAction(choose_color_action) - cycle_colors = QShortcut(Qt.Key_C, self, self.cycle_color_attr, context=Qt.WidgetWithChildrenShortcut) + cycle_colors = QShortcut( # noqa: F841 + Qt.Key_C, self, self.cycle_color_attr, context=Qt.WidgetWithChildrenShortcut + ) self.waterfall_apply() self.grid_apply() @@ -1149,11 +1291,13 @@ def __init__(self, parent: OWWidget, select=SELECTNONE): self.parameter_setter = ParameterSetter(self) def _update_default_pens(self): - self.pen_mouse = pg.mkPen(color=(0, 0, 255), width=self.line_width+1) - pen_normal, pen_selected, pen_subset = self._generate_pens(QColor(60, 60, 60, 200), - QColor(200, 200, 200, 127), - QColor(0, 0, 0, 255), - width=self.line_width) + self.pen_mouse = pg.mkPen(color=(0, 0, 255), width=self.line_width + 1) + pen_normal, pen_selected, pen_subset = self._generate_pens( + QColor(60, 60, 60, 200), + QColor(200, 200, 200, 127), + QColor(0, 0, 0, 255), + width=self.line_width, + ) self._default_pen_selected = pen_selected self.pen_normal = defaultdict(lambda: pen_normal) self.pen_subset = defaultdict(lambda: pen_subset) @@ -1175,7 +1319,9 @@ def _create_legend(self): def init_interface_data(self, data): domain = data.domain if data is not None else None self.feature_color_model.set_domain(domain) - self.feature_color = self.feature_color_model[0] if self.feature_color_model else None + self.feature_color = ( + self.feature_color_model[0] if self.feature_color_model else None + ) def line_select_start(self): if self.viewtype == INDIVIDUAL and self.waterfall is False: @@ -1189,7 +1335,8 @@ def help_event(self, ev): variables = self.data.domain.metas + self.data.domain.class_vars text += "".join( '{} = {}\n'.format(attr.name, self.data[index, attr]) - for attr in variables) + for attr in variables + ) elif self.viewtype == AVERAGE: c = self.multiple_curves_info[self.highlighted] nc = sum(c[2]) @@ -1202,8 +1349,7 @@ def help_event(self, ev): text += "{} curves".format(nc) if text: text = text.rstrip() - text = ('{}' - .format(escape(text))) + text = '{}'.format(escape(text)) QToolTip.showText(ev.screenPos(), text, widget=self.plotview) return True else: @@ -1329,7 +1475,7 @@ def resized(self): try: vr = self.plot.vb.viewRect() - except: + except: # noqa: E722 return if self.invertX: @@ -1344,7 +1490,9 @@ def make_selection(self, data_indices): redraw_curve_indices = set() def current_selection(): - return set(icurve for idata, icurve in invd.items() if self.selection_group[idata]) + return set( + icurve for idata, icurve in invd.items() if self.selection_group[idata] + ) old_sel_ci = current_selection() @@ -1367,20 +1515,23 @@ def current_selection(): if data_indices is not None: self.selection_group[data_indices] = selnum redraw_curve_indices.update( - icurve for idata, icurve in invd.items() if idata in data_indices_set) + icurve for idata, icurve in invd.items() if idata in data_indices_set + ) changed = True fixes = self.make_selection_valid() if fixes: changed = True redraw_curve_indices.update( - icurve for idata, icurve in invd.items() if idata in fixes) + icurve for idata, icurve in invd.items() if idata in fixes + ) new_sel_ci = current_selection() # redraw whole selection if it increased or decreased over the threshold - if len(old_sel_ci) <= MAX_THICK_SELECTED < len(new_sel_ci) or \ - len(old_sel_ci) > MAX_THICK_SELECTED >= len(new_sel_ci): + if len(old_sel_ci) <= MAX_THICK_SELECTED < len(new_sel_ci) or len( + old_sel_ci + ) > MAX_THICK_SELECTED >= len(new_sel_ci): redraw_curve_indices.update(old_sel_ci) self.set_curve_pens(redraw_curve_indices) @@ -1388,9 +1539,12 @@ def current_selection(): self.selection_changed_confirm() def make_selection_valid(self): - """ Make the selection valid and return the changed positions. """ - if self.select_at_least_1 and not len(np.flatnonzero(self.selection_group)) \ - and len(self.data) > 0: # no selection + """Make the selection valid and return the changed positions.""" + if ( + self.select_at_least_1 + and not len(np.flatnonzero(self.selection_group)) + and len(self.data) > 0 + ): # no selection self.selection_group[0] = 1 return set([0]) return set() @@ -1422,7 +1576,7 @@ def mouse_moved_viewhelpers(self, pos): posx, posy = mousePoint.x(), mousePoint.y() labels = [] - for a, vs in sorted(self.reports.items()): + for _a, vs in sorted(self.reports.items()): for v in vs: if isinstance(v, tuple) and len(v) == 2: if v[0] == "x": @@ -1433,8 +1587,11 @@ def mouse_moved_viewhelpers(self, pos): self.crosshair_hidden = bool(labels) if self.location and not labels: - labels = strdec(posx, self.important_decimals[0]) + " " + \ - strdec(posy, self.important_decimals[1]) + labels = ( + strdec(posx, self.important_decimals[0]) + + " " + + strdec(posy, self.important_decimals[1]) + ) self.label.setText(labels, color=(0, 0, 0)) self.vLine.setPos(posx) @@ -1445,8 +1602,11 @@ def mouse_moved_viewhelpers(self, pos): def mouse_moved_closest(self, evt): pos = evt[0] - if self.plot.sceneBoundingRect().contains(pos) and \ - self.curves and len(self.curves[0][0]): # need non-zero x axis! + if ( + self.plot.sceneBoundingRect().contains(pos) + and self.curves + and len(self.curves[0][0]) + ): # need non-zero x axis! mousePoint = self.plot.vb.mapSceneToView(pos) posx, posy = mousePoint.x(), mousePoint.y() @@ -1454,8 +1614,15 @@ def mouse_moved_closest(self, evt): bd = None if self.markclosest and self.plot.vb.action != ZOOMING: xpixel, ypixel = self.plot.vb.viewPixelSize() - distances = distancetocurves(self.curves[0], posx, posy, xpixel, ypixel, - r=self.MOUSE_RADIUS, cache=cache) + distances = distancetocurves( + self.curves[0], + posx, + posy, + xpixel, + ypixel, + r=self.MOUSE_RADIUS, + cache=cache, + ) try: mindi = np.nanargmin(distances) if distances[mindi] < self.MOUSE_RADIUS: @@ -1501,8 +1668,10 @@ def highlight_index_in_data(self, index, emit): def _set_selection_pen_width(self): n_selected = np.count_nonzero(self.selection_group[self.sampled_indices]) use_thick = n_selected <= MAX_THICK_SELECTED - for v in itertools.chain(self.pen_selected.values(), [self._default_pen_selected]): - v.setWidth(self.line_width+1 if use_thick else self.line_width) + for v in itertools.chain( + self.pen_selected.values(), [self._default_pen_selected] + ): + v.setWidth(self.line_width + 1 if use_thick else self.line_width) def set_line_width(self, width): self.line_width = width @@ -1525,7 +1694,7 @@ def set_curve_pen(self, idc): value = idc % len(self._color_individual_cycle) self.curves_cont.objs[idc].setPen(thispen[value]) # to always draw selected above subset, multiply with 2 - self.curves_cont.objs[idc].setZValue(int(insubset) + 2*int(inselected)) + self.curves_cont.objs[idc].setZValue(int(insubset) + 2 * int(inselected)) def set_curve_pens(self, curves=None): if self.viewtype == INDIVIDUAL and self.curves: @@ -1564,16 +1733,20 @@ def clear_connect_views(self): def _compute_sample(self, ys): if len(ys) > MAX_INSTANCES_DRAWN: - sample_selection = \ - random.Random(self.sample_seed).sample(range(len(ys)), MAX_INSTANCES_DRAWN) + sample_selection = random.Random(self.sample_seed).sample( + range(len(ys)), MAX_INSTANCES_DRAWN + ) # with random selection also show at most MAX_INSTANCES_DRAW elements from the subset subset = set(np.where(self.subset_indices)[0]) subset_to_show = subset - set(sample_selection) - subset_additional = MAX_INSTANCES_DRAWN - (len(subset) - len(subset_to_show)) + subset_additional = MAX_INSTANCES_DRAWN - ( + len(subset) - len(subset_to_show) + ) if len(subset_to_show) > subset_additional: - subset_to_show = \ - random.Random(self.sample_seed).sample(sorted(subset_to_show), subset_additional) + subset_to_show = random.Random(self.sample_seed).sample( + sorted(subset_to_show), subset_additional + ) sampled_indices = sorted(sample_selection + list(subset_to_show)) else: sampled_indices = list(range(len(ys))) @@ -1621,21 +1794,28 @@ def set_pen_colors(self): palette, legend = False, False if color_var is not None: discrete_palette = color_var.palette - palette = [(v, discrete_palette[color_var.to_val(v)]) for v in color_var.values] + palette = [ + (v, discrete_palette[color_var.to_val(v)]) for v in color_var.values + ] legend = True elif self.color_individual: - palette = [(i, QColor(*c)) for i, c in enumerate(self._color_individual_cycle)] + palette = [ + (i, QColor(*c)) for i, c in enumerate(self._color_individual_cycle) + ] if palette: for v, color in palette: color = QColor(color) # copy color color.setAlphaF(0.9) - self.pen_normal[v], self.pen_selected[v], self.pen_subset[v] = \ + self.pen_normal[v], self.pen_selected[v], self.pen_subset[v] = ( self._generate_pens(color, width=self.line_width) + ) pen = pg.mkPen(color=color) brush = pg.mkBrush(color=color) if legend: self.legend.addItem( - pg.ScatterPlotItem(pen=pen, brush=brush, size=10, symbol="o"), escape(v)) + pg.ScatterPlotItem(pen=pen, brush=brush, size=10, symbol="o"), + escape(v), + ) self.legend.setVisible(bool(self.legend.items)) def show_individual(self): @@ -1651,15 +1831,35 @@ def rescale_current_view_y(self): bleft = qrect.left() bright = qrect.right() - maxcurve = [np.nanmax(ys[:, np.searchsorted(x, bleft): - np.searchsorted(x, bright, side="right")]) - for x, ys in self.curves_plotted if len(x)] - mincurve = [np.nanmin(ys[:, np.searchsorted(x, bleft): - np.searchsorted(x, bright, side="right")]) - for x, ys in self.curves_plotted if len(x)] + maxcurve = [ + np.nanmax( + ys[ + :, + np.searchsorted(x, bleft) : np.searchsorted( + x, bright, side="right" + ), + ] + ) + for x, ys in self.curves_plotted + if len(x) + ] + mincurve = [ + np.nanmin( + ys[ + :, + np.searchsorted(x, bleft) : np.searchsorted( + x, bright, side="right" + ), + ] + ) + for x, ys in self.curves_plotted + if len(x) + ] # if all values are nan there is nothing to do - if bottleneck.allnan(maxcurve): # allnan(mincurve) would obtain the same result + if bottleneck.allnan( + maxcurve + ): # allnan(mincurve) would obtain the same result return ymax = np.nanmax(maxcurve) @@ -1751,7 +1951,9 @@ def select_by_click(self, _): self.make_selection(None) def select_line(self, startp, endp): - intersected = self.intersect_curves((startp.x(), startp.y()), (endp.x(), endp.y())) + intersected = self.intersect_curves( + (startp.x(), startp.y()), (endp.x(), endp.y()) + ) self.make_selection(intersected if len(intersected) else None) def intersect_curves(self, q1, q2): @@ -1763,7 +1965,9 @@ def intersect_curves(self, q1, q2): xmax = closestindex(x, x2, side="right") xmin = max(0, xmin - 1) xmax = xmax + 2 - sel = np.flatnonzero(intersect_curves_chunked(x, ys, self.data_xsind, q1, q2, xmin, xmax)) + sel = np.flatnonzero( + intersect_curves_chunked(x, ys, self.data_xsind, q1, q2, xmin, xmax) + ) return sel def shutdown(self): @@ -1776,7 +1980,9 @@ def migrate_settings_sub(cls, settings, version): if "selected_indices" in settings: # transform into list-of-tuples as we do not have data size if settings["selected_indices"]: - settings["selection_group_saved"] = [(a, 1) for a in settings["selected_indices"]] + settings["selection_group_saved"] = [ + (a, 1) for a in settings["selected_indices"] + ] @classmethod def migrate_context_sub_feature_color(cls, values, version): @@ -1800,8 +2006,10 @@ class Outputs(SelectionOutputsMixin.Outputs): icon = "icons/spectra.svg" priority = 10 - replaces = ["orangecontrib.infrared.widgets.owcurves.OWCurves", - "orangecontrib.infrared.widgets.owspectra.OWSpectra"] + replaces = [ + "orangecontrib.infrared.widgets.owcurves.OWCurves", + "orangecontrib.infrared.widgets.owspectra.OWSpectra", + ] keywords = ["curves", "lines", "spectrum"] want_control_area = False @@ -1812,7 +2020,9 @@ class Outputs(SelectionOutputsMixin.Outputs): curveplot = SettingProvider(CurvePlot) visual_settings = Setting({}, schema_only=True) - graph_name = "curveplot.plotview" # need to be defined for the save button to be shown + graph_name = ( + "curveplot.plotview" # need to be defined for the save button to be shown + ) class Information(SelectionOutputsMixin.Information): showing_sample = Msg("Showing {} of {} curves.") @@ -1829,7 +2039,8 @@ def __init__(self): self.curveplot.selection_changed.connect(self.selection_changed) self.curveplot.new_sampling.connect(self._showing_sample_info) self.curveplot.locked_axes_changed.connect( - lambda locked: self.Information.view_locked(shown=locked)) + lambda locked: self.Information.view_locked(shown=locked) + ) self.mainArea.layout().addWidget(self.curveplot) self.resize(900, 700) VisualSettingsDialog(self, self.curveplot.parameter_setter.initial_settings) @@ -1857,8 +2068,7 @@ def handleNewSignals(self): self.curveplot.update_view() def selection_changed(self): - self.send_selection(self.curveplot.data, - self.curveplot.selection_group) + self.send_selection(self.curveplot.data, self.curveplot.selection_group) def _showing_sample_info(self, num): if num is not None and self.curveplot.data and num != len(self.curveplot.data): @@ -1881,9 +2091,11 @@ def onDeleteWidget(self): def migrate_to_visual_settings(cls, settings): if "curveplot" in settings: cs = settings["curveplot"] - translate = {'label_title': ('Annotations', 'Title', 'Title'), - 'label_xaxis': ('Annotations', 'x-axis title', 'Title'), - 'label_yaxis': ('Annotations', 'y-axis title', 'Title')} + translate = { + 'label_title': ('Annotations', 'Title', 'Title'), + 'label_xaxis': ('Annotations', 'x-axis title', 'Title'), + 'label_yaxis': ('Annotations', 'y-axis title', 'Title'), + } for from_, to_ in translate.items(): if cs.get(from_): @@ -1905,11 +2117,13 @@ def migrate_settings(cls, settings, version): @classmethod def migrate_context(cls, context, version): if version <= 1 and "curveplot" in context.values: - CurvePlot.migrate_context_sub_feature_color(context.values["curveplot"], version) + CurvePlot.migrate_context_sub_feature_color( + context.values["curveplot"], version + ) if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview + collagen = Orange.data.Table("collagen.csv") - WidgetPreview(OWSpectra).run(set_data=collagen, - set_subset=collagen[:40]) + WidgetPreview(OWSpectra).run(set_data=collagen, set_subset=collagen[:40]) diff --git a/orangecontrib/spectroscopy/widgets/owspectralseries.py b/orangecontrib/spectroscopy/widgets/owspectralseries.py index fae8d3f50..0e764fd31 100644 --- a/orangecontrib/spectroscopy/widgets/owspectralseries.py +++ b/orangecontrib/spectroscopy/widgets/owspectralseries.py @@ -1,7 +1,13 @@ from xml.sax.saxutils import escape -from AnyQt.QtWidgets import QWidget, QPushButton, QGridLayout, QVBoxLayout, QWidgetAction, \ - QToolTip +from AnyQt.QtWidgets import ( + QWidget, + QPushButton, + QGridLayout, + QVBoxLayout, + QWidgetAction, + QToolTip, +) from AnyQt.QtCore import Qt, QRectF @@ -14,25 +20,41 @@ from Orange.data import Table, Domain, DiscreteVariable, ContinuousVariable from Orange.widgets.widget import OWWidget, Msg, OWComponent, Input from Orange.widgets import gui -from Orange.widgets.settings import \ - Setting, ContextSetting, DomainContextHandler, SettingProvider +from Orange.widgets.settings import ( + Setting, + ContextSetting, + DomainContextHandler, + SettingProvider, +) from Orange.widgets.utils.itemmodels import DomainModel from Orange.widgets.visualize.utils.plotutils import GraphicsView, PlotItem from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.utils import values_to_linspace, index_values -from orangecontrib.spectroscopy.widgets.owhyper import _shift, \ - ImageColorSettingMixin, ImageZoomMixin, ImageItemNan, ImageColorLegend -from orangecontrib.spectroscopy.widgets.owspectra import HelpEventDelegate, \ - selection_modifiers, \ - SELECTMANY, INDIVIDUAL, InteractiveViewBox, MenuFocus -from orangecontrib.spectroscopy.widgets.utils import \ - SelectionGroupMixin, SelectionOutputsMixin - - -class LineScanPlot(QWidget, OWComponent, SelectionGroupMixin, - ImageColorSettingMixin, ImageZoomMixin): - +from orangecontrib.spectroscopy.widgets.owhyper import ( + _shift, + ImageColorSettingMixin, + ImageZoomMixin, + ImageItemNan, + ImageColorLegend, +) +from orangecontrib.spectroscopy.widgets.owspectra import ( + HelpEventDelegate, + selection_modifiers, + SELECTMANY, + INDIVIDUAL, + InteractiveViewBox, + MenuFocus, +) +from orangecontrib.spectroscopy.widgets.utils import ( + SelectionGroupMixin, + SelectionOutputsMixin, +) + + +class LineScanPlot( + QWidget, OWComponent, SelectionGroupMixin, ImageColorSettingMixin, ImageZoomMixin +): attr_x = ContextSetting(None, exclude_attributes=True) gamma = Setting(0) @@ -64,8 +86,7 @@ def __init__(self, parent): self.legend = ImageColorLegend() ci.addItem(self.legend) - self.plot.scene().installEventFilter( - HelpEventDelegate(self.help_event, self)) + self.plot.scene().installEventFilter(HelpEventDelegate(self.help_event, self)) layout = QVBoxLayout() self.setLayout(layout) @@ -89,23 +110,36 @@ def __init__(self, parent): self.button.setMenu(view_menu) # prepare interface according to the new context - self.parent.contextAboutToBeOpened.connect(lambda x: self.init_interface_data(x[0])) + self.parent.contextAboutToBeOpened.connect( + lambda x: self.init_interface_data(x[0]) + ) self.add_zoom_actions(view_menu) common_options = dict( - labelWidth=50, orientation=Qt.Horizontal, sendSelectedValue=True, - valueType=str) + labelWidth=50, + orientation=Qt.Horizontal, + sendSelectedValue=True, + valueType=str, + ) choose_xy = QWidgetAction(self) box = gui.vBox(self) box.setFocusPolicy(Qt.TabFocus) - self.xy_model = DomainModel(DomainModel.METAS | DomainModel.CLASSES, - valid_types=DomainModel.PRIMITIVE, - placeholder="Position (index)") + self.xy_model = DomainModel( + DomainModel.METAS | DomainModel.CLASSES, + valid_types=DomainModel.PRIMITIVE, + placeholder="Position (index)", + ) self.cb_attr_x = gui.comboBox( - box, self, "attr_x", label="Axis x:", callback=self.update_attr, - model=self.xy_model, **common_options) + box, + self, + "attr_x", + label="Axis x:", + callback=self.update_attr, + model=self.xy_model, + **common_options, + ) box.setFocusProxy(self.cb_attr_x) @@ -131,15 +165,17 @@ def help_event(self, ev): if sel is not None: prepared.append(str(self.wavenumbers[wavenumber_ind])) for d in self.data[sel]: - variables = [v for v in self.data.domain.metas + self.data.domain.class_vars - if v not in [self.attr_x]] + variables = [ + v + for v in self.data.domain.metas + self.data.domain.class_vars + if v not in [self.attr_x] + ] features = ['{} = {}'.format(attr.name, d[attr]) for attr in variables] features.append('value = {}'.format(d[wavenumber_ind])) prepared.append("\n".join(features)) text = "\n\n".join(prepared) if text: - text = ('{}' - .format(escape(text))) + text = '{}'.format(escape(text)) QToolTip.showText(ev.screenPos(), text, widget=self.plotview) return True else: @@ -192,7 +228,7 @@ def update_view(self): imdata = np.ones((lsy[2], lsx[2])) * float("nan") xindex = index_values(coorx, lsx) yindex = index_values(wavenumbers, lsy) - for xind, d in zip(xindex, self.data.X): + for xind, d in zip(xindex, self.data.X, strict=False): imdata[yindex, xind] = d self.data_imagepixels = xindex @@ -206,8 +242,8 @@ def update_view(self): shifty = _shift(lsy) left = lsx[0] - shiftx bottom = lsy[0] - shifty - width = (lsx[1]-lsx[0]) + 2*shiftx - height = (lsy[1]-lsy[0]) + 2*shifty + width = (lsx[1] - lsx[0]) + 2 * shiftx + height = (lsy[1] - lsy[0]) + 2 * shifty self.img.setRect(QRectF(left, bottom, width, height)) self.refresh_img_selection() @@ -240,7 +276,7 @@ def _points_at_pos(self, pos): if self.data and self.lsx and self.lsy: x, y = pos.x(), pos.y() x_distance = np.abs(self.data_xs - x) - sel = (x_distance < _shift(self.lsx)) + sel = x_distance < _shift(self.lsx) wavenumber_distance = np.abs(self.wavenumbers - y) wavenumber_ind = np.argmin(wavenumber_distance) return sel, wavenumber_ind @@ -302,8 +338,11 @@ def set_data(self, data): def valid_context(data): if data is None: return False - annotation_features = [v for v in data.domain.metas + data.domain.class_vars - if isinstance(v, (DiscreteVariable, ContinuousVariable))] + annotation_features = [ + v + for v in data.domain.metas + data.domain.class_vars + if isinstance(v, (DiscreteVariable, ContinuousVariable)) + ] return len(annotation_features) >= 1 if valid_context(data): @@ -324,4 +363,5 @@ def migrate_settings(cls, settings, version): if __name__ == "__main__": # pragma: no cover from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWSpectralSeries).run(Table("collagen")) diff --git a/orangecontrib/spectroscopy/widgets/owstackalign.py b/orangecontrib/spectroscopy/widgets/owstackalign.py index 8ccdfb0ef..cc529d45e 100644 --- a/orangecontrib/spectroscopy/widgets/owstackalign.py +++ b/orangecontrib/spectroscopy/widgets/owstackalign.py @@ -18,10 +18,15 @@ from orangecontrib.spectroscopy.data import getx, build_spec_table from orangecontrib.spectroscopy.io.util import _spectra_from_image from orangecontrib.spectroscopy.widgets.gui import lineEditIntRange -from orangecontrib.spectroscopy.utils import NanInsideHypercube, InvalidAxisException, \ - get_hypercube +from orangecontrib.spectroscopy.utils import ( + NanInsideHypercube, + InvalidAxisException, + get_hypercube, +) from orangecontrib.spectroscopy.preprocess.utils import WrongReferenceException -from orangecontrib.spectroscopy.utils.skimage._phase_cross_correlation import phase_cross_correlation +from orangecontrib.spectroscopy.utils.skimage._phase_cross_correlation import ( + phase_cross_correlation, +) from orangecontrib.spectroscopy.widgets.owspectra import InteractiveViewBox @@ -46,12 +51,12 @@ def shift_fill(img, sh, fill=np.nan): (u, v) = img.shape shifty = int(round(sh[0])) - aligned[:max(0, shifty), :] = fill - aligned[min(u, u+shifty):, :] = fill + aligned[: max(0, shifty), :] = fill + aligned[min(u, u + shifty) :, :] = fill shiftx = int(round(sh[1])) - aligned[:, :max(0, shiftx)] = fill - aligned[:, min(v, v+shiftx):] = fill + aligned[:, : max(0, shiftx)] = fill + aligned[:, min(v, v + shiftx) :] = fill return aligned @@ -130,15 +135,16 @@ def process_stack( ymin, ymax = int(round(ymin)), int(round(ymax)) shape = hypercube.shape - slicex = slice(max(xmax, 0), min(shape[1], shape[1]+xmin)) - slicey = slice(max(ymax, 0), min(shape[0], shape[0]+ymin)) + slicex = slice(max(xmax, 0), min(shape[1], shape[1] + xmin)) + slicey = slice(max(ymax, 0), min(shape[0], shape[0] + ymin)) cropped = np.array(aligned_stack).T[slicey, slicex] # transform numpy array back to Orange.data.Table - return shifts, build_spec_table(*_spectra_from_image(cropped, - getx(data), - np.linspace(*lsx)[slicex], - np.linspace(*lsy)[slicey])) + return shifts, build_spec_table( + *_spectra_from_image( + cropped, getx(data), np.linspace(*lsx)[slicex], np.linspace(*lsy)[slicey] + ) + ) class OWStackAlign(OWWidget): @@ -146,8 +152,7 @@ class OWStackAlign(OWWidget): name = "Align Stack" # Short widget description - description = ( - "Aligns and crops a stack of images using various methods.") + description = "Aligns and crops a stack of images using various methods." icon = "icons/stackalign.svg" @@ -188,16 +193,30 @@ def __init__(self): # TODO: add input box for selecting which should be the reference frame box = gui.widgetBox(self.controlArea, "Axes") - common_options = dict(labelWidth=50, orientation=Qt.Horizontal, - sendSelectedValue=True) - self.xy_model = DomainModel(DomainModel.METAS | DomainModel.CLASSES, - valid_types=ContinuousVariable) + common_options = dict( + labelWidth=50, orientation=Qt.Horizontal, sendSelectedValue=True + ) + self.xy_model = DomainModel( + DomainModel.METAS | DomainModel.CLASSES, valid_types=ContinuousVariable + ) self.cb_attr_x = gui.comboBox( - box, self, "attr_x", label="Axis x:", callback=self._update_attr, - model=self.xy_model, **common_options) + box, + self, + "attr_x", + label="Axis x:", + callback=self._update_attr, + model=self.xy_model, + **common_options, + ) self.cb_attr_y = gui.comboBox( - box, self, "attr_y", label="Axis y:", callback=self._update_attr, - model=self.xy_model, **common_options) + box, + self, + "attr_y", + label="Axis y:", + callback=self._update_attr, + model=self.xy_model, + **common_options, + ) self.contextAboutToBeOpened.connect(self._init_interface_data) @@ -212,9 +231,13 @@ def __init__(self): box = gui.widgetBox(self.controlArea, "Parameters") - gui.checkBox(box, self, "sobel_filter", - label="Use sobel filter", - callback=self._sobel_changed) + gui.checkBox( + box, + self, + "sobel_filter", + label="Use sobel filter", + callback=self._sobel_changed, + ) gui.separator(box) hbox1 = gui.hBox(box) self.le_upscale = lineEditIntRange( @@ -223,8 +246,14 @@ def __init__(self): hbox1.layout().addWidget(QLabel("Upscale factor:", self)) hbox1.layout().addWidget(self.le_upscale) - self.le1 = lineEditIntRange(box, self, "ref_frame_num", bottom=1, default=1, - callback=self._ref_frame_changed) + self.le1 = lineEditIntRange( + box, + self, + "ref_frame_num", + bottom=1, + default=1, + callback=self._ref_frame_changed, + ) hbox2 = gui.hBox(box) hbox2.layout().addWidget(QLabel("Reference frame:", self)) hbox2.layout().addWidget(self.le1) @@ -273,8 +302,7 @@ def _init_attr_values(self, data): domain = data.domain if data is not None else None self.xy_model.set_domain(domain) self.attr_x = self.xy_model[0] if self.xy_model else None - self.attr_y = self.xy_model[1] if len(self.xy_model) >= 2 \ - else self.attr_x + self.attr_y = self.xy_model[1] if len(self.xy_model) >= 2 else self.attr_x def _init_interface_data(self, args): data = args[0] @@ -316,16 +344,27 @@ def commit(self): self.Error.wrong_reference.clear() self.plotview.plotItem.clear() - if not (self.data and len(self.data.domain.attributes) and self.attr_x and self.attr_y): + if not ( + self.data + and len(self.data.domain.attributes) + and self.attr_x + and self.attr_y + ): pass elif self.attr_x == self.attr_y: self.Error.invalid_axis("same values") else: try: refdata = self.refdata if self.use_refinput else None - shifts, new_stack = process_stack(self.data, self.attr_x, self.attr_y, - upsample_factor=self.upscale_factor, use_sobel=self.sobel_filter, - ref_frame_num=self.ref_frame_num-1, refdata=refdata) + shifts, new_stack = process_stack( + self.data, + self.attr_x, + self.attr_y, + upsample_factor=self.upscale_factor, + use_sobel=self.sobel_filter, + ref_frame_num=self.ref_frame_num - 1, + refdata=refdata, + ) except NanInsideHypercube as e: self.Error.nan_in_image(e.args[0]) except InvalidAxisException as e: @@ -334,29 +373,41 @@ def commit(self): self.Error.wrong_reference(e.args[0]) else: frames = np.linspace(1, shifts.shape[0], shifts.shape[0]) - self.plotview.plotItem.plot(frames, shifts[:, 0], - pen=pg.mkPen(color=(255, 40, 0), width=3), - symbol='o', symbolBrush=(255, 40, 0), symbolPen='w', - symbolSize=7) - self.plotview.plotItem.plot(frames, shifts[:, 1], - pen=pg.mkPen(color=(0, 139, 139), width=3), - symbol='o', symbolBrush=(0, 139, 139), symbolPen='w', - symbolSize=7) + self.plotview.plotItem.plot( + frames, + shifts[:, 0], + pen=pg.mkPen(color=(255, 40, 0), width=3), + symbol='o', + symbolBrush=(255, 40, 0), + symbolPen='w', + symbolSize=7, + ) + self.plotview.plotItem.plot( + frames, + shifts[:, 1], + pen=pg.mkPen(color=(0, 139, 139), width=3), + symbol='o', + symbolBrush=(0, 139, 139), + symbolPen='w', + symbolSize=7, + ) self.plotview.getPlotItem().setLabel('bottom', 'Frame number') self.plotview.getPlotItem().setLabel('left', 'Shift / pixel') - self.plotview.getPlotItem().addLine(self.ref_frame_num, - pen=pg.mkPen(color=(150, 150, 150), width=3, - style=Qt.DashDotDotLine)) + self.plotview.getPlotItem().addLine( + self.ref_frame_num, + pen=pg.mkPen( + color=(150, 150, 150), width=3, style=Qt.DashDotDotLine + ), + ) self.Outputs.newstack.send(new_stack) def send_report(self): - self.report_items(( - ("Use sobel filter", str(self.sobel_filter)), - )) + self.report_items((("Use sobel filter", str(self.sobel_filter)),)) if __name__ == "__main__": # pragma: no cover from orangecontrib.spectroscopy.tests.test_owalignstack import stxm_diamond from Orange.widgets.utils.widgetpreview import WidgetPreview + WidgetPreview(OWStackAlign).run(set_data=stxm_diamond) diff --git a/orangecontrib/spectroscopy/widgets/owtilefile.py b/orangecontrib/spectroscopy/widgets/owtilefile.py index 99d7249be..b9ecbac65 100644 --- a/orangecontrib/spectroscopy/widgets/owtilefile.py +++ b/orangecontrib/spectroscopy/widgets/owtilefile.py @@ -7,20 +7,32 @@ import numpy as np from AnyQt.QtCore import Qt, QSize -from AnyQt.QtWidgets import QApplication -from AnyQt.QtWidgets import \ - QStyle, QComboBox, QMessageBox, QGridLayout, QLabel, \ - QSizePolicy as Policy, QCompleter +from AnyQt.QtWidgets import ( + QStyle, + QComboBox, + QMessageBox, + QGridLayout, + QLabel, + QSizePolicy as Policy, + QCompleter, +) from Orange.data.io import FileFormat, UrlReader, class_from_qualified_name from Orange.data.table import Table from Orange.preprocess.preprocess import Preprocess, PreprocessorList from Orange.widgets import widget, gui from Orange.widgets.data.owfile import NamedURLModel, LineEditSelectOnFocus, add_origin -from Orange.widgets.settings import Setting, ContextSetting, \ - PerfectDomainContextHandler, SettingProvider +from Orange.widgets.settings import ( + Setting, + ContextSetting, + PerfectDomainContextHandler, + SettingProvider, +) from Orange.widgets.utils.domaineditor import DomainEditor -from Orange.widgets.utils.filedialogs import RecentPathsWComboMixin, open_filename_dialog +from Orange.widgets.utils.filedialogs import ( + RecentPathsWComboMixin, + open_filename_dialog, +) from Orange.widgets.widget import MultiInput, Msg, Output # Backward compatibility (from owfile): class RecentPath used to be defined in this module, @@ -33,12 +45,15 @@ log = logging.getLogger(__name__) + class OWTilefile(widget.OWWidget, RecentPathsWComboMixin): name = "Tile File" id = "orangecontrib.spectroscopy.widgets.tilefile" icon = "icons/tilefile.svg" - description = "Read data tile-by-tile from input files, " \ - "preprocess, and send a data table to the output." + description = ( + "Read data tile-by-tile from input files, " + "preprocess, and send a data table to the output." + ) priority = 10000 replaces = ["orangecontrib.protospec.widgets.owtilefile.OWTilefile"] @@ -46,8 +61,9 @@ class Inputs: preprocessor = MultiInput("Preprocessor", Preprocess) class Outputs: - data = Output("Data", Table, - doc="Preprocessed dataset read from the input files.") + data = Output( + "Data", Table, doc="Preprocessed dataset read from the input files." + ) want_main_area = False @@ -66,9 +82,11 @@ class Outputs: variables: list # Overload RecentPathsWidgetMixin.recent_paths to set defaults - recent_paths = Setting([ - RecentPath("", "sample-datasets", "agilent/5_mosaic_agg1024.dmt"), - ]) + recent_paths = Setting( + [ + RecentPath("", "sample-datasets", "agilent/5_mosaic_agg1024.dmt"), + ] + ) recent_urls = Setting([]) source = Setting(LOCAL_FILE) xls_sheet = ContextSetting("") @@ -80,15 +98,15 @@ class Outputs: domain_editor = SettingProvider(DomainEditor) class Warning(widget.OWWidget.Warning): - no_preprocessor = Msg("No preprocessor on input." - " Press Reload to load anyway.") - new_preprocessor = Msg("Preprocessor has changed." - " Press Reload to apply.") - file_too_big = widget.Msg("The file is too large to load automatically." - " Press Reload to load.") + no_preprocessor = Msg("No preprocessor on input. Press Reload to load anyway.") + new_preprocessor = Msg("Preprocessor has changed. Press Reload to apply.") + file_too_big = widget.Msg( + "The file is too large to load automatically. Press Reload to load." + ) load_warning = widget.Msg("Read warning:\n{}") performance_warning = widget.Msg( - "Categorical variables with >100 values may decrease performance.") + "Categorical variables with >100 values may decrease performance." + ) class Error(widget.OWWidget.Error): missing_reader = Msg("No tile-by-tile reader for this file.") @@ -101,9 +119,9 @@ class NoFileSelected: UserAdviceMessages = [ widget.Message( - "Connect a Preprocessor " - "which results in data-reduction ", - "to best make use of this widget."), + "Connect a Preprocessor which results in data-reduction ", + "to best make use of this widget.", + ), ] def __init__(self): @@ -118,8 +136,15 @@ def __init__(self): layout = QGridLayout() gui.widgetBox(self.controlArea, margin=0, orientation=layout) - vbox = gui.radioButtons(None, self, "source", box=True, addSpace=True, - callback=self.load_data, addToLayout=False) + vbox = gui.radioButtons( + None, + self, + "source", + box=True, + addSpace=True, + callback=self.load_data, + addToLayout=False, + ) rb_button = gui.appendRadioButton(vbox, "File:", addToLayout=False) layout.addWidget(rb_button, 0, 0, Qt.AlignVCenter) @@ -132,32 +157,33 @@ def __init__(self): layout.addWidget(box, 0, 1) file_button = gui.button( - None, self, '...', callback=self.browse_file, autoDefault=False) + None, self, '...', callback=self.browse_file, autoDefault=False + ) file_button.setIcon(self.style().standardIcon(QStyle.SP_DirOpenIcon)) file_button.setSizePolicy(Policy.Maximum, Policy.Fixed) layout.addWidget(file_button, 0, 2) reload_button = gui.button( - None, self, "Reload", callback=self.load_data, autoDefault=False) - reload_button.setIcon(self.style().standardIcon( - QStyle.SP_BrowserReload)) + None, self, "Reload", callback=self.load_data, autoDefault=False + ) + reload_button.setIcon(self.style().standardIcon(QStyle.SP_BrowserReload)) reload_button.setSizePolicy(Policy.Fixed, Policy.Fixed) layout.addWidget(reload_button, 0, 3) self.sheet_box = gui.hBox(None, addToLayout=False, margin=0) - self.sheet_combo = gui.comboBox(None, self, "xls_sheet", - callback=self.select_sheet, - sendSelectedValue=True,) - self.sheet_combo.setSizePolicy( - Policy.MinimumExpanding, Policy.Fixed) + self.sheet_combo = gui.comboBox( + None, + self, + "xls_sheet", + callback=self.select_sheet, + sendSelectedValue=True, + ) + self.sheet_combo.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed) self.sheet_label = QLabel() self.sheet_label.setText('Sheet') - self.sheet_label.setSizePolicy( - Policy.MinimumExpanding, Policy.Fixed) - self.sheet_box.layout().addWidget( - self.sheet_label, Qt.AlignLeft) - self.sheet_box.layout().addWidget( - self.sheet_combo, Qt.AlignVCenter) + self.sheet_label.setSizePolicy(Policy.MinimumExpanding, Policy.Fixed) + self.sheet_box.layout().addWidget(self.sheet_label, Qt.AlignLeft) + self.sheet_box.layout().addWidget(self.sheet_combo, Qt.AlignVCenter) layout.addWidget(self.sheet_box, 2, 1) self.sheet_box.hide() @@ -195,18 +221,23 @@ def __init__(self): box = gui.hBox(self.controlArea) gui.button( - box, self, "Browse documentation datasets", - callback=lambda: self.browse_file(True), autoDefault=False) + box, + self, + "Browse documentation datasets", + callback=lambda: self.browse_file(True), + autoDefault=False, + ) gui.rubber(box) - gui.button( - box, self, "Reset", callback=self.reset_domain_edit) + gui.button(box, self, "Reset", callback=self.reset_domain_edit) self.apply_button = gui.button( - box, self, "Apply", callback=self.apply_domain_edit) + box, self, "Apply", callback=self.apply_domain_edit + ) self.apply_button.setEnabled(False) self.apply_button.setFixedWidth(170) self.editor_model.dataChanged.connect( - lambda: self.apply_button.setEnabled(True)) + lambda: self.apply_button.setEnabled(True) + ) self.set_file_list() # Must not call open_file from within __init__. open_file @@ -340,8 +371,7 @@ def missing_prop(prop): text = "" attrs = getattr(table, "attributes", {}) - descs = [attrs[desc] - for desc in ("Name", "Description") if desc in attrs] + descs = [attrs[desc] for desc in ("Name", "Description") if desc in attrs] if len(descs) == 2: descs[0] = f"{descs[0]}" if descs: @@ -349,20 +379,26 @@ def missing_prop(prop): text += f"

{len(table)} instance(s)" - missing_in_attr = missing_prop(table.has_missing_attribute() - and table.get_nan_frequency_attribute()) - missing_in_class = missing_prop(table.has_missing_class() - and table.get_nan_frequency_class()) + missing_in_attr = missing_prop( + table.has_missing_attribute() and table.get_nan_frequency_attribute() + ) + missing_in_class = missing_prop( + table.has_missing_class() and table.get_nan_frequency_class() + ) text += f"
{len(domain.attributes)} feature(s) {missing_in_attr}" if domain.has_continuous_class: text += f"
Regression; numerical class {missing_in_class}" elif domain.has_discrete_class: - text += "
Classification; categorical class " \ + text += ( + "
Classification; categorical class " f"with {len(domain.class_var.values)} values {missing_in_class}" + ) elif table.domain.class_vars: - text += "
Multi-target; " \ - f"{len(table.domain.class_vars)} target variables " \ + text += ( + "
Multi-target; " + f"{len(table.domain.class_vars)} target variables " f"{missing_in_class}" + ) else: text += "
Data has no target variable." text += f"
{len(domain.metas)} meta attribute(s)" @@ -370,8 +406,10 @@ def missing_prop(prop): if 'Timestamp' in table.domain: # Google Forms uses this header to timestamp responses - text += f"

First entry: {table[0, 'Timestamp']}
" \ + text += ( + f"

First entry: {table[0, 'Timestamp']}
" f"Last entry: {table[-1, 'Timestamp']}

" + ) return text def storeSpecificSettings(self): @@ -430,17 +468,22 @@ def get_ext_name(filename): home = os.path.expanduser("~") if self.loaded_file.startswith(home): # os.path.join does not like ~ - name = "~" + os.path.sep + \ - self.loaded_file[len(home):].lstrip("/").lstrip("\\") + name = ( + "~" + + os.path.sep + + self.loaded_file[len(home) :].lstrip("/").lstrip("\\") + ) else: name = self.loaded_file if self.sheet_combo.isVisible(): name += f" ({self.sheet_combo.currentText()})" - self.report_items("File", [("File name", name), - ("Format", get_ext_name(name))]) + self.report_items( + "File", [("File name", name), ("Format", get_ext_name(name))] + ) else: - self.report_items("Data", [("Resource", self.url), - ("Format", get_ext_name(self.url))]) + self.report_items( + "Data", [("Resource", self.url), ("Format", get_ext_name(self.url))] + ) self.report_data("Data", self.data) @@ -470,15 +513,17 @@ def workflowEnvChanged(self, key, value, oldvalue): (e.g. relative file paths are changed) """ self.update_file_list(key, value, oldvalue) - #### End code copy #### + #### End code copy #### @staticmethod def _is_preproc(p): """ Tests that a preprocessor is not None or empty PreprocessorList """ - return not(p is None or (isinstance(p, PreprocessorList) and len(p.preprocessors) == 0)) + return not ( + p is None or (isinstance(p, PreprocessorList) and len(p.preprocessors) == 0) + ) @staticmethod def _format_preproc_str(preprocessor): @@ -498,8 +543,10 @@ def warn_preprocessor(self): self.info_preproc.setText("No preprocessor on input.") self.Warning.no_preprocessor() return True - self.info_preproc.setText("New preprocessor, reload file to use.\n" + - self._format_preproc_str(self.preprocessor)) + self.info_preproc.setText( + "New preprocessor, reload file to use.\n" + + self._format_preproc_str(self.preprocessor) + ) self.Warning.new_preprocessor() return False @@ -526,14 +573,19 @@ def browse_file(self, in_demos=False): start_file = get_sample_datasets_dir() if not os.path.exists(start_file): QMessageBox.information( - None, "File", - "Cannot find the directory with documentation datasets") + None, + "File", + "Cannot find the directory with documentation datasets", + ) return else: start_file = self.last_path() or os.path.expanduser("~/") - readers = [f for f in FileFormat.formats - if getattr(f, 'read_tile', None) and getattr(f, "EXTENSIONS", None)] + readers = [ + f + for f in FileFormat.formats + if getattr(f, 'read_tile', None) and getattr(f, "EXTENSIONS", None) + ] filename, reader, _ = open_filename_dialog(start_file, None, readers) if not filename: return @@ -559,8 +611,11 @@ def get_tile_reader(cls, filename): ------- FileFormat """ - readers = [f for f in FileFormat.formats - if getattr(f, 'read_tile', None) and getattr(f, "EXTENSIONS", None)] + readers = [ + f + for f in FileFormat.formats + if getattr(f, 'read_tile', None) and getattr(f, "EXTENSIONS", None) + ] for reader in readers: if os.path.splitext(filename)[1] in reader.EXTENSIONS: return reader(filename) @@ -588,7 +643,8 @@ def _get_reader(self): reader.set_preprocessor(self.preprocessor) if self.preprocessor is not None: self.info_preproc.setText( - self._format_preproc_str(self.preprocessor)) + self._format_preproc_str(self.preprocessor) + ) else: # only allow readers with tile-by-tile support to run. reader = None @@ -602,5 +658,6 @@ def _get_reader(self): if __name__ == "__main__": from Orange.widgets.utils.widgetpreview import WidgetPreview from orangecontrib.spectroscopy.preprocess import Cut, LinearBaseline + preproc = PreprocessorList([LinearBaseline(), Cut(lowlim=2000, highlim=2006)]) WidgetPreview(OWTilefile).run(insert_preprocessor=(0, preproc)) diff --git a/orangecontrib/spectroscopy/widgets/peak_editors.py b/orangecontrib/spectroscopy/widgets/peak_editors.py index 85ed4c6f4..1e6846295 100644 --- a/orangecontrib/spectroscopy/widgets/peak_editors.py +++ b/orangecontrib/spectroscopy/widgets/peak_editors.py @@ -6,15 +6,24 @@ from AnyQt.QtCore import Signal from Orange.widgets.data.utils.preprocess import blocked from AnyQt.QtCore import QSize, QObject -from AnyQt.QtWidgets import \ - QWidget, QHBoxLayout, QSizePolicy, QComboBox, QLineEdit, QGridLayout, QLabel, \ - QAbstractSpinBox +from AnyQt.QtWidgets import ( + QWidget, + QHBoxLayout, + QSizePolicy, + QComboBox, + QLineEdit, + QGridLayout, + QLabel, + QAbstractSpinBox, +) from orangewidget.widget import Msg from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.widgets.gui import MovableVline -from orangecontrib.spectroscopy.widgets.preprocessors.utils import \ - SetXDoubleSpinBox, BaseEditorOrange +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + SetXDoubleSpinBox, + BaseEditorOrange, +) DEFAULT_DELTA = 1 @@ -23,10 +32,8 @@ class CompactDoubleSpinBox(SetXDoubleSpinBox): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs, - buttonSymbols=QAbstractSpinBox.NoButtons) + super().__init__(*args, **kwargs, buttonSymbols=QAbstractSpinBox.NoButtons) def sizeHint(self) -> QSize: sh = super().sizeHint() @@ -36,6 +43,7 @@ def sizeHint(self) -> QSize: def minimumSizeHint(self) -> QSize: return self.sizeHint() + class ParamHintBox(QWidget): """ Box to interact with lmfit parameter hints @@ -62,46 +70,73 @@ def __init__(self, hints, parent=None, **kwargs): minf, maxf = -sys.float_info.max, sys.float_info.max - self._defaults = {'min': UNUSED_VALUE, - 'value': DEFAULT_VALUE, - 'max': UNUSED_VALUE, - 'delta': DEFAULT_DELTA, - 'expr': ''} - - self.min_e = CompactDoubleSpinBox(minimum=UNUSED_VALUE, maximum=maxf, singleStep=0.5, - value=self.hints.get('min', self._defaults["min"]), - specialValueText="None") - self.val_e = CompactDoubleSpinBox(minimum=minf, maximum=maxf, singleStep=0.5, - value=self.hints.get('value', self._defaults["value"])) - self.max_e = CompactDoubleSpinBox(minimum=UNUSED_VALUE, maximum=maxf, singleStep=0.5, - value=self.hints.get('max', self._defaults["min"]), - specialValueText="None") - self.delta_e = CompactDoubleSpinBox(minimum=minf, maximum=maxf, singleStep=0.5, - value=self.hints.get('delta', self._defaults["delta"]), - prefix="±", visible=False) + self._defaults = { + 'min': UNUSED_VALUE, + 'value': DEFAULT_VALUE, + 'max': UNUSED_VALUE, + 'delta': DEFAULT_DELTA, + 'expr': '', + } + + self.min_e = CompactDoubleSpinBox( + minimum=UNUSED_VALUE, + maximum=maxf, + singleStep=0.5, + value=self.hints.get('min', self._defaults["min"]), + specialValueText="None", + ) + self.val_e = CompactDoubleSpinBox( + minimum=minf, + maximum=maxf, + singleStep=0.5, + value=self.hints.get('value', self._defaults["value"]), + ) + self.max_e = CompactDoubleSpinBox( + minimum=UNUSED_VALUE, + maximum=maxf, + singleStep=0.5, + value=self.hints.get('max', self._defaults["min"]), + specialValueText="None", + ) + self.delta_e = CompactDoubleSpinBox( + minimum=minf, + maximum=maxf, + singleStep=0.5, + value=self.hints.get('delta', self._defaults["delta"]), + prefix="±", + visible=False, + ) self.vary_e = QComboBox() - v_opt = ('fixed', 'limits', 'delta', 'expr') if 'expr' in self.hints \ + v_opt = ( + ('fixed', 'limits', 'delta', 'expr') + if 'expr' in self.hints else ('fixed', 'limits', 'delta') + ) self.vary_e.insertItems(0, v_opt) with blocked(self.vary_e): self.vary_e.setCurrentText(self.hints['vary']) - self.expr_e = QLineEdit(visible=False, enabled=False, - text=self.hints.get('expr', "")) + self.expr_e = QLineEdit( + visible=False, enabled=False, text=self.hints.get('expr', "") + ) - self.edits = [("min", self.min_e), - ("value", self.val_e), - ("max", self.max_e), - ("delta", self.delta_e), - ("vary", self.vary_e), - ("expr", self.expr_e)] + self.edits = [ + ("min", self.min_e), + ("value", self.val_e), + ("max", self.max_e), + ("delta", self.delta_e), + ("vary", self.vary_e), + ("expr", self.expr_e), + ] for name, widget in self.edits[:4]: # float fields - widget.valueChanged[float].connect(lambda x, name=name: self._changed_float(x, name)) + widget.valueChanged[float].connect( + lambda x, name=name: self._changed_float(x, name) + ) self.vary_e.currentTextChanged.connect(self._changed_vary) self.expr_e.textChanged.connect(self._changed_expr) - for name, widget in self.edits: + for _name, widget in self.edits: layout.addWidget(widget) widget.focusIn = self.focusIn @@ -223,6 +258,7 @@ def __init__(self, parent=None, **kwargs): def change_hint(name=name): self.edited.emit() self.changed_param_hints(name) + e.valueChanged.connect(change_hint) self.__editors[name] = e layout.addWidget(QLabel(name), row, 0) @@ -235,6 +271,7 @@ def change_value(_, line=l, name=name): self.set_hint(name, "value", float(line.rounded_value())) self.__editors[name].update_min_max_for_delta() self.edited.emit() + l.sigMoved.connect(change_value) self.__lines[name] = l @@ -296,8 +333,9 @@ def set_preview_data(self, data): if len(xs): minx = np.min(xs) maxx = np.max(xs) - limits = [(name, self.__values.get(name, {})) - for name in self.model_lines()] + limits = [ + (name, self.__values.get(name, {})) for name in self.model_lines() + ] for name, h in limits: v = h.get('value', None) if v is not None and (v < minx or v > maxx): @@ -410,7 +448,7 @@ def set_preview_data(self, data): if not self.user_changed: x = getx(data) if len(x): - self.set_hint('center', 'value', x[int(len(x)/2)]) + self.set_hint('center', 'value', x[int(len(x) / 2)]) self.edited.emit() super().set_preview_data(data) @@ -544,7 +582,10 @@ class SkewedVoigtModelEditor(PeakModelEditor): @classmethod def model_parameters(cls): - return super().model_parameters() + ('gamma', 'skew',) + return super().model_parameters() + ( + 'gamma', + 'skew', + ) class ThermalDistributionModelEditor(PeakModelEditor): diff --git a/orangecontrib/spectroscopy/widgets/peakfit_compute.py b/orangecontrib/spectroscopy/widgets/peakfit_compute.py index 90c287538..d746574f3 100644 --- a/orangecontrib/spectroscopy/widgets/peakfit_compute.py +++ b/orangecontrib/spectroscopy/widgets/peakfit_compute.py @@ -29,7 +29,8 @@ def best_fit_results(model_result, x, shape): for comp in out.model.components: # Peak area output[col] = scipy.integrate.trapezoid( - np.broadcast_to(comps[comp.prefix], x.shape), sorted_x) + np.broadcast_to(comps[comp.prefix], x.shape), sorted_x + ) col += 1 for param in [n for n in out.var_names if n.startswith(comp.prefix)]: output[col] = best_values[param] @@ -59,10 +60,7 @@ def pool_fit(v): bpar = best_fit_results(model_result, x, shape) fitted = np.broadcast_to(model_result.eval(x=x), x.shape) - return model_result.dumps(), \ - bpar, \ - fitted, \ - model_result.residual + return model_result.dumps(), bpar, fitted, model_result.residual def pool_fit2(v, model, parameters, x): diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/__init__.py b/orangecontrib/spectroscopy/widgets/preprocessors/__init__.py index d5f2a8631..997741110 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/__init__.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/__init__.py @@ -1,4 +1,5 @@ # load and register editors + from . import misc from . import als from . import atm_corr diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/als.py b/orangecontrib/spectroscopy/widgets/preprocessors/als.py index 7f9da3b85..55108565a 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/als.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/als.py @@ -8,13 +8,14 @@ class ALSEditor(BaseEditorOrange): """ - Asymmetric least squares subtraction. + Asymmetric least squares subtraction. """ + name = "Asymmetric Least Squares Smoothing" qualname = "preprocessors.ALS" ALS_TYPE = 0 - LAM = 1E+6 + LAM = 1e6 ITERMAX = 10 P = 0.1 RATIO = 0.05 @@ -30,34 +31,62 @@ def __init__(self, parent=None, **kwargs): self.p = self.P self.ratio = self.RATIO self.porder = self.PORDER - self.alst_combo = gui.comboBox(None, self, "als_type", - items=["Asymmetric", - "Asymmetrically Reweighted", - "Adaptive Iteratively Reweighted"], - callback=self.edited.emit) + self.alst_combo = gui.comboBox( + None, + self, + "als_type", + items=[ + "Asymmetric", + "Asymmetrically Reweighted", + "Adaptive Iteratively Reweighted", + ], + callback=self.edited.emit, + ) self.controlArea.layout().addLayout(form) - self.itermaxspin = gui.spin(None, self, "itermax", - label="Max. iterations", - minv=1, maxv=100000, controlWidth=100, - callback=self.edited.emit) - self.lamspin = lineEditDecimalOrNone(None, self, value="lam", - bottom=0, callback=self.edited.emit) + self.itermaxspin = gui.spin( + None, + self, + "itermax", + label="Max. iterations", + minv=1, + maxv=100000, + controlWidth=100, + callback=self.edited.emit, + ) + self.lamspin = lineEditDecimalOrNone( + None, self, value="lam", bottom=0, callback=self.edited.emit + ) form.addRow("ALS Type", self.alst_combo) form.addRow("Smoothing Constant", self.lamspin) form.addRow('Max. Iterations', self.itermaxspin) - self.palsspin = lineEditDecimalOrNone(None, self, value="p", - bottom=0.000000001, top=1, - callback=self.edited.emit) - self.palsspin.setToolTip("0.5 = symmetric, <0.5: negative " - "deviations are more strongly suppressed") - self.ratior = lineEditDecimalOrNone(None, self, value="ratio", - bottom=0.000000001, top=1, - callback=self.edited.emit) - self.ratior.setToolTip("0 < ratio < 1, smaller values allow less negative values") - self.porderairplsspin = gui.spin(None, self, "porder", - label="Order of the difference of penalties (Adaptive)", - minv=1, maxv=100, controlWidth=100, - step=1, callback=self.edited.emit) + self.palsspin = lineEditDecimalOrNone( + None, self, value="p", bottom=0.000000001, top=1, callback=self.edited.emit + ) + self.palsspin.setToolTip( + "0.5 = symmetric, <0.5: negative deviations are more strongly suppressed" + ) + self.ratior = lineEditDecimalOrNone( + None, + self, + value="ratio", + bottom=0.000000001, + top=1, + callback=self.edited.emit, + ) + self.ratior.setToolTip( + "0 < ratio < 1, smaller values allow less negative values" + ) + self.porderairplsspin = gui.spin( + None, + self, + "porder", + label="Order of the difference of penalties (Adaptive)", + minv=1, + maxv=100, + controlWidth=100, + step=1, + callback=self.edited.emit, + ) form.addRow('Weighting Deviations', self.palsspin) form.addRow('Weighting Deviations', self.ratior) form.addRow('Penalties Order', self.porderairplsspin) @@ -96,14 +125,11 @@ def createinstance(cls, params): porderairpls = params.get('porder', cls.PORDER) if als_type == 0: - return ALSP(lam=lam, itermax=itermax, - p=float(pals)) + return ALSP(lam=lam, itermax=itermax, p=float(pals)) elif als_type == 1: - return ARPLS(lam=lam, ratio=float(ratioarpls), - itermax=itermax) + return ARPLS(lam=lam, ratio=float(ratioarpls), itermax=itermax) elif als_type == 2: - return AIRPLS(lam=lam, itermax=itermax, - porder=porderairpls) + return AIRPLS(lam=lam, itermax=itermax, porder=porderairpls) else: raise Exception("unknown baseline type") diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/atm_corr.py b/orangecontrib/spectroscopy/widgets/preprocessors/atm_corr.py index efcca2544..a462197a3 100755 --- a/orangecontrib/spectroscopy/widgets/preprocessors/atm_corr.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/atm_corr.py @@ -5,23 +5,25 @@ from AnyQt.QtWidgets import QVBoxLayout, QLabel from Orange.widgets import gui from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors -from orangecontrib.spectroscopy.widgets.preprocessors.utils import \ - BaseEditorOrange, REFERENCE_DATA_PARAM +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + BaseEditorOrange, + REFERENCE_DATA_PARAM, +) from orangecontrib.spectroscopy.preprocess.atm_corr import AtmCorr from orangecontrib.spectroscopy.data import spectra_mean, getx class AtmCorrEditor(BaseEditorOrange): """ - Atmospheric gas correction. - Default ranges are two H2O regions (corrected) and one CO2 region (removed) + Atmospheric gas correction. + Default ranges are two H2O regions (corrected) and one CO2 region (removed) """ + name = "Atmospheric gas (CO2/H2O) correction" categories = ["Correction"] qualname = "preprocessors.atm_corr" - RANGES = [[1300, 2100, 1], [2190, 2480, 2], - [3410, 3850, 1], ['', '', 0]] + RANGES = [[1300, 2100, 1], [2190, 2480, 2], [3410, 3850, 1], ['', '', 0]] SMOOTH = True SMOOTH_WIN = 9 BRIDGE_WIN = 9 @@ -46,26 +48,69 @@ def __init__(self, parent=None, **kwargs): self.range_boxes = [] for b in range(len(self.RANGES)): box = gui.hBox(self.controlArea) - gui.comboBox(box, self, f"corrmode_{b}", - items=('No-op', 'Correct', 'Bridge'), callback=self.edited.emit) - gui.lineEdit(box, self, f"low_{b}", label="from", - callback=self.edited.emit, orientation=Qt.Horizontal, - controlWidth=75) - gui.lineEdit(box, self, f"high_{b}", label="to", - callback=self.edited.emit, orientation=Qt.Horizontal, - controlWidth=75) + gui.comboBox( + box, + self, + f"corrmode_{b}", + items=('No-op', 'Correct', 'Bridge'), + callback=self.edited.emit, + ) + gui.lineEdit( + box, + self, + f"low_{b}", + label="from", + callback=self.edited.emit, + orientation=Qt.Horizontal, + controlWidth=75, + ) + gui.lineEdit( + box, + self, + f"high_{b}", + label="to", + callback=self.edited.emit, + orientation=Qt.Horizontal, + controlWidth=75, + ) self.range_boxes.append(box) - self.smooth_button = gui.checkBox(self.controlArea, self, "smooth", - "Smooth corrected regions", callback=self.edited.emit) - self.smooth_win_spin = gui.spin(self.controlArea, self, "smooth_win", - label="Savitzky-Golay window size", minv=5, maxv=35, - step=2, controlWidth=60, callback=self.edited.emit) - self.bridge_win_spin = gui.spin(self.controlArea, self, "bridge_win", - label="Bridge base window size", minv=3, maxv=35, - step=2, controlWidth=60, callback=self.edited.emit) - gui.checkBox(self.controlArea, self, "mean_reference", - "Use mean of references", callback=self.edited.emit) + self.smooth_button = gui.checkBox( + self.controlArea, + self, + "smooth", + "Smooth corrected regions", + callback=self.edited.emit, + ) + self.smooth_win_spin = gui.spin( + self.controlArea, + self, + "smooth_win", + label="Savitzky-Golay window size", + minv=5, + maxv=35, + step=2, + controlWidth=60, + callback=self.edited.emit, + ) + self.bridge_win_spin = gui.spin( + self.controlArea, + self, + "bridge_win", + label="Bridge base window size", + minv=3, + maxv=35, + step=2, + controlWidth=60, + callback=self.edited.emit, + ) + gui.checkBox( + self.controlArea, + self, + "mean_reference", + "Use mean of references", + callback=self.edited.emit, + ) self.reference_info = QLabel("", self) self.controlArea.layout().addWidget(self.reference_info) @@ -149,8 +194,11 @@ def createinstance(cls, params): cranges.append(r) elif cm == 2: sranges.append(r) - smooth_win = params.get("smooth_win", cls.SMOOTH_WIN) if \ - params.get("smooth", cls.SMOOTH) else 0 + smooth_win = ( + params.get("smooth_win", cls.SMOOTH_WIN) + if params.get("smooth", cls.SMOOTH) + else 0 + ) bridge_win = params.get("bridge_win", cls.BRIDGE_WIN) mean_reference = params.get("mean_reference", cls.MEAN_REF) reference = params.get(REFERENCE_DATA_PARAM, None) @@ -158,10 +206,14 @@ def createinstance(cls, params): if reference is None: return lambda data: data[:0] # return an empty data table else: - return AtmCorr(reference=reference, correct_ranges=cranges, - spline_ranges=sranges, smooth_win=smooth_win, - spline_base_win=bridge_win, - mean_reference=mean_reference) + return AtmCorr( + reference=reference, + correct_ranges=cranges, + spline_ranges=sranges, + smooth_win=smooth_win, + spline_base_win=bridge_win, + mean_reference=mean_reference, + ) preprocess_editors.register(AtmCorrEditor, 700) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/baseline.py b/orangecontrib/spectroscopy/widgets/preprocessors/baseline.py index e407fd1a7..e2a654770 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/baseline.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/baseline.py @@ -7,14 +7,18 @@ from orangecontrib.spectroscopy.preprocess import LinearBaseline, RubberbandBaseline from orangecontrib.spectroscopy.widgets.gui import XPosLineEdit from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors -from orangecontrib.spectroscopy.widgets.preprocessors.utils import BaseEditorOrange, \ - PreviewMinMaxMixin, layout_widgets +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + BaseEditorOrange, + PreviewMinMaxMixin, + layout_widgets, +) class BaselineEditor(BaseEditorOrange, PreviewMinMaxMixin): """ Baseline subtraction. """ + name = "Baseline Correction" qualname = "orangecontrib.infrared.baseline" @@ -28,15 +32,27 @@ def __init__(self, parent=None, **kwargs): self.peak_dir = 0 self.sub = 0 - self.baselinecb = gui.comboBox(None, self, "baseline_type", - items=["Linear", "Rubber band"], - callback=self.edited.emit) - self.peakcb = gui.comboBox(None, self, "peak_dir", - items=["Positive", "Negative"], - callback=self.edited.emit) - self.subcb = gui.comboBox(None, self, "sub", - items=["Subtract", "Calculate"], - callback=self.edited.emit) + self.baselinecb = gui.comboBox( + None, + self, + "baseline_type", + items=["Linear", "Rubber band"], + callback=self.edited.emit, + ) + self.peakcb = gui.comboBox( + None, + self, + "peak_dir", + items=["Positive", "Negative"], + callback=self.edited.emit, + ) + self.subcb = gui.comboBox( + None, + self, + "sub", + items=["Subtract", "Calculate"], + callback=self.edited.emit, + ) form.addRow("Baseline Type", self.baselinecb) form.addRow("Peak Direction", self.peakcb) @@ -51,7 +67,7 @@ def __init__(self, parent=None, **kwargs): self.controlArea.layout().addWidget(self.range_button) self.reference_curve = pg.PlotCurveItem() - self.reference_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=2.)) + self.reference_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=2.0)) self.reference_curve.setZValue(10) self.preview_data = None @@ -70,9 +86,9 @@ def activateOptions(self): self.parent_widget.curveplot.add_marking(w.line) def _set_button_text(self): - self.range_button.setText("Select point" - if self.ranges_box.layout().count() == 0 - else "Add point") + self.range_button.setText( + "Select point" if self.ranges_box.layout().count() == 0 else "Add point" + ) def _range_widgets(self): for b in layout_widgets(self.ranges_box): @@ -80,7 +96,9 @@ def _range_widgets(self): def add_point(self): pmin, pmax = self.preview_min_max() - if len(list(self._range_widgets())) == 0: # if empty, add two points at the same time + if ( + len(list(self._range_widgets())) == 0 + ): # if empty, add two points at the same time lwmin = self.add_range_selection_ui() lwmax = self.add_range_selection_ui() self._extract_pair(lwmin)[0].position = pmin @@ -97,14 +115,16 @@ def add_range_selection_ui(self): linelayout = gui.hBox(self) pmin, pmax = self.preview_min_max() e = XPosLineEdit(label="") - e.set_default((pmin+pmax)/2) + e.set_default((pmin + pmax) / 2) linelayout.layout().addWidget(e) e.edited.connect(self.edited) e.focusIn.connect(self.activateOptions) remove_button = QPushButton( QApplication.style().standardIcon(QStyle.SP_DockWidgetCloseButton), - "", autoDefault=False) + "", + autoDefault=False, + ) remove_button.clicked.connect(lambda: self.delete_range(linelayout)) linelayout.layout().addWidget(remove_button) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/emsc.py b/orangecontrib/spectroscopy/widgets/preprocessors/emsc.py index 65f9768f2..49d2ffbff 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/emsc.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/emsc.py @@ -2,17 +2,31 @@ import pyqtgraph as pg from AnyQt.QtCore import Qt from AnyQt.QtGui import QColor -from AnyQt.QtWidgets import QVBoxLayout, QLabel, QPushButton, QApplication, QStyle, QSizePolicy +from AnyQt.QtWidgets import ( + QVBoxLayout, + QLabel, + QPushButton, + QApplication, + QStyle, + QSizePolicy, +) from Orange.widgets import gui from orangecontrib.spectroscopy.data import spectra_mean, getx from orangecontrib.spectroscopy.preprocess import EMSC -from orangecontrib.spectroscopy.preprocess.emsc import SelectionFunction, SmoothedSelectionFunction +from orangecontrib.spectroscopy.preprocess.emsc import ( + SelectionFunction, + SmoothedSelectionFunction, +) from orangecontrib.spectroscopy.preprocess.npfunc import Sum from orangecontrib.spectroscopy.widgets.gui import XPosLineEdit, lineEditFloatOrNone from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors -from orangecontrib.spectroscopy.widgets.preprocessors.utils import BaseEditorOrange, \ - PreviewMinMaxMixin, layout_widgets, REFERENCE_DATA_PARAM +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + BaseEditorOrange, + PreviewMinMaxMixin, + layout_widgets, + REFERENCE_DATA_PARAM, +) class EMSCEditor(BaseEditorOrange, PreviewMinMaxMixin): @@ -33,18 +47,33 @@ def __init__(self, parent=None, **kwargs): self.order = self.ORDER_DEFAULT - gui.spin(self.controlArea, self, "order", label="Polynomial order", minv=0, maxv=10, - controlWidth=50, callback=self.edited.emit) + gui.spin( + self.controlArea, + self, + "order", + label="Polynomial order", + minv=0, + maxv=10, + controlWidth=50, + callback=self.edited.emit, + ) self.scaling = self.SCALING_DEFAULT - gui.checkBox(self.controlArea, self, "scaling", "Scaling", callback=self.edited.emit) + gui.checkBox( + self.controlArea, self, "scaling", "Scaling", callback=self.edited.emit + ) self.reference_info = QLabel("", self) self.controlArea.layout().addWidget(self.reference_info) self.output_model = self.OUTPUT_MODEL_DEFAULT - gui.checkBox(self.controlArea, self, "output_model", "Output EMSC model as metas", - callback=self.edited.emit) + gui.checkBox( + self.controlArea, + self, + "output_model", + "Output EMSC model as metas", + callback=self.edited.emit, + ) self._init_regions() self._init_reference_curve() @@ -59,18 +88,18 @@ def _init_regions(self): self.controlArea.layout().addWidget(self.range_button) self.weight_curve = pg.PlotCurveItem() - self.weight_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=1.)) + self.weight_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=1.0)) self.weight_curve.setZValue(11) def _init_reference_curve(self): self.reference_curve = pg.PlotCurveItem() - self.reference_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=2.)) + self.reference_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=2.0)) self.reference_curve.setZValue(10) def _set_button_text(self): - self.range_button.setText("Select Region" - if self.ranges_box.layout().count() == 0 - else "Add Region") + self.range_button.setText( + "Select Region" if self.ranges_box.layout().count() == 0 else "Add Region" + ) def add_range_selection(self): pmin, pmax = self.preview_min_max() @@ -100,7 +129,9 @@ def add_range_selection_ui(self): remove_button = QPushButton( QApplication.style().standardIcon(QStyle.SP_DockWidgetCloseButton), - "", autoDefault=False) + "", + autoDefault=False, + ) remove_button.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)) remove_button.clicked.connect(lambda: self.delete_range(linelayout)) linelayout.layout().addWidget(remove_button) @@ -177,10 +208,14 @@ def parameters(self): parameters = super().parameters() parameters["ranges"] = [] for pair in self._range_widgets(): - parameters["ranges"].append([pair[0].position, - pair[1].position, - 1.0, # for now weight is always 1.0 - pair[2].position]) + parameters["ranges"].append( + [ + pair[0].position, + pair[1].position, + 1.0, # for now weight is always 1.0 + pair[2].position, + ] + ) return parameters @classmethod @@ -213,8 +248,13 @@ def createinstance(cls, params): if reference is None: return lambda data: data[:0] # return an empty data table else: - return EMSC(reference=reference, weights=weights, order=order, - scaling=scaling, output_model=output_model) + return EMSC( + reference=reference, + weights=weights, + order=order, + scaling=scaling, + output_model=output_model, + ) def set_reference_data(self, reference): self.reference = reference @@ -226,8 +266,11 @@ def update_reference_info(self): self.reference_info.setText("Reference: missing!") self.reference_info.setStyleSheet("color: red") else: - rinfo = "mean of %d spectra" % len(self.reference) \ - if len(self.reference) > 1 else "1 spectrum" + rinfo = ( + "mean of %d spectra" % len(self.reference) + if len(self.reference) > 1 + else "1 spectrum" + ) self.reference_info.setText("Reference: " + rinfo) self.reference_info.setStyleSheet("color: black") X_ref = spectra_mean(self.reference.X) @@ -242,8 +285,8 @@ def update_weight_curve(self, params): self.weight_curve.hide() else: pmin, pmax = self.preview_min_max() - dist = pmax-pmin - xs = np.linspace(pmin-dist/2, pmax+dist/2, 10000) + dist = pmax - pmin + xs = np.linspace(pmin - dist / 2, pmax + dist / 2, 10000) ys = weights(xs) self.weight_curve.setData(x=xs, y=ys) self.weight_curve.show() diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/integrate.py b/orangecontrib/spectroscopy/widgets/preprocessors/integrate.py index 5b9baaf9e..6738c85cb 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/integrate.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/integrate.py @@ -1,26 +1,40 @@ import sys from AnyQt.QtCore import pyqtSignal as Signal, QObject -from AnyQt.QtWidgets import QVBoxLayout, QFormLayout, QComboBox, QPushButton, \ - QSizePolicy, QHBoxLayout, QLabel, QApplication, QStyle +from AnyQt.QtWidgets import ( + QVBoxLayout, + QFormLayout, + QComboBox, + QPushButton, + QSizePolicy, + QHBoxLayout, + QLabel, + QApplication, + QStyle, +) from Orange.widgets.data.utils.preprocess import blocked from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.preprocess import Integrate from orangecontrib.spectroscopy.widgets.gui import MovableVline from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors -from orangecontrib.spectroscopy.widgets.preprocessors.utils import \ - BaseEditor, SetXDoubleSpinBox +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + BaseEditor, + SetXDoubleSpinBox, +) class IntegrateEditor(BaseEditor): """ Editor to integrate defined regions. """ + name = "Integrate" qualname = "orangecontrib.infrared.integrate" - Integrators_classes = [i for i in Integrate.INTEGRALS if i not in (Integrate.Separate,)] + Integrators_classes = [ + i for i in Integrate.INTEGRALS if i not in (Integrate.Separate,) + ] Integrators = [a.name for a in Integrators_classes] def __init__(self, parent=None, **kwargs): @@ -32,7 +46,9 @@ def __init__(self, parent=None, **kwargs): deprecation_info = QLabel( "This preprocessor is deprecated and will be removed in the future. " - "Use the Integrate widget instead.", self) + "Use the Integrate widget instead.", + self, + ) deprecation_info.setWordWrap(True) deprecation_info.setStyleSheet("color: red") self.layout().addWidget(deprecation_info) @@ -75,10 +91,10 @@ def add_limit(self, *args, row=None): try: self._limits.append(self._limits[-1]) except IndexError: - self._limits.append([0., 1.]) - label = "Region {0}".format(row+1) + self._limits.append([0.0, 1.0]) + label = "Region {0}".format(row + 1) limitbox = LimitsBox(limits=self._limits[row], label=label) - if self.form_lim.rowCount() < row+1: + if self.form_lim.rowCount() < row + 1: # new row self.form_lim.addRow(limitbox) else: @@ -127,17 +143,18 @@ def setParameters(self, params): if params: # parameters were manually set somewhere else self.user_changed = True self.methodcb.setCurrentIndex( - params.get("method", self.Integrators_classes.index(Integrate.Baseline))) - self.set_all_limits(params.get("limits", [[0., 1.]]), user=False) + params.get("method", self.Integrators_classes.index(Integrate.Baseline)) + ) + self.set_all_limits(params.get("limits", [[0.0, 1.0]]), user=False) def parameters(self): - return {"method": self.methodcb.currentIndex(), - "limits": self._limits} + return {"method": self.methodcb.currentIndex(), "limits": self._limits} @staticmethod def createinstance(params): - methodindex = params.get("method", - IntegrateEditor.Integrators_classes.index(Integrate.Baseline)) + methodindex = params.get( + "method", IntegrateEditor.Integrators_classes.index(Integrate.Baseline) + ) method = IntegrateEditor.Integrators_classes[methodindex] limits = params.get("limits", None) return Integrate(methods=method, limits=limits) @@ -175,12 +192,12 @@ def __init__(self, parent=None, **kwargs): if label: self.addWidget(QLabel(label)) - self.lowlime = SetXDoubleSpinBox(minimum=minf, - maximum=maxf, singleStep=0.5, - value=limits[0], maximumWidth=75) - self.highlime = SetXDoubleSpinBox(minimum=minf, - maximum=maxf, singleStep=0.5, - value=limits[1], maximumWidth=75) + self.lowlime = SetXDoubleSpinBox( + minimum=minf, maximum=maxf, singleStep=0.5, value=limits[0], maximumWidth=75 + ) + self.highlime = SetXDoubleSpinBox( + minimum=minf, maximum=maxf, singleStep=0.5, value=limits[1], maximumWidth=75 + ) self.lowlime.setValue(limits[0]) self.highlime.setValue(limits[1]) self.addWidget(self.lowlime) @@ -188,7 +205,8 @@ def __init__(self, parent=None, **kwargs): if delete: self.button = QPushButton( - QApplication.style().standardIcon(QStyle.SP_DockWidgetCloseButton), "") + QApplication.style().standardIcon(QStyle.SP_DockWidgetCloseButton), "" + ) self.addWidget(self.button) self.button.clicked.connect(self.selfDelete) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/me_emsc.py b/orangecontrib/spectroscopy/widgets/preprocessors/me_emsc.py index 3f8e4c6a7..3504f1e48 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/me_emsc.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/me_emsc.py @@ -6,8 +6,10 @@ from orangecontrib.spectroscopy.preprocess.me_emsc import ME_EMSC from orangecontrib.spectroscopy.widgets.gui import lineEditFloatRange from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors -from orangecontrib.spectroscopy.widgets.preprocessors.utils import REFERENCE_DATA_PARAM, \ - BaseEditorOrange +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + REFERENCE_DATA_PARAM, + BaseEditorOrange, +) from orangecontrib.spectroscopy.widgets.preprocessors.emsc import EMSCEditor @@ -44,39 +46,67 @@ def __init__(self, parent=None, **kwargs): self.a_low = self.A_LOW self.a_high = self.A_HIGH - gui.spin(self.controlArea, self, "max_iter", label="Max iterations", minv=0, maxv=100, - controlWidth=50, callback=self.edited.emit) - gui.checkBox(self.controlArea, self, "fixed_iter", label="Always perform max iterations", - callback=self.edited.emit) - - self.comp_spin = gui.spin(self.controlArea, self, "ncomp", label="Components", - minv=3, maxv=15, - controlWidth=50, callback=self.edited.emit) - gui.checkBox(self.controlArea, self, "autoset_ncomp", - label="Automatically set components", - callback=lambda: (self._auto_click(), self.edited.emit())) + gui.spin( + self.controlArea, + self, + "max_iter", + label="Max iterations", + minv=0, + maxv=100, + controlWidth=50, + callback=self.edited.emit, + ) + gui.checkBox( + self.controlArea, + self, + "fixed_iter", + label="Always perform max iterations", + callback=self.edited.emit, + ) + + self.comp_spin = gui.spin( + self.controlArea, + self, + "ncomp", + label="Components", + minv=3, + maxv=15, + controlWidth=50, + callback=self.edited.emit, + ) + gui.checkBox( + self.controlArea, + self, + "autoset_ncomp", + label="Automatically set components", + callback=lambda: (self._auto_click(), self.edited.emit()), + ) form_set = QFormLayout() self.controlArea.layout().addLayout(form_set) bint = QBoxLayout(QBoxLayout.LeftToRight) - low = lineEditFloatRange(self, self, "n0_low", bottom=1.1, top=3, - callback=self.edited.emit) + low = lineEditFloatRange( + self, self, "n0_low", bottom=1.1, top=3, callback=self.edited.emit + ) low.sizeHintFactor = 0.4 bint.addWidget(low) - high = lineEditFloatRange(self, self, "n0_high", bottom=1.1, top=3, - callback=self.edited.emit) + high = lineEditFloatRange( + self, self, "n0_high", bottom=1.1, top=3, callback=self.edited.emit + ) high.sizeHintFactor = 0.4 bint.addWidget(high) form_set.addRow("Refractive index", bint) bint = QBoxLayout(QBoxLayout.LeftToRight) - low = lineEditFloatRange(self, self, "a_low", bottom=2, top=50, - callback=self.edited.emit) + low = lineEditFloatRange( + self, self, "a_low", bottom=2, top=50, callback=self.edited.emit + ) low.sizeHintFactor = 0.4 bint.addWidget(low) - high = lineEditFloatRange(self, self, "a_high", bottom=2, top=50, - callback=self.edited.emit) + high = lineEditFloatRange( + self, self, "a_high", bottom=2, top=50, callback=self.edited.emit + ) high.sizeHintFactor = 0.4 bint.addWidget(high) form_set.addRow("Spherical radius", bint) @@ -85,8 +115,13 @@ def __init__(self, parent=None, **kwargs): self.controlArea.layout().addWidget(self.reference_info) self.output_model = self.OUTPUT_MODEL_DEFAULT - gui.checkBox(self.controlArea, self, "output_model", "Output EMSC model", - callback=self.edited.emit) + gui.checkBox( + self.controlArea, + self, + "output_model", + "Output EMSC model", + callback=self.edited.emit, + ) self._auto_click() self._init_regions() @@ -141,9 +176,16 @@ def createinstance(cls, params): if reference is None: return lambda data: data[:0] # return an empty data table else: - return ME_EMSC(reference=reference, weights=weights, max_iter=max_iter, - fixed_iter=fixed_iter, ncomp=ncomp, n0=n0, a=a, - output_model=output_model) + return ME_EMSC( + reference=reference, + weights=weights, + max_iter=max_iter, + fixed_iter=fixed_iter, + ncomp=ncomp, + n0=n0, + a=a, + output_model=output_model, + ) preprocess_editors.register(MeEMSCEditor, 325) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/misc.py b/orangecontrib/spectroscopy/widgets/preprocessors/misc.py index 9481a6059..b5bd87bd4 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/misc.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/misc.py @@ -2,9 +2,7 @@ import pyqtgraph as pg from AnyQt.QtCore import Qt -from AnyQt.QtWidgets import ( - QComboBox, QVBoxLayout, QHBoxLayout, QFormLayout, QLabel -) +from AnyQt.QtWidgets import QComboBox, QVBoxLayout, QHBoxLayout, QFormLayout, QLabel from AnyQt.QtGui import QColor from Orange.widgets import gui @@ -13,16 +11,28 @@ from orangecontrib.spectroscopy.data import getx from orangecontrib.spectroscopy.preprocess import ( - PCADenoising, GaussianSmoothing, Cut, SavitzkyGolayFiltering, - Absorbance, Transmittance, - ShiftAndScale, SpSubtract, - MNFDenoising + PCADenoising, + GaussianSmoothing, + Cut, + SavitzkyGolayFiltering, + Absorbance, + Transmittance, + ShiftAndScale, + SpSubtract, + MNFDenoising, ) from orangecontrib.spectroscopy.preprocess.transform import SpecTypes -from orangecontrib.spectroscopy.widgets.gui import lineEditFloatRange, MovableVline, \ - connect_line, floatornone, round_virtual_pixels -from orangecontrib.spectroscopy.widgets.preprocessors.utils import BaseEditorOrange, \ - REFERENCE_DATA_PARAM +from orangecontrib.spectroscopy.widgets.gui import ( + lineEditFloatRange, + MovableVline, + connect_line, + floatornone, + round_virtual_pixels, +) +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + BaseEditorOrange, + REFERENCE_DATA_PARAM, +) from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors @@ -34,7 +44,7 @@ class GaussianSmoothingEditor(BaseEditorOrange): name = "Gaussian smoothing" qualname = "orangecontrib.infrared.gaussian" - DEFAULT_SD = 10. + DEFAULT_SD = 10.0 MINIMUM_SD = 10e-10 def __init__(self, parent=None, **kwargs): @@ -45,8 +55,15 @@ def __init__(self, parent=None, **kwargs): self.sd = self.DEFAULT_SD # editing will always return a valid output (in the range) - w = lineEditFloatRange(self, self, "sd", bottom=0., top=1000., default=self.DEFAULT_SD, - callback=self.edited.emit) + w = lineEditFloatRange( + self, + self, + "sd", + bottom=0.0, + top=1000.0, + default=self.DEFAULT_SD, + callback=self.edited.emit, + ) layout.addRow("SD", w) def setParameters(self, params): @@ -66,10 +83,10 @@ class CutEditor(BaseEditorOrange): """ Editor for Cut """ + name = "Cut" qualname = "orangecontrib.spectroscopy.cut" - replaces = ["orangecontrib.infrared.cut", - "orangecontrib.infrared.cutinverse"] + replaces = ["orangecontrib.infrared.cut", "orangecontrib.infrared.cutinverse"] class Warning(BaseEditorOrange.Warning): out_of_range = Msg("Limits are out of range.") @@ -77,17 +94,22 @@ class Warning(BaseEditorOrange.Warning): def __init__(self, parent=None, **kwargs): BaseEditorOrange.__init__(self, parent, **kwargs) - self.lowlim = 0. - self.highlim = 1. + self.lowlim = 0.0 + self.highlim = 1.0 self.inverse = False layout = QFormLayout() self.controlArea.setLayout(layout) - self._lowlime = lineEditFloatRange(self, self, "lowlim", callback=self.edited.emit) - self._highlime = lineEditFloatRange(self, self, "highlim", callback=self.edited.emit) - self._inverse = gui.radioButtons(self, self, "inverse", - orientation=Qt.Horizontal, callback=self.edited.emit) + self._lowlime = lineEditFloatRange( + self, self, "lowlim", callback=self.edited.emit + ) + self._highlime = lineEditFloatRange( + self, self, "highlim", callback=self.edited.emit + ) + self._inverse = gui.radioButtons( + self, self, "inverse", orientation=Qt.Horizontal, callback=self.edited.emit + ) gui.appendRadioButton(self._inverse, "Keep") gui.appendRadioButton(self._inverse, "Remove") @@ -116,10 +138,10 @@ def activateOptions(self): self.parent_widget.curveplot.add_marking(line) def setParameters(self, params): - if params: #parameters were manually set somewhere else + if params: # parameters were manually set somewhere else self.user_changed = True - self.lowlim = params.get("lowlim", 0.) - self.highlim = params.get("highlim", 1.) + self.lowlim = params.get("lowlim", 0.0) + self.highlim = params.get("highlim", 1.0) self.inverse = params.get("inverse", False) @staticmethod @@ -128,7 +150,9 @@ def createinstance(params): lowlim = params.get("lowlim", None) highlim = params.get("highlim", None) inverse = params.get("inverse", None) - return Cut(lowlim=floatornone(lowlim), highlim=floatornone(highlim), inverse=inverse) + return Cut( + lowlim=floatornone(lowlim), highlim=floatornone(highlim), inverse=inverse + ) def set_preview_data(self, data): self.Warning.out_of_range.clear() @@ -149,8 +173,9 @@ def set_preview_data(self, data): self.highlim = init_highlim self.edited.emit() - if (self.lowlim < minx and self.highlim < minx) \ - or (self.lowlim > maxx and self.highlim > maxx): + if (self.lowlim < minx and self.highlim < minx) or ( + self.lowlim > maxx and self.highlim > maxx + ): self.parent_widget.Warning.preprocessor() self.Warning.out_of_range() @@ -159,13 +184,14 @@ class SpSubtractEditor(BaseEditorOrange): """ Editor for preprocess.SpSubtract """ + name = "Spectrum subtraction" qualname = "orangecontrib.spectroscopy.sp_subtract" def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) - self.amount = 0. + self.amount = 0.0 form = QFormLayout() amounte = lineEditFloatRange(self, self, "amount", callback=self.edited.emit) @@ -178,7 +204,7 @@ def __init__(self, parent=None, **kwargs): self.controlArea.layout().addWidget(self.reference_info) self.reference_curve = pg.PlotCurveItem() - self.reference_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=2.)) + self.reference_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=2.0)) self.reference_curve.setZValue(10) def activateOptions(self): @@ -191,7 +217,7 @@ def set_reference_data(self, reference): self.update_reference_info() def setParameters(self, params): - self.amount = params.get("amount", 0.) + self.amount = params.get("amount", 0.0) self.reference = params.get("reference", None) self.update_reference_info() @@ -200,8 +226,11 @@ def update_reference_info(self): self.reference_info.setText("Reference: None") self.reference_curve.hide() else: - rinfo = "{0:d} spectra".format(len(self.reference)) \ - if len(self.reference) > 1 else "1 spectrum" + rinfo = ( + "{0:d} spectra".format(len(self.reference)) + if len(self.reference) > 1 + else "1 spectrum" + ) self.reference_info.setText("Reference: " + rinfo) X_ref = self.reference.X[0] x = getx(self.reference) @@ -212,7 +241,7 @@ def update_reference_info(self): @staticmethod def createinstance(params): params = dict(params) - amount = float(params.get("amount", 0.)) + amount = float(params.get("amount", 0.0)) reference = params.get(REFERENCE_DATA_PARAM, None) return SpSubtract(reference, amount=amount) @@ -221,6 +250,7 @@ class SavitzkyGolayFilteringEditor(BaseEditorOrange): """ Editor for preprocess.savitzkygolayfiltering. """ + name = "Savitzky-Golay Filter" qualname = "orangecontrib.spectroscopy.savitzkygolay" @@ -242,14 +272,35 @@ def __init__(self, parent=None, **kwargs): form = QFormLayout() - self.wspin = gui.spin(self, self, "window", minv=self.MIN_WINDOW, maxv=self.MAX_WINDOW, - step=2, callback=self._window_edited) - - self.pspin = gui.spin(self, self, "polyorder", minv=0, maxv=self.MAX_POLYORDER, - step=1, callback=self._polyorder_edited) - - self.dspin = gui.spin(self, self, "deriv", minv=0, maxv=self.MAX_DERIV, - step=1, callback=self._deriv_edited) + self.wspin = gui.spin( + self, + self, + "window", + minv=self.MIN_WINDOW, + maxv=self.MAX_WINDOW, + step=2, + callback=self._window_edited, + ) + + self.pspin = gui.spin( + self, + self, + "polyorder", + minv=0, + maxv=self.MAX_POLYORDER, + step=1, + callback=self._polyorder_edited, + ) + + self.dspin = gui.spin( + self, + self, + "deriv", + minv=0, + maxv=self.MAX_DERIV, + step=1, + callback=self._deriv_edited, + ) form.addRow("Window", self.wspin) form.addRow("Polynomial Order", self.pspin) @@ -300,6 +351,7 @@ class ShiftAndScaleEditor(BaseEditorOrange): """ Editor for ShiftAndScale """ + # TODO: the layout changes when I click the area of the preprocessor # EFFECT: the sidebar snaps in @@ -310,25 +362,27 @@ class ShiftAndScaleEditor(BaseEditorOrange): def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) - self.offset = 0. - self.scale = 1. + self.offset = 0.0 + self.scale = 1.0 form = QFormLayout() - offset_input = lineEditFloatRange(self, self, "offset", callback=self.edited.emit) + offset_input = lineEditFloatRange( + self, self, "offset", callback=self.edited.emit + ) form.addRow("Vertical offset", offset_input) scale_input = lineEditFloatRange(self, self, "scale", callback=self.edited.emit) form.addRow("Vertical scaling", scale_input) self.controlArea.setLayout(form) def setParameters(self, params): - self.amount = params.get("offset", 0.) - self.scale = params.get("scale", 1.) + self.amount = params.get("offset", 0.0) + self.scale = params.get("scale", 1.0) @staticmethod def createinstance(params): params = dict(params) - offset = float(params.get("offset", 0.)) - scale = float(params.get("scale", 1.)) + offset = float(params.get("offset", 0.0)) + scale = float(params.get("scale", 1.0)) return ShiftAndScale(offset=offset, scale=scale) @@ -344,8 +398,15 @@ def __init__(self, parent=None, **kwargs): form = QFormLayout() self.controlArea.setLayout(form) - compspin = gui.spin(self, self, "components", minv=1, maxv=100, - step=1, callback=self.edited.emit) + compspin = gui.spin( + self, + self, + "components", + minv=1, + maxv=100, + step=1, + callback=self.edited.emit, + ) endlabel = QLabel("components.") hboxLayout = QHBoxLayout() @@ -380,8 +441,15 @@ def __init__(self, parent=None, **kwargs): form = QFormLayout() self.controlArea.setLayout(form) - compspin = gui.spin(self, self, "components", minv=1, maxv=100, - step=1, callback=self.edited.emit) + compspin = gui.spin( + self, + self, + "components", + minv=1, + maxv=100, + step=1, + callback=self.edited.emit, + ) endlabel = QLabel("components.") hboxLayout = QHBoxLayout() @@ -404,8 +472,7 @@ class SpectralTransformEditor(BaseEditorOrange): name = "Spectral Transformations" qualname = "orangecontrib.spectroscopy.transforms" - TRANSFORMS = [Absorbance, - Transmittance] + TRANSFORMS = [Absorbance, Transmittance] transform_names = [a.__name__ for a in TRANSFORMS] from_names = [a.value for a in SpecTypes] @@ -437,7 +504,7 @@ def __init__(self, parent=None, **kwargs): self.controlArea.layout().addWidget(self.reference_info) self.reference_curve = pg.PlotCurveItem() - self.reference_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=2.)) + self.reference_curve.setPen(pg.mkPen(color=QColor(Qt.red), width=2.0)) self.reference_curve.setZValue(10) def activateOptions(self): @@ -453,8 +520,10 @@ def setParameters(self, params): self.update_reference_info() def parameters(self): - return {"from_type": self.fromcb.currentIndex(), - "to_type": self.tocb.currentIndex()} + return { + "from_type": self.fromcb.currentIndex(), + "to_type": self.tocb.currentIndex(), + } @staticmethod def createinstance(params): @@ -478,8 +547,11 @@ def update_reference_info(self): self.reference_info.setText("Reference: None") self.reference_curve.hide() else: - rinfo = "1st of {0:d} spectra".format(len(self.reference)) \ - if len(self.reference) > 1 else "1 spectrum" + rinfo = ( + "1st of {0:d} spectra".format(len(self.reference)) + if len(self.reference) > 1 + else "1 spectrum" + ) self.reference_info.setText("Reference: " + rinfo) X_ref = self.reference.X[0] x = getx(self.reference) @@ -488,7 +560,6 @@ def update_reference_info(self): self.reference_curve.show() - preprocess_editors.register(CutEditor, 25) preprocess_editors.register(GaussianSmoothingEditor, 75) preprocess_editors.register(SavitzkyGolayFilteringEditor, 100) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/normalize.py b/orangecontrib/spectroscopy/widgets/preprocessors/normalize.py index 3f74231a1..8ff4adb23 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/normalize.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/normalize.py @@ -1,6 +1,12 @@ import sys -from AnyQt.QtWidgets import QVBoxLayout, QFormLayout, QComboBox, QButtonGroup, QRadioButton +from AnyQt.QtWidgets import ( + QVBoxLayout, + QFormLayout, + QComboBox, + QButtonGroup, + QRadioButton, +) from Orange.data import ContinuousVariable from Orange.widgets import gui @@ -9,13 +15,20 @@ from Orange.widgets.utils.itemmodels import DomainModel from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.preprocess import Normalize, Integrate, NormalizeReference, \ - NormalizePhaseReference +from orangecontrib.spectroscopy.preprocess import ( + Normalize, + Integrate, + NormalizeReference, + NormalizePhaseReference, +) from orangecontrib.spectroscopy.widgets.gui import MovableVline from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors from orangecontrib.spectroscopy.widgets.preprocessors.integrate import IntegrateEditor -from orangecontrib.spectroscopy.widgets.preprocessors.utils import \ - BaseEditorOrange, SetXDoubleSpinBox, REFERENCE_DATA_PARAM +from orangecontrib.spectroscopy.widgets.preprocessors.utils import ( + BaseEditorOrange, + SetXDoubleSpinBox, + REFERENCE_DATA_PARAM, +) NORMALIZE_BY_REFERENCE = 42 @@ -26,6 +39,7 @@ class NormalizeEditor(BaseEditorOrange): """ Normalize spectra. """ + name = "Normalize Spectra" qualname = "orangecontrib.infrared.normalize" icon = icon_path("Normalize.svg") @@ -38,7 +52,8 @@ class NormalizeEditor(BaseEditorOrange): ("Attribute Normalization", Normalize.Attribute), ("Standard Normal Variate (SNV)", Normalize.SNV), ("Normalize by Reference", NORMALIZE_BY_REFERENCE), - ("Normalize by Reference (Complex Phase)", PHASE_REFERENCE)] + ("Normalize by Reference (Complex Phase)", PHASE_REFERENCE), + ] def __init__(self, parent=None, **kwargs): super().__init__(parent, **kwargs) @@ -49,14 +64,16 @@ def __init__(self, parent=None, **kwargs): self.lower = 0 self.upper = 4000 self.int_method = 0 - self.attrs = DomainModel(DomainModel.METAS | DomainModel.CLASSES, - valid_types=ContinuousVariable) + self.attrs = DomainModel( + DomainModel.METAS | DomainModel.CLASSES, valid_types=ContinuousVariable + ) self.attrform = QFormLayout() self.chosen_attr = None self.last_domain = None self.saved_attr = None - self.attrcb = gui.comboBox(None, self, "chosen_attr", callback=self.edited.emit, - model=self.attrs) + self.attrcb = gui.comboBox( + None, self, "chosen_attr", callback=self.edited.emit, model=self.attrs + ) self.attrform.addRow("Normalize to", self.attrcb) self.areaform = QFormLayout() @@ -64,11 +81,11 @@ def __init__(self, parent=None, **kwargs): self.int_method_cb.addItems(IntegrateEditor.Integrators) minf, maxf = -sys.float_info.max, sys.float_info.max self.lspin = SetXDoubleSpinBox( - minimum=minf, maximum=maxf, singleStep=0.5, - value=self.lower, enabled=False) + minimum=minf, maximum=maxf, singleStep=0.5, value=self.lower, enabled=False + ) self.uspin = SetXDoubleSpinBox( - minimum=minf, maximum=maxf, singleStep=0.5, - value=self.upper, enabled=False) + minimum=minf, maximum=maxf, singleStep=0.5, value=self.upper, enabled=False + ) self.areaform.addRow("Normalize to", self.int_method_cb) self.areaform.addRow("Lower limit", self.lspin) self.areaform.addRow("Upper limit", self.uspin) @@ -115,13 +132,15 @@ def activateOptions(self): if self.__method == Normalize.Area: if self.lline not in self.parent_widget.curveplot.markings: self.parent_widget.curveplot.add_marking(self.lline) - if (self.uline not in self.parent_widget.curveplot.markings - and IntegrateEditor.Integrators_classes[self.int_method] - is not Integrate.PeakAt): + if ( + self.uline not in self.parent_widget.curveplot.markings + and IntegrateEditor.Integrators_classes[self.int_method] + is not Integrate.PeakAt + ): self.parent_widget.curveplot.add_marking(self.uline) def setParameters(self, params): - if params: #parameters were manually set somewhere else + if params: # parameters were manually set somewhere else self.user_changed = True method = params.get("method", Normalize.Vector) lower = params.get("lower", 0) @@ -134,12 +153,18 @@ def setParameters(self, params): self.int_method_cb.setCurrentIndex(int_method) self.setL(lower, user=False) self.setU(upper, user=False) - self.saved_attr = params.get("attr") # chosen_attr will be set when data are connected + self.saved_attr = params.get( + "attr" + ) # chosen_attr will be set when data are connected def parameters(self): - return {"method": self.__method, "lower": self.lower, - "upper": self.upper, "int_method": self.int_method, - "attr": self.chosen_attr} + return { + "method": self.__method, + "lower": self.lower, + "upper": self.upper, + "int_method": self.int_method, + "attr": self.chosen_attr, + } def setMethod(self, method): if self.__method != method: @@ -178,8 +203,7 @@ def setU(self, upper, user=True): self.changed.emit() def reorderLimits(self): - if (IntegrateEditor.Integrators_classes[self.int_method] - is Integrate.PeakAt): + if IntegrateEditor.Integrators_classes[self.int_method] is Integrate.PeakAt: self.upper = self.lower + 10 limits = [self.lower, self.upper] self.lower, self.upper = min(limits), max(limits) @@ -211,8 +235,13 @@ def createinstance(params): int_method = IntegrateEditor.Integrators_classes[int_method_index] attr = params.get("attr", None) if method not in (NORMALIZE_BY_REFERENCE, PHASE_REFERENCE): - return Normalize(method=method, lower=lower, upper=upper, - int_method=int_method, attr=attr) + return Normalize( + method=method, + lower=lower, + upper=upper, + int_method=int_method, + attr=attr, + ) else: # avoids circular imports reference = params.get(REFERENCE_DATA_PARAM, None) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/registry.py b/orangecontrib/spectroscopy/widgets/preprocessors/registry.py index 2a1f62f52..f99dd8204 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/registry.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/registry.py @@ -1,5 +1,4 @@ class PreprocessorEditorRegistry: - def __init__(self): self.registered = [] diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/spikeremoval.py b/orangecontrib/spectroscopy/widgets/preprocessors/spikeremoval.py index fc38900ba..94e7fdddf 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/spikeremoval.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/spikeremoval.py @@ -2,8 +2,7 @@ from Orange.widgets import gui from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors -from orangecontrib.spectroscopy.widgets.preprocessors.utils import \ - BaseEditorOrange +from orangecontrib.spectroscopy.widgets.preprocessors.utils import BaseEditorOrange from orangecontrib.spectroscopy.widgets.gui import lineEditDecimalOrNone from orangecontrib.spectroscopy.preprocess import Despike @@ -12,6 +11,7 @@ class SpikeRemovalEditor(BaseEditorOrange): """ Spike Removal. """ + name = "Spike Removal" qualname = "preprocessors.spikeremoval" categories = ["Correction"] @@ -28,20 +28,37 @@ def __init__(self, parent=None, **kwargs): self.controlArea.setLayout(QVBoxLayout()) box = gui.widgetBox(self.controlArea) - self.cutoffline = lineEditDecimalOrNone(None, master=self, - bottom=0, value='cutoff', default=self.CUTOFF, - callback=self.edited.emit) + self.cutoffline = lineEditDecimalOrNone( + None, + master=self, + bottom=0, + value='cutoff', + default=self.CUTOFF, + callback=self.edited.emit, + ) self.cutoffline.setPlaceholderText(str(self.CUTOFF)) gui.widgetLabel(box, label="Cutoff:", labelWidth=50) box.layout().addWidget(self.cutoffline) - self.thresholdline = lineEditDecimalOrNone(None, master=self, bottom=0, - value='threshold', default=self.THRESHOLD, - callback=self.edited.emit) + self.thresholdline = lineEditDecimalOrNone( + None, + master=self, + bottom=0, + value='threshold', + default=self.THRESHOLD, + callback=self.edited.emit, + ) self.thresholdline.setPlaceholderText(str(self.THRESHOLD)) gui.widgetLabel(box, label='Threshold:', labelWidth=60) box.layout().addWidget(self.thresholdline) - self.distancespin = gui.spin(None, self, "dis", label="distance to average", - minv=0, maxv=1000, callback=self.edited.emit) + self.distancespin = gui.spin( + None, + self, + "dis", + label="distance to average", + minv=0, + maxv=1000, + callback=self.edited.emit, + ) gui.widgetLabel(box, label='Distance to Average:') box.layout().addWidget(self.distancespin) diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/utils.py b/orangecontrib/spectroscopy/widgets/preprocessors/utils.py index fc19a65cb..6151a6dc0 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/utils.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/utils.py @@ -11,7 +11,6 @@ class BaseEditor(BaseEditor): - name = "Unnamed" qualname = None icon = None @@ -34,6 +33,7 @@ class BaseEditorOrange(BaseEditor, OWComponent, WidgetMessagesMixin): """ Base widget for editing preprocessor's parameters that works with Orange settings. """ + # the following signals need to defined for WidgetMessagesMixin messageActivated = Signal(Msg) messageDeactivated = Signal(Msg) @@ -64,11 +64,12 @@ def parameters(self): class SetXDoubleSpinBox(DoubleSpinBox): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs, - keyboardTracking=False # disable valueChanged while typing - ) + super().__init__( + *args, + **kwargs, + keyboardTracking=False, # disable valueChanged while typing + ) def focusInEvent(self, *e): if hasattr(self, "focusIn"): @@ -87,11 +88,10 @@ def locale(self): class PreviewMinMaxMixin: - """ Classes extending the mixin need to set preview_data - """ + """Classes extending the mixin need to set preview_data""" - MINLIM_DEFAULT = 0. - MAXLIM_DEFAULT = 1. + MINLIM_DEFAULT = 0.0 + MAXLIM_DEFAULT = 1.0 def preview_min_max(self): if self.preview_data is not None: diff --git a/orangecontrib/spectroscopy/widgets/preprocessors/xas.py b/orangecontrib/spectroscopy/widgets/preprocessors/xas.py index b17e1e8dd..95da52dc0 100644 --- a/orangecontrib/spectroscopy/widgets/preprocessors/xas.py +++ b/orangecontrib/spectroscopy/widgets/preprocessors/xas.py @@ -7,19 +7,29 @@ from extranormal3 import curved_tools from orangecontrib.spectroscopy.data import getx -from orangecontrib.spectroscopy.preprocess import XASnormalization, ExtractEXAFS -from orangecontrib.spectroscopy.widgets.gui import ValueTransform, connect_settings, \ - float_to_str_decimals, lineEditFloatRange, connect_line, MovableVline +from orangecontrib.spectroscopy.preprocess import XASnormalization, ExtractEXAFS +from orangecontrib.spectroscopy.widgets.gui import ( + ValueTransform, + connect_settings, + float_to_str_decimals, + lineEditFloatRange, + connect_line, + MovableVline, +) from orangecontrib.spectroscopy.widgets.preprocessors.registry import preprocess_editors from orangecontrib.spectroscopy.widgets.preprocessors.utils import BaseEditorOrange -def init_bounds_hform(prepro_widget, - from_lim, to_lim, - title='', - from_line=None, to_line=None, - from_val_name=None, to_val_name=None): - +def init_bounds_hform( + prepro_widget, + from_lim, + to_lim, + title='', + from_line=None, + to_line=None, + from_val_name=None, + to_val_name=None, +): bounds_form = QGridLayout() title_font = QFont() @@ -46,8 +56,12 @@ def init_bounds_hform(prepro_widget, to_lim.focusIn.connect(prepro_widget.activateOptions) prepro_widget.focusIn = prepro_widget.activateOptions - if from_line is not None and to_line is not None and \ - from_val_name is not None and to_val_name is not None: + if ( + from_line is not None + and to_line is not None + and from_val_name is not None + and to_val_name is not None + ): connect_line(from_line, prepro_widget, from_val_name) from_line.sigMoveFinished.connect(prepro_widget.edited) @@ -67,7 +81,7 @@ def __init__(self, parent=None, **kwargs): self.controlArea.setLayout(QGridLayout()) curr_row = 0 - self.edge = 0. + self.edge = 0.0 edge_form = QFormLayout() edge_form.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint) edge_edit = lineEditFloatRange(self, self, "edge", callback=self.edited.emit) @@ -79,27 +93,35 @@ def __init__(self, parent=None, **kwargs): curr_row += 1 # ---------------------------- pre-edge form ------------ - self.preedge_from = self.preedge_to = 0. - self._pre_from_lim = lineEditFloatRange(self, self, "preedge_from", - callback=self.edited.emit) - self._pre_to_lim = lineEditFloatRange(self, self, "preedge_to", - callback=self.edited.emit) + self.preedge_from = self.preedge_to = 0.0 + self._pre_from_lim = lineEditFloatRange( + self, self, "preedge_from", callback=self.edited.emit + ) + self._pre_to_lim = lineEditFloatRange( + self, self, "preedge_to", callback=self.edited.emit + ) self.pre_from_line = MovableVline(label="Pre-edge start") self.pre_to_line = MovableVline(label="Pre-edge end") - preedge_form = init_bounds_hform(self, - self._pre_from_lim, self._pre_to_lim, - "Pre-edge fit:", - self.pre_from_line, self.pre_to_line, - "preedge_from", "preedge_to") + preedge_form = init_bounds_hform( + self, + self._pre_from_lim, + self._pre_to_lim, + "Pre-edge fit:", + self.pre_from_line, + self.pre_to_line, + "preedge_from", + "preedge_to", + ) self.controlArea.layout().addLayout(preedge_form, curr_row, 0, 1, 2) curr_row += 1 - self.preedge_deg = 1. + self.preedge_deg = 1.0 preedgedeg_form = QFormLayout() preedgedeg_form.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint) - preedgedeg_edit = lineEditFloatRange(self, self, "preedge_deg", - callback=self.edited.emit) + preedgedeg_edit = lineEditFloatRange( + self, self, "preedge_deg", callback=self.edited.emit + ) preedgedeg_form.addRow("poly degree", preedgedeg_edit) dummylabel2 = QLabel() dummylabel2.setText(' ') @@ -108,27 +130,35 @@ def __init__(self, parent=None, **kwargs): curr_row += 1 # ---------------------------- post-edge form ------------ - self.postedge_from = self.postedge_to = 0. - self._post_from_lim = lineEditFloatRange(self, self, "postedge_from", - callback=self.edited.emit) - self._post_to_lim = lineEditFloatRange(self, self, "postedge_to", - callback=self.edited.emit) + self.postedge_from = self.postedge_to = 0.0 + self._post_from_lim = lineEditFloatRange( + self, self, "postedge_from", callback=self.edited.emit + ) + self._post_to_lim = lineEditFloatRange( + self, self, "postedge_to", callback=self.edited.emit + ) self.post_from_line = MovableVline(label="Post-edge start") self.post_to_line = MovableVline(label="Post-edge end:") - postedge_form = init_bounds_hform(self, - self._post_from_lim, self._post_to_lim, - "Post-edge fit:", - self.post_from_line, self.post_to_line, - "postedge_from", "postedge_to") + postedge_form = init_bounds_hform( + self, + self._post_from_lim, + self._post_to_lim, + "Post-edge fit:", + self.post_from_line, + self.post_to_line, + "postedge_from", + "postedge_to", + ) self.controlArea.layout().addLayout(postedge_form, curr_row, 0, 1, 2) curr_row += 1 - self.postedge_deg = 2. + self.postedge_deg = 2.0 postedgedeg_form = QFormLayout() postedgedeg_form.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint) - postedgedeg_edit = lineEditFloatRange(self, self, "postedge_deg", - callback=self.edited.emit) + postedgedeg_edit = lineEditFloatRange( + self, self, "postedge_deg", callback=self.edited.emit + ) postedgedeg_form.addRow("poly degree", postedgedeg_edit) self.controlArea.layout().addLayout(postedgedeg_form, curr_row, 0, 1, 1) curr_row += 1 @@ -137,23 +167,27 @@ def __init__(self, parent=None, **kwargs): def activateOptions(self): self.parent_widget.curveplot.clear_markings() - for line in [self.pre_from_line, self.pre_to_line, self.post_from_line, self.post_to_line]: + for line in [ + self.pre_from_line, + self.pre_to_line, + self.post_from_line, + self.post_to_line, + ]: line.report = self.parent_widget.curveplot self.parent_widget.curveplot.add_marking(line) def setParameters(self, params): - if params: # parameters were manually set somewhere else self.user_changed = True - self.edge = params.get("edge", 0.) + self.edge = params.get("edge", 0.0) - self.preedge_from = params.get("preedge_from", 0.) - self.preedge_to = params.get("preedge_to", 0.) + self.preedge_from = params.get("preedge_from", 0.0) + self.preedge_to = params.get("preedge_to", 0.0) self.preedge_deg = params.get("preedge_deg", 1) - self.postedge_from = params.get("postedge_from", 0.) - self.postedge_to = params.get("postedge_to", 0.) + self.postedge_from = params.get("postedge_from", 0.0) + self.postedge_to = params.get("postedge_to", 0.0) self.postedge_deg = params.get("postedge_deg", 2) def set_preview_data(self, data): @@ -171,7 +205,9 @@ def set_preview_data(self, data): if not self.user_changed: if data: y = data.X[0] - maxderiv_idx = np.argmax(curved_tools.derivative_vals(np.array([x, y]))) + maxderiv_idx = np.argmax( + curved_tools.derivative_vals(np.array([x, y])) + ) self.edge = x[maxderiv_idx] else: self.edge = (max(x) - min(x)) / 2 @@ -188,32 +224,31 @@ def set_preview_data(self, data): def createinstance(params): params = dict(params) - edge = float(params.get("edge", 0.)) + edge = float(params.get("edge", 0.0)) preedge = {} - preedge['from'] = float(params.get("preedge_from", 0.)) - preedge['to'] = float(params.get("preedge_to", 0.)) + preedge['from'] = float(params.get("preedge_from", 0.0)) + preedge['to'] = float(params.get("preedge_to", 0.0)) preedge['deg'] = int(params.get("preedge_deg", 1)) postedge = {} - postedge['from'] = float(params.get("postedge_from", 0.)) - postedge['to'] = float(params.get("postedge_to", 0.)) + postedge['from'] = float(params.get("postedge_from", 0.0)) + postedge['to'] = float(params.get("postedge_to", 0.0)) postedge['deg'] = int(params.get("postedge_deg", 2)) return XASnormalization(edge=edge, preedge_dict=preedge, postedge_dict=postedge) class E2K(ValueTransform): - def __init__(self, xas_prepro_widget): self.xas_prepro_widget = xas_prepro_widget def transform(self, v): - res = np.sqrt(0.2625 * (float(v)-float(self.xas_prepro_widget.edge))) + res = np.sqrt(0.2625 * (float(v) - float(self.xas_prepro_widget.edge))) return Decimal(float_to_str_decimals(res, 2)) def inverse(self, v): - res = (float(v)**2)/0.2625+float(self.xas_prepro_widget.edge) + res = (float(v) ** 2) / 0.2625 + float(self.xas_prepro_widget.edge) return Decimal(float_to_str_decimals(res, 2)) @@ -232,41 +267,50 @@ def __init__(self, parent=None, **kwargs): self.controlArea.setLayout(QGridLayout()) curr_row = 0 - self.edge = 0. + self.edge = 0.0 edge_form = QFormLayout() edge_form.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint) edge_edit = lineEditFloatRange(self, self, "edge", callback=self.edited.emit) edge_form.addRow("Edge", edge_edit) dummylabel = QLabel() dummylabel.setText(' ') - edge_form.addWidget(dummylabel) # adding vertical space + edge_form.addWidget(dummylabel) # adding vertical space self.controlArea.layout().addLayout(edge_form, curr_row, 0, 1, 1) curr_row += 1 - self.extra_from = self.extra_to = 0. - self._extrafrom_lim = lineEditFloatRange(self, self, "extra_from", - callback=self.edited.emit) - self._extrato_lim = lineEditFloatRange(self, self, "extra_to", - callback=self.edited.emit) + self.extra_from = self.extra_to = 0.0 + self._extrafrom_lim = lineEditFloatRange( + self, self, "extra_from", callback=self.edited.emit + ) + self._extrato_lim = lineEditFloatRange( + self, self, "extra_to", callback=self.edited.emit + ) self.extrafrom_line = MovableVline(label="Extraction start") self.extrato_line = MovableVline(label="Extraction end") - extrabounds_form = init_bounds_hform(self, - self._extrafrom_lim, self._extrato_lim, - "Energy bounds:", - self.extrafrom_line, self.extrato_line, - "extra_from", "extra_to") + extrabounds_form = init_bounds_hform( + self, + self._extrafrom_lim, + self._extrato_lim, + "Energy bounds:", + self.extrafrom_line, + self.extrato_line, + "extra_from", + "extra_to", + ) self.controlArea.layout().addLayout(extrabounds_form, curr_row, 0, 1, 2) curr_row += 1 - self.extra_fromK = self.extra_toK = 0. - self._extrafromK_lim = lineEditFloatRange(self, self, "extra_fromK", - callback=self.edited.emit) - self._extratoK_lim = lineEditFloatRange(self, self, "extra_toK", - callback=self.edited.emit) - Kbounds_form = init_bounds_hform(self, - self._extrafromK_lim, self._extratoK_lim, - "K bounds:") + self.extra_fromK = self.extra_toK = 0.0 + self._extrafromK_lim = lineEditFloatRange( + self, self, "extra_fromK", callback=self.edited.emit + ) + self._extratoK_lim = lineEditFloatRange( + self, self, "extra_toK", callback=self.edited.emit + ) + Kbounds_form = init_bounds_hform( + self, self._extrafromK_lim, self._extratoK_lim, "K bounds:" + ) self.controlArea.layout().addLayout(Kbounds_form, curr_row, 0, 1, 2) curr_row += 1 @@ -277,7 +321,9 @@ def __init__(self, parent=None, **kwargs): self.poly_deg = 0 polydeg_form = QFormLayout() polydeg_form.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint) - polydeg_edit = lineEditFloatRange(self, self, "poly_deg", callback=self.edited.emit) + polydeg_edit = lineEditFloatRange( + self, self, "poly_deg", callback=self.edited.emit + ) titlabel.setText("Polynomial degree:") polydeg_form.addRow(titlabel, polydeg_edit) dummylabel2 = QLabel() @@ -289,7 +335,9 @@ def __init__(self, parent=None, **kwargs): self.kweight = 0 kweight_form = QFormLayout() kweight_form.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint) - kweight_edit = lineEditFloatRange(self, self, "kweight", callback=self.edited.emit) + kweight_edit = lineEditFloatRange( + self, self, "kweight", callback=self.edited.emit + ) kweight_form.addRow("Kweight (fit)", kweight_edit) self.controlArea.layout().addLayout(kweight_form, curr_row, 0, 1, 1) curr_row += 1 @@ -312,14 +360,13 @@ def activateOptions(self): self.parent_widget.curveplot.add_marking(line) def setParameters(self, params): - if params: # parameters were manually set somewhere else self.user_changed = True - self.edge = params.get("edge", 0.) + self.edge = params.get("edge", 0.0) - self.extra_from = params.get("extra_from", 0.) - self.extra_to = params.get("extra_to", 0.) + self.extra_from = params.get("extra_from", 0.0) + self.extra_to = params.get("extra_to", 0.0) self.poly_deg = params.get("poly_deg", 0) self.kweight = params.get("kweight", 0) @@ -338,7 +385,9 @@ def set_preview_data(self, data): if not self.user_changed: if data: y = data.X[0] - maxderiv_idx = np.argmax(curved_tools.derivative_vals(np.array([x, y]))) + maxderiv_idx = np.argmax( + curved_tools.derivative_vals(np.array([x, y])) + ) self.edge = x[maxderiv_idx] else: self.edge = (max(x) - min(x)) / 2 @@ -354,17 +403,23 @@ def set_preview_data(self, data): def createinstance(params): params = dict(params) - edge = float(params.get("edge", 0.)) + edge = float(params.get("edge", 0.0)) - extra_from = float(params.get("extra_from", 0.)) - extra_to = float(params.get("extra_to", 0.)) + extra_from = float(params.get("extra_from", 0.0)) + extra_to = float(params.get("extra_to", 0.0)) poly_deg = int(params.get("poly_deg", 0)) kweight = int(params.get("kweight", 0)) m = int(params.get("m", 0)) - return ExtractEXAFS(edge=edge, extra_from=extra_from, extra_to=extra_to, - poly_deg=poly_deg, kweight=kweight, m=m) + return ExtractEXAFS( + edge=edge, + extra_from=extra_from, + extra_to=extra_to, + poly_deg=poly_deg, + kweight=kweight, + m=m, + ) preprocess_editors.register(XASnormalizationEditor, 900) diff --git a/orangecontrib/spectroscopy/widgets/utils.py b/orangecontrib/spectroscopy/widgets/utils.py index a860ffa77..2568144ba 100644 --- a/orangecontrib/spectroscopy/widgets/utils.py +++ b/orangecontrib/spectroscopy/widgets/utils.py @@ -3,8 +3,11 @@ import numpy as np from Orange.data import Table -from Orange.widgets.utils.annotated_data import \ - ANNOTATED_DATA_SIGNAL_NAME, create_annotated_table, create_groups_table +from Orange.widgets.utils.annotated_data import ( + ANNOTATED_DATA_SIGNAL_NAME, + create_annotated_table, + create_groups_table, +) from Orange.widgets.settings import Setting from Orange.widgets.widget import OWWidget, Msg, Output @@ -16,7 +19,12 @@ def pack_selection(selection_group): if len(nonzero_indices) == 0: return None if len(nonzero_indices) < 1000: - return [(a, b) for a, b in zip(nonzero_indices, selection_group[nonzero_indices])] + return [ + (a, b) + for a, b in zip( + nonzero_indices, selection_group[nonzero_indices], strict=True + ) + ] else: # much faster than array.array("B", selection_group) a = array.array("B") @@ -25,7 +33,7 @@ def pack_selection(selection_group): def unpack_selection(saved_selection): - """ Return an numpy array of np.uint8 representing the selection. + """Return an numpy array of np.uint8 representing the selection. The array can be smaller than the size of data.""" if saved_selection is None or len(saved_selection) == 0: return np.array([], dtype=np.uint8) @@ -75,7 +83,9 @@ def __init__(self): def restore_selection_settings(self): self.selection_group = unpack_selection(self._pending_selection_restore) - self.selection_group = selections_to_length(self.selection_group, len(self.data)) + self.selection_group = selections_to_length( + self.selection_group, len(self.data) + ) self._pending_selection_restore = None def prepare_settings_for_saving(self): @@ -83,7 +93,6 @@ def prepare_settings_for_saving(self): class SelectionOutputsMixin: - # older versions did not include the "Group" feature # fot the selected output compat_no_group = Setting(False, schema_only=True) @@ -107,17 +116,19 @@ def _send_selection(self, data, selection_group, no_group=False): selected = None if data: - if no_group and data: # compatibility mode, the output used to lack the group column + if ( + no_group and data + ): # compatibility mode, the output used to lack the group column selection_indices = np.flatnonzero(selection_group) selected = data[selection_indices] else: - selected = create_groups_table(data, - selection_group, False, "Group") + selected = create_groups_table(data, selection_group, False, "Group") selected = selected if selected else None self.Outputs.selected_data.send(selected if selected else None) return annotated_data, selected def send_selection(self, data, selection_group): - return self._send_selection(data, selection_group, - no_group=self.compat_no_group) + return self._send_selection( + data, selection_group, no_group=self.compat_no_group + ) diff --git a/orangecontrib/spectroscopy/widgets/visual_settings.py b/orangecontrib/spectroscopy/widgets/visual_settings.py index c85baf20d..6a8ad25cd 100644 --- a/orangecontrib/spectroscopy/widgets/visual_settings.py +++ b/orangecontrib/spectroscopy/widgets/visual_settings.py @@ -2,7 +2,11 @@ from orangewidget.utils.visual_settings_dlg import _add_control, _set_control_value -from orangecontrib.spectroscopy.widgets.gui import FloatOrEmptyValidator, floatornone, str_or_empty +from orangecontrib.spectroscopy.widgets.gui import ( + FloatOrEmptyValidator, + floatornone, + str_or_empty, +) class FloatOrUndefined: @@ -10,7 +14,6 @@ class FloatOrUndefined: class FloatOrEmptyLineEdit(QLineEdit): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) validator = FloatOrEmptyValidator(self, allow_empty=True) diff --git a/pylintrc b/pylintrc deleted file mode 100644 index 02e76ac05..000000000 --- a/pylintrc +++ /dev/null @@ -1,335 +0,0 @@ -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Pickle collected data for later comparisons. -persistent=yes - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins=pylint.extensions.eq_without_hash - -# Use multiple processes to speed up Pylint. -jobs=0 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist=Orange.distance._distance,Orange.data._variable,numpy.random.mtrand - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable= - missing-docstring, # I guess not - no-name-in-module, no-member, # too many false positives from Qt - import-error, # false positives, and we don't expect any true positives - too-many-ancestors, # I don't think this is a problem - no-else-return, # else's may actually improve readability - duplicate-code # too strict; we're disciplined and don't do this by mistake - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=error + warning + refactor + convention - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=100 - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=yes - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format=LF - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=yes - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_$|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb,on_,_on_ - - -[BASIC] - -# Good variable names which should always be accepted, separated by a comma -good-names=ex,Run,_,a,c,d,e,i,j,k,m,n,o,p,t,u,v,w,x,y,A,X,Y,M - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=yes - -# Regular expression matching correct method names -# TODO: find a better way to allow long method names in test classes. -method-rgx=[a-z_][a-zA-Z0-9_]{2,80}$ - -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]{2,30}$ - -# Regular expression matching correct module names -module-rgx=([a-z_][a-z0-9_]{2,30})$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=[A-Za-z_][A-Za-z0-9_]{2,30}$ - -# Regular expression matching correct constant names -const-rgx=[A-Za-z_][A-Za-z0-9_]+$ - -# Regular expression matching correct function names -function-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ - -# Regular expression matching correct variable names -variable-rgx=[a-zA-Z_][a-zA-Z0-9_]{0,30}$ - -# Regular expression matching correct attribute names -attr-rgx=[a-z_][a-zA-Z0-9_]*$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct argument names -argument-rgx=[a-z_][a-zA-Z0-9_]*$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=7 - - -[ELIF] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignored-checks-for-mixins=no-member - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=numpy,PyQt4,scipy - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). This supports can work -# with qualified names. -ignored-classes= - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO,HACK - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=20 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=^_ - -# Maximum number of locals for function / method body -max-locals=60 - -# Maximum number of return / yield for function / method body -max-returns=20 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of statements in function / method body -max-statements=200 - -# Maximum number of parents for a class (see R0901). -max-parents=9 - -# Maximum number of attributes for a class (see R0902). -max-attributes=20 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=0 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=40 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=10 - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=optparse - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=builtins.Exception - -[isort] -known-standard-library= diff --git a/pyproject.toml b/pyproject.toml index 66b052e92..699b0be5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,92 @@ [build-system] requires = [ - 'setuptools', - 'wheel', + 'setuptools>=61.0', 'recommonmark', ] build-backend = 'setuptools.build_meta' + +[project] +name = "Orange-Spectroscopy" +description = "Extends Orange to handle spectral and hyperspectral analysis." +authors = [ + { name="Canadian Light Source, Biolab UL, Soleil, Elettra", email="marko.toplak@gmail.com"} +] +version = "0.9.1" +readme = {file = "README.pypi", content-type = "text/markdown"} +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "Operating System :: OS Independent", +] +keywords = ["orange3 add-on", "spectroscopy", "infrared"] +dependencies = [ + 'numpy>=1.24.0', + 'Orange3>=3.38.0', + 'orange-canvas-core>=0.2.4', + 'orange-widget-base>=4.25.0', + 'scipy>=1.10.0,<1.17.0', + 'scikit-learn>=1.5.1', + 'spectral>=0.22.3,!=0.23', + 'serverfiles>=0.2', + 'AnyQt>=0.2.0', + 'pandas>=1.5.3', + 'pyqtgraph>=0.13.1', + 'colorcet', + 'h5py', + 'extranormal3 >=0.0.3', + 'renishawWiRE>=0.1.8', + 'pillow>=9.2.0', + 'lmfit>=1.3.3', + 'bottleneck', + 'pebble', + 'agilent-format>=0.4.5', + 'pySNOM>=0.2.0', +] + +[project.optional-dependencies] +doc = [ + "sphinx", + "recommonmark", + "sphinx_rtd_theme", +] +test = [ + "coverage", +] +dev = [ + "pre-commit", + "ruff", +] + +[project.urls] +Homepage = "https://github.com/Quasars/orange-spectroscopy" +Issues = "https://github.com/Quasars/orange-spectroscopy/issues" + +[project.entry-points."orange3.addon"] +spectroscopy = "orangecontrib.spectroscopy" + +[project.entry-points."orange.widgets.tutorials"] +"Spectroscopy Examples" = "orangecontrib.spectroscopy.tutorials" + +[project.entry-points."orange.widgets"] +"Spectroscopy" = "orangecontrib.spectroscopy.widgets" + +[project.entry-points."orange.canvas.help"] +html-index = "orangecontrib.spectroscopy.widgets:WIDGET_HELP_PATH" + +[tool.setuptools.packages.find] +include = ["orangecontrib*"] + +[tool.ruff] +force-exclude = true + +[tool.ruff.lint] +select = ["E4", "E7", "E9", "F", "B"] +ignore = ["E741", "B904"] + +[tool.ruff.lint.per-file-ignores] +# allow unused imports +"__init__.py" = ["F401"] + +[tool.ruff.format] +quote-style = "preserve" \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000..30f858e6b --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[options] +namespace_packages = orangecontrib \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 99e345dae..000000000 --- a/setup.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python - -import os -from os import walk, path -import subprocess -import sys - -from setuptools import setup, find_packages, Command - -PACKAGES = find_packages() - -PACKAGE_DATA = {} - -README_FILE = os.path.join(os.path.dirname(__file__), 'README.pypi') -LONG_DESCRIPTION = open(README_FILE, "rt", encoding="utf8").read() - -DATA_FILES = [ - # Data files that will be installed outside site-packages folder -] - -ENTRY_POINTS = { - # Entry points that marks this package as an orange add-on. If set, addon will - # be shown in the add-ons manager even if not published on PyPi. - 'orange3.addon': ( - 'spectroscopy = orangecontrib.spectroscopy', - ), - - # Entry point used to specify packages containing tutorials accessible - # from welcome screen. Tutorials are saved Orange Workflows (.ows files). - 'orange.widgets.tutorials': ( - # Syntax: any_text = path.to.package.containing.tutorials - 'infraredtutorials = orangecontrib.spectroscopy.tutorials', - ), - - # Entry point used to specify packages containing widgets. - 'orange.widgets': ( - # Syntax: category name = path.to.package.containing.widgets - # Widget category specification can be seen in - # orangecontrib/example/widgets/__init__.py - 'Spectroscopy = orangecontrib.spectroscopy.widgets', - ), - - # Register widget help - "orange.canvas.help": ( - 'html-index = orangecontrib.spectroscopy.widgets:WIDGET_HELP_PATH',) - -} - -KEYWORDS = [ - # [PyPi](https://pypi.python.org) packages with keyword "orange3 add-on" - # can be installed using the Orange Add-on Manager - 'orange3 add-on', - 'spectroscopy', - 'infrared' -] - - -class CoverageCommand(Command): - """A setup.py coverage subcommand developers can run locally.""" - description = "run code coverage" - user_options = [] - initialize_options = finalize_options = lambda self: None - - def run(self): - """Check coverage on current workdir""" - sys.exit(subprocess.call(r''' - coverage run -m unittest - echo; echo - coverage report - coverage html && - { echo; echo "See also: file://$(pwd)/htmlcov/index.html"; echo; } - ''', shell=True, cwd=os.path.dirname(os.path.abspath(__file__)))) - - -class LintCommand(Command): - """A setup.py lint subcommand developers can run locally.""" - description = "run code linter(s)" - user_options = [] - initialize_options = finalize_options = lambda self: None - - def run(self): - """Lint current branch compared to a reasonable master branch""" - sys.exit(subprocess.call(r''' - set -eu - upstream="$(git remote -v | - awk '/[@\/]github.com[:\/]Quasars\/orange-spectroscopy[\. ]/{ print $1; exit }')" - git fetch -q $upstream master - best_ancestor=$(git merge-base HEAD refs/remotes/$upstream/master) - .github/workflows/check_pylint_diff.sh $best_ancestor - ''', shell=True, cwd=os.path.dirname(os.path.abspath(__file__)))) - - -TEST_SUITE = "orangecontrib.spectroscopy.tests.suite" - - -def include_documentation(local_dir, install_dir): - global DATA_FILES - - doc_files = [] - for dirpath, _, files in walk(local_dir): - doc_files.append((dirpath.replace(local_dir, install_dir), - [path.join(dirpath, f) for f in files])) - DATA_FILES.extend(doc_files) - - -if __name__ == '__main__': - - cmdclass = { - 'coverage': CoverageCommand, - 'lint': LintCommand, - } - - include_documentation('doc/build/htmlhelp', 'help/orange-spectroscopy') - - setup( - name="Orange-Spectroscopy", - python_requires='>3.8.0', - description='Extends Orange to handle spectral and hyperspectral analysis.', - long_description=LONG_DESCRIPTION, - long_description_content_type='text/markdown', - author='Canadian Light Source, Biolab UL, Soleil, Elettra', - author_email='marko.toplak@gmail.com', - version="0.9.1", - packages=PACKAGES, - package_data=PACKAGE_DATA, - data_files=DATA_FILES, - install_requires=[ - 'setuptools>=51.0.0', # same as the last one needed for Orange3 - 'pip>=19.3', # same as for Orange 3.38 - 'numpy>=1.24.0', - 'Orange3>=3.38.0', - 'orange-canvas-core>=0.2.4', - 'orange-widget-base>=4.25.0', - 'scipy>=1.10.0,<1.17.0', - 'scikit-learn>=1.5.1', - 'spectral>=0.22.3,!=0.23', - 'serverfiles>=0.2', - 'AnyQt>=0.2.0', - 'pandas>=1.5.3', - 'pyqtgraph>=0.13.1', - 'colorcet', - 'h5py', - 'extranormal3 >=0.0.3', - 'renishawWiRE>=0.1.8', - 'pillow>=9.2.0', - 'lmfit>=1.3.3', - 'bottleneck', - 'pebble', - 'agilent-format>=0.4.5', - 'pySNOM>=0.2.0', - ], - extras_require={ - 'test': ['coverage'], - 'doc': ['sphinx', 'recommonmark'], - }, - entry_points=ENTRY_POINTS, - keywords=KEYWORDS, - namespace_packages=['orangecontrib'], - test_suite=TEST_SUITE, - include_package_data=True, - zip_safe=False, - url="https://github.com/Quasars/orange-spectroscopy", - cmdclass=cmdclass, - license='GPLv3+', - ) diff --git a/tox.ini b/tox.ini index bfbdc005f..5c83ce889 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] envlist = orange-{oldest, latest, released} - pylint-ci skip_missing_interpreters = true isolated_build = true @@ -33,6 +32,8 @@ deps = oldest: lmfit==1.3.3 oldest: pillow==9.2.0 oldest: pySNOM==0.2.0 + oldest: chardet<7 # until oldest orange3 supports chardet 7 + released: chardet<7 # until released orange3 supports chardet 7 latest: https://github.com/biolab/orange3/archive/refs/heads/master.zip#egg=orange3 latest: https://github.com/biolab/orange-canvas-core/archive/refs/heads/master.zip#egg=orange-canvas-core latest: https://github.com/biolab/orange-widget-base/archive/refs/heads/master.zip#egg=orange-widget-base @@ -49,11 +50,3 @@ commands = coverage run -m unittest orangecontrib.spectroscopy.tests coverage report coverage xml -o {toxinidir}/coverage.xml - -[testenv:pylint-ci] -changedir = {toxinidir} -skip_install = true -allowlist_externals = bash -deps = pylint -commands = - bash .github/workflows/check_pylint_diff.sh