-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/dei 225 filter extremes rule (#122)
* add first setup filter extremes rule * test on how to implement find_peaks method * first working prototype filter extremes rule * update working version filter extremes with distance * use distance and mask from user input * implement timeoperationsettings * WIP set up test * first Test * adding tests for extreme filter rule * add tests for parser and data object * update documentation * acceptance test, mkdocs and clean up * update documentation * pylint * update review part 1 * update review part 2 * clarify documentation to include local maxima and local minima * change sentence for readability * add check on extreme_type options * import mistake * update test * Update parser_filter_extremes_rule.py * fix for lower python versions (<3.11) * and get back the str not the enumerate --------- Co-authored-by: mKlapwijk <maarten.klapwijk@deltares.nl>
- Loading branch information
1 parent
e0455ce
commit c1bbe1b
Showing
25 changed files
with
1,231 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
decoimpact/business/entities/rules/filter_extremes_rule.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# This file is part of D-EcoImpact | ||
# Copyright (C) 2022-2024 Stichting Deltares | ||
# This program is free software distributed under the | ||
# GNU Affero General Public License version 3.0 | ||
# A copy of the GNU Affero General Public License can be found at | ||
# https://github.com/Deltares/D-EcoImpact/blob/main/LICENSE.md | ||
""" | ||
Module for FilterExtremesRule class | ||
Classes: | ||
FilterExtremesRule | ||
""" | ||
|
||
from typing import List | ||
|
||
import xarray as _xr | ||
import scipy as _sc | ||
import numpy as _np | ||
|
||
from decoimpact.business.entities.rules.i_array_based_rule import IArrayBasedRule | ||
from decoimpact.business.entities.rules.options.options_filter_extreme_rule import ( | ||
ExtremeTypeOptions, | ||
) | ||
from decoimpact.business.entities.rules.rule_base import RuleBase | ||
from decoimpact.business.entities.rules.time_operation_settings import ( | ||
TimeOperationSettings, | ||
) | ||
from decoimpact.business.utils.data_array_utils import get_time_dimension_name | ||
from decoimpact.crosscutting.i_logger import ILogger | ||
from decoimpact.data.dictionary_utils import get_dict_element | ||
|
||
|
||
class FilterExtremesRule(RuleBase, IArrayBasedRule): | ||
"""Implementation for the filter extremes rule""" | ||
|
||
# pylint: disable=too-many-arguments | ||
def __init__( | ||
self, | ||
name: str, | ||
input_variable_names: List[str], | ||
extreme_type: ExtremeTypeOptions, | ||
distance: int, | ||
time_scale: str, | ||
mask: bool, | ||
): | ||
super().__init__(name, input_variable_names) | ||
self._settings = TimeOperationSettings( | ||
{"second": "s", "hour": "h", "day": "D", "month": "M", "year": "Y"} | ||
) | ||
self._extreme_type: ExtremeTypeOptions = extreme_type | ||
self._distance = distance | ||
self._settings.time_scale = time_scale | ||
self._mask = mask | ||
|
||
@property | ||
def settings(self): | ||
"""Time operation settings""" | ||
return self._settings | ||
|
||
@property | ||
def extreme_type(self) -> ExtremeTypeOptions: | ||
"""Type of extremes (peaks or troughs)""" | ||
return self._extreme_type | ||
|
||
@property | ||
def distance(self) -> int: | ||
"""Minimal distance between peaks""" | ||
return self._distance | ||
|
||
@property | ||
def mask(self) -> bool: | ||
"""Return either directly the values of the filtered array or a | ||
True/False array""" | ||
return self._mask | ||
|
||
def validate(self, logger: ILogger) -> bool: | ||
"""Validates if the rule is valid | ||
Returns: | ||
bool: wether the rule is valid | ||
""" | ||
return self.settings.validate(self.name, logger) | ||
|
||
def execute(self, value_array: _xr.DataArray, logger: ILogger) -> _xr.DataArray: | ||
""" | ||
Retrieve the extremes | ||
extreme_type: Either retrieve the values at the peaks or troughs | ||
mask: If False return the values at the peaks, otherwise return a | ||
1 at the extreme locations. | ||
Args: | ||
value_array (DataArray): Values to filter at extremes | ||
Returns: | ||
DataArray: Filtered DataArray with only the extremes remaining | ||
at all other times the values are set to NaN | ||
""" | ||
|
||
time_scale = get_dict_element( | ||
self.settings.time_scale, self.settings.time_scale_mapping | ||
) | ||
|
||
time_dim_name = get_time_dimension_name(value_array, logger) | ||
time = value_array.time.values | ||
timestep = (time[-1] - time[0]) / len(time) | ||
width_time = _np.timedelta64(self.distance, time_scale) | ||
distance = width_time / timestep | ||
|
||
results = _xr.apply_ufunc( | ||
self._process_peaks, | ||
value_array, | ||
input_core_dims=[[time_dim_name]], | ||
output_core_dims=[[time_dim_name]], | ||
vectorize=True, | ||
kwargs={ | ||
"distance": distance, | ||
"mask": self.mask, | ||
"extreme_type": self.extreme_type, | ||
}, | ||
) | ||
|
||
results = results.transpose(*value_array.dims) | ||
return results | ||
|
||
def _process_peaks( | ||
self, arr: _xr.DataArray, distance: float, mask: bool, extreme_type: str | ||
): | ||
factor = 1 | ||
if extreme_type == "troughs": | ||
factor = -1 | ||
peaks, _ = _sc.signal.find_peaks(factor * arr, distance=distance) | ||
values = arr[peaks] | ||
if mask: | ||
values = True | ||
new_arr = _np.full_like(arr, _np.nan, dtype=float) | ||
new_arr[peaks] = values | ||
return new_arr |
File renamed without changes.
20 changes: 20 additions & 0 deletions
20
decoimpact/business/entities/rules/options/options_filter_extreme_rule.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# This file is part of D-EcoImpact | ||
# Copyright (C) 2022-2024 Stichting Deltares | ||
# This program is free software distributed under the | ||
# GNU Affero General Public License version 3.0 | ||
# A copy of the GNU Affero General Public License can be found at | ||
# https://github.com/Deltares/D-EcoImpact/blob/main/LICENSE.md | ||
""" | ||
Module for ExtremeTypeOptions Class | ||
Classes: | ||
ExtremeTypeOptions | ||
""" | ||
from enum import Enum | ||
|
||
|
||
class ExtremeTypeOptions(str, Enum): | ||
"""Classify the extreme type options.""" | ||
|
||
PEAKS = "peaks" | ||
TROUGHS = "troughs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# This file is part of D-EcoImpact | ||
# Copyright (C) 2022-2024 Stichting Deltares | ||
# This program is free software distributed under the | ||
# GNU Affero General Public License version 3.0 | ||
# A copy of the GNU Affero General Public License can be found at | ||
# https://github.com/Deltares/D-EcoImpact/blob/main/LICENSE.md | ||
"""Module for IFilterExtremesRuleData interface | ||
Interfaces: | ||
IFilterExtremesRuleData | ||
""" | ||
|
||
from abc import ABC, abstractmethod | ||
from typing import List | ||
|
||
from decoimpact.data.api.i_rule_data import IRuleData | ||
|
||
|
||
class IFilterExtremesRuleData(IRuleData, ABC): | ||
"""Data for a filter extremes rule""" | ||
|
||
@property | ||
@abstractmethod | ||
def input_variables(self) -> List[str]: | ||
"""List with input variable name""" | ||
|
||
@property | ||
@abstractmethod | ||
def extreme_type(self) -> str: | ||
"""Type of extremes [peaks or throughs]""" | ||
|
||
@property | ||
@abstractmethod | ||
def distance(self) -> int: | ||
"""Property for the distance between peaks""" | ||
|
||
@property | ||
@abstractmethod | ||
def time_scale(self) -> str: | ||
"""Property for the timescale of the distance between peaks""" | ||
|
||
@property | ||
@abstractmethod | ||
def mask(self) -> bool: | ||
"""Property for mask""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# This file is part of D-EcoImpact | ||
# Copyright (C) 2022-2024 Stichting Deltares | ||
# This program is free software distributed under the | ||
# GNU Affero General Public License version 3.0 | ||
# A copy of the GNU Affero General Public License can be found at | ||
# https://github.com/Deltares/D-EcoImpact/blob/main/LICENSE.md | ||
""" | ||
Module for FilterExtremesRuleData class | ||
Classes: | ||
FilterExtremesRuleData | ||
""" | ||
|
||
from typing import List | ||
|
||
from decoimpact.data.api.i_filter_extremes_rule_data import IFilterExtremesRuleData | ||
from decoimpact.data.entities.rule_data import RuleData | ||
|
||
|
||
class FilterExtremesRuleData(IFilterExtremesRuleData, RuleData): | ||
"""Class for storing data related to filter extremes rule""" | ||
|
||
# pylint: disable=too-many-arguments | ||
def __init__( | ||
self, | ||
name: str, | ||
input_variables: List[str], | ||
extreme_type: str, | ||
distance: int, | ||
time_scale: str, | ||
mask: bool, | ||
): | ||
super().__init__(name) | ||
self._input_variables = input_variables | ||
self._extreme_type = extreme_type | ||
self._distance = distance | ||
self._time_scale = time_scale | ||
self._mask = mask | ||
|
||
@property | ||
def input_variables(self) -> List[str]: | ||
"""List with input variables""" | ||
return self._input_variables | ||
|
||
@property | ||
def extreme_type(self) -> str: | ||
"""Property for the extremes type""" | ||
return self._extreme_type | ||
|
||
@property | ||
def distance(self) -> int: | ||
"""Property for the distance between peaks""" | ||
return self._distance | ||
|
||
@property | ||
def time_scale(self) -> str: | ||
"""Property for the timescale of the distance between peaks""" | ||
return self._time_scale | ||
|
||
@property | ||
def mask(self) -> bool: | ||
"""Property for mask""" | ||
return self._mask |
Oops, something went wrong.