Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pyPRMS/metadata/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(self, version: str | Version = PRMS_VERSION,

fcn_map = {'control': self.__control_to_dict,
'dimensions': self.__dimensions_to_dict,
'parameters': self.__parameters_to_dict,
'parameters': self._parameters_to_dict,
'variables': self.__variables_to_dict,
'data_file': self.__data_file_to_dict,
'cbh': self.__cbh_to_dict}
Expand Down Expand Up @@ -250,9 +250,9 @@ def __control_to_dict(self, xml_root: xmlET.Element,

return meta_dict

def __parameters_to_dict(self, xml_root: xmlET.Element,
meta_type: str,
req_version: Version) -> dict:
def _parameters_to_dict(self, xml_root: xmlET.Element,
meta_type: str,
req_version: Version) -> dict:
"""Convert parameter metadata to dictionary.

:param xml_root: XML root element
Expand Down
13 changes: 7 additions & 6 deletions pyPRMS/parameters/ParamDb.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

import os
import pandas as pd # type: ignore
from typing import cast, Optional
from typing import cast

from ..constants import PRMS_VERSION
from ..Exceptions_custom import ParameterNotValidError
Expand All @@ -17,7 +18,7 @@
class ParamDb(Parameters):
def __init__(self, paramdb_dir: str,
metadata,
verbose: Optional[bool] = False):
verbose: bool = False):
"""Initialize ParamDb object.

This object handles the monolithic parameter database.
Expand All @@ -26,7 +27,7 @@ def __init__(self, paramdb_dir: str,
:param verbose: Output additional debug information
"""

super(ParamDb, self).__init__(metadata=metadata, verbose=verbose)
super().__init__(metadata=metadata, verbose=verbose)

global con
con = get_console_instance()
Expand Down Expand Up @@ -59,9 +60,9 @@ def _read(self):

# Create a MetaData object to use its parameter parsing function
mobj = MetaData()
pvt_meta = mobj._MetaData__parameters_to_dict(xml_root=params_root,
meta_type='parameters',
req_version=PRMS_VERSION)
pvt_meta = mobj._parameters_to_dict(xml_root=params_root,
meta_type='parameters',
req_version=PRMS_VERSION)

# Populate parameterSet with all available parameter names
for param in params_root.findall('parameter'):
Expand Down
116 changes: 31 additions & 85 deletions pyPRMS/parameters/Parameter.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
from __future__ import annotations

import functools
import numpy as np
import numpy.typing as npt
import pandas as pd # type: ignore
from typing import Any, cast, Dict, List, NamedTuple, Optional, Union
from collections.abc import ValuesView
from typing import Any, cast, NamedTuple
import xml.etree.ElementTree as xmlET

from ..base.console import get_console_instance
from ..constants import NEW_PTYPE_TO_DTYPE
from ..dimensions.Dimensions import ParamDimensions
from ..Exceptions_custom import FixedDimensionError

# from rich.console import Console
# from rich import pretty
#
# pretty.install()
# con = Console(force_jupyter=False)
con = None

ParamDataRawType = Union[npt.NDArray, np.int32, np.float32, np.float64, np.str_]
ParamDataType = Union[npt.NDArray, np.int32, np.float32, np.float64, np.str_, int, float, str]
ParamDataRawType = npt.NDArray | np.int32 | np.float32 | np.float64 | np.str_
ParamDataType = npt.NDArray | np.int32 | np.float32 | np.float64 | np.str_ | int | float | str


class Outliers(NamedTuple):
Expand All @@ -29,10 +27,10 @@ class Outliers(NamedTuple):

class Stats(NamedTuple):
name: str
min: Optional[npt.DTypeLike]
max: Optional[npt.DTypeLike]
mean: Optional[npt.DTypeLike]
median: Optional[npt.DTypeLike]
min: npt.DTypeLike | None
max: npt.DTypeLike | None
mean: npt.DTypeLike | None
median: npt.DTypeLike | None


class Parameter(object):
Expand All @@ -44,10 +42,10 @@ class Parameter(object):

# Container for a single parameter
def __init__(self, name: str,
meta: Optional[Dict] = None,
meta: dict | None = None,
global_dims=None,
strict: Optional[bool] = True,
verbose: Optional[bool] = False):
strict: bool = True,
verbose: bool = False):
"""
Initialize a parameter object.

Expand Down Expand Up @@ -86,9 +84,16 @@ def __init__(self, name: str,
# The meta must be supplied as an adhoc dictionary
self.meta = meta

self.__data: Optional[ParamDataRawType] = None
self.__data: ParamDataRawType | None = None
self.__modified = False

def __repr__(self) -> str:
"""String representation of the Parameter object.

:return: string with parameter name and dimensions
"""
return f"Parameter(name='{self.name}')"

def __str__(self) -> str:
"""Pretty-print string representation of the parameter information.

Expand Down Expand Up @@ -245,7 +250,7 @@ def dimensions(self) -> ParamDimensions:
return self.__dimensions

@property
def index_map(self) -> Union[Dict[Any, int], None]:
def index_map(self) -> dict[Any, int] | None:
"""Returns an ordered dictionary which maps data values of a 1D array
to index positions.

Expand All @@ -260,7 +265,7 @@ def index_map(self) -> Union[Dict[Any, int], None]:
return None

@property
def is_scalar(self):
def is_scalar(self) -> bool:
try:
return 'one' in self.meta['dimensions']
except KeyError:
Expand All @@ -275,7 +280,7 @@ def modified(self) -> bool:
return self.__modified

@property
def modules(self) -> List[str]:
def modules(self) -> list[str]:
"""Returns the names of the PRMS modules that require the parameter.

:returns: names of PRMS modules that require the parameter
Expand All @@ -301,16 +306,6 @@ def ndim(self) -> int:
else:
return self.__dimensions.ndim

# @property
# def size(self) -> int:
# """Return the total size of the parameter for the defined dimensions.
#
# :returns total size of parameter dimensions"""
# arr_shp = [dd.size for dd in self.dimensions.dimensions.values()]
#
# # Compute the total size of the parameter
# return functools.reduce(lambda x, y: x * y, arr_shp)

@property
def xml(self) -> xmlET.Element:
"""Return the xml metadata for the parameter as an xml Element.
Expand Down Expand Up @@ -355,7 +350,6 @@ def check_values(self) -> bool:

:returns: true when all values are within the valid min/max range for the parameter
"""
# if self.__data is not None:
minval = self.meta.get('minimum', None)
maxval = self.meta.get('maximum', None)

Expand Down Expand Up @@ -409,7 +403,6 @@ def outliers(self) -> Outliers:

:returns: NamedTuple containing count of values less than and values greater than valid range
"""
# Outliers = namedtuple('Outliers', ['name', 'under', 'over'])

values_under = 0
values_over = 0
Expand All @@ -422,13 +415,13 @@ def outliers(self) -> Outliers:

return Outliers(self.__name, values_under, values_over)

def remove_by_index(self, dim_name: str, indices: List[int]):
def remove_by_index(self, dim_name: str, indices: list[int]):
"""Remove columns (nhru or nsegment) from data array given a list of indices.

:param dim_name: Name of dimension to reduce
:param indices: List of indices to remove"""

if isinstance(indices, type(dict().values())):
if isinstance(indices, ValuesView):
indices = list(indices)

if self.__data is not None:
Expand All @@ -441,58 +434,11 @@ def remove_by_index(self, dim_name: str, indices: List[int]):
else:
raise TypeError('Parameter data is not initialized')

# def reshape(self, new_dims: Dict):
# """Reshape a parameter, broadcasting existing values as necessary.
#
# :param new_dims: Dimension names and sizes that will be used to reshape the parameter data
# """
#
# if self.__data is None:
# # Reshape has no meaning if there is no data to reshape
# return
#
# if self.dimensions.ndim == 1:
# if 'one' in self.dimensions.keys():
# # Reshaping from a scalar to a 1D or 2D array
# # print('Scalar to 1D or 2D')
# new_sizes = [vv.size for vv in new_dims.values()]
# tmp_data = np.broadcast_to(self.__data, new_sizes)
#
# # Remove the original dimension
# self.dimensions.remove('one')
#
# # Add the new ones
# for kk, vv in new_dims.items():
# self.dimensions.add(kk, vv.size)
#
# self.__data = tmp_data
# elif set(self.dimensions.keys()).issubset(set(new_dims.keys())):
# # Reschaping a 1D to a 2D
# if len(new_dims) == 1:
# print('ERROR: Cannot reshape from 1D array to 1D array')
# else:
# # print('1D array to 2D array')
# new_sizes = [vv.size for vv in new_dims.values()]
# try:
# tmp_data = np.broadcast_to(self.__data, new_sizes)
# except ValueError:
# # operands could not be broadcast together with remapped shapes
# tmp_data = np.broadcast_to(self.__data, new_sizes[::-1]).T
#
# old_dim = list(self.dimensions.keys())[0]
# self.dimensions.remove(old_dim)
#
# for kk, vv in new_dims.items():
# self.dimensions.add(kk, vv.size)
#
# self.__data = tmp_data

def stats(self) -> Stats:
"""Returns basic statistics on parameter values.

:returns: None (for strings or no data) or NamedTuple containing min, max, mean, and median of parameter values
"""
# Stats = namedtuple('Stats', ['name', 'min', 'max', 'mean', 'median'])

try:
return Stats(self.__name, np.min(self.data_raw), np.max(self.data_raw),
Expand All @@ -507,7 +453,7 @@ def subset_by_index(self, dim_name: str, indices):
:param dim_name: name of dimension
:param indices: local indices of HRUs or segments to extract"""

if isinstance(indices, type(dict().values())):
if isinstance(indices, ValuesView):
indices = list(indices)

if self.dimensions[dim_name].is_fixed:
Expand All @@ -522,7 +468,7 @@ def subset_by_index(self, dim_name: str, indices):
assert self.data_raw is not None # Needed so mypy doesn't fail on next line
self.dimensions[dim_name].size = self.data_raw.shape[dim_idx]

def tolist(self) -> List[Union[int, float, str]]:
def tolist(self) -> list[int | float | str]:
"""Returns the parameter data as a list.

:returns: Parameter data
Expand Down Expand Up @@ -572,14 +518,14 @@ def tostructure(self) -> dict:
'data': self.tolist()}
return param

def unique(self) -> Optional[npt.NDArray]:
def unique(self) -> npt.NDArray | None:
"""Create array of unique values from the parameter data.

:returns: Array of unique values
"""
return np.unique(self.data_raw)

def update_element(self, index: int, value: Union[int, float, List[int], List[float]]):
def update_element(self, index: int, value: int | float | list[int] | list[float]):
"""Update single value or row of values (e.g. nhru by nmonths) for a
given local zero-based index in the parameter data array.

Expand Down Expand Up @@ -634,7 +580,7 @@ def update_element(self, index: int, value: Union[int, float, List[int], List[fl
self.__data[index] = value # type: ignore
self.__modified = True

def _value_index_1d(self, value: Union[int, float, str]) -> npt.NDArray:
def _value_index_1d(self, value: int | float | str) -> npt.NDArray:
"""Given a scalar value return the indices where there is a match.

:param value: The value to find in the parameter data array
Expand Down
Loading
Loading