Skip to content

Commit

Permalink
Merge pull request #23 from pastas/dev
Browse files Browse the repository at this point in the history
Release 0.3.2: minor update
  • Loading branch information
dbrakenhoff authored Dec 15, 2020
2 parents 5a2fd29 + 3c97fac commit f3317b0
Show file tree
Hide file tree
Showing 16 changed files with 1,219 additions and 143 deletions.
19 changes: 11 additions & 8 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,31 @@ The PastaStore object
---------------------

The `PastaStore` object provides useful methods e.g. for creating models and
determining which timeseries are closest to one another. The database
read/write/delete methods are always accessed through `pr.conn` i.e.::
determining which timeseries are closest to one another. The database
read/write/delete methods can be accessed directly from the `PastaStore`
object::

# add an observation timeseries
# create a timeseries
series = pd.Series(index=pd.date_range("2019", "2020", freq="D"), data=1.0)
store.conn.add_oseries(series, "my_oseries", metadata={"x": 100, "y": 200})
# add an observation timeseries
store.add_oseries(series, "my_oseries", metadata={"x": 100, "y": 200})

# retrieve the oseries
oseries = store.conn.get_oseries("my_oseries")
oseries = store.get_oseries("my_oseries")

To create a timeseries model use `store.create_model()`. Note that this does
To create a Pastas timeseries model use `store.create_model()`. Note that this does
not automatically add the model to the database. To store the model, it has to
be explicitly added to the database::

# create a timeseries model
ml = store.create_model("my_oseries", add_recharge=False)

# add to the database
store.conn.add_model(ml)
store.add_model(ml)

# retrieve model from database
ml = store.conn.get_models("my_oseries")
ml = store.get_models("my_oseries")


Example Notebooks
Expand Down
28 changes: 15 additions & 13 deletions docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ of Python.

Dependencies
------------
This module has several optional dependencies that have to be installed
depending on which connectors are used. Both the `PystoreConnector` and
`ArcticConnector` are dependent on external software. It is recommended to
install these dependencies.
This module has several optional dependencies. These are required for storing
timeseries and models in a database. Without these, only the in-memory option
is available.

It is up to the user to determine which dependencies they want to install.
Both the `PystoreConnector` and `ArcticConnector` are dependent on external
software. It is recommended but not required to install these dependencies.

* Using Pystore requires Snappy:
* `Snappy <http://google.github.io/snappy/>`_ is a fast and efficient
Expand Down Expand Up @@ -44,21 +47,20 @@ using `docker-compose`:
The `-d` flag runs the container in the background. This command uses the
`docker-compose.yml` file by default.
#. View your running containers with `docker ps -a`.

To stop the container run `docker-compose stop` in a terminal. Once the docker
container has been created, starting it using the `docker-compose up`
command might fail. In that case use the command `docker start mongodb` from
the terminal instead. Check if it is running by typing `docker ps -a`.
#. If you are done and wish to stop the container, run `docker-compose stop` in a terminal.

Installing `pastastore`
-----------------------
Install the module by typing `pip install .` from the root of the module
directory. Please note that the `pystore` module is not automatically installed
Install the module by typing::
pip install pastastore

Please note that the `pystore` module is not automatically installed
as a dependency because it requires Snappy to be (manually) installed first
(see `Dependencies`_ section above)!

For installing in development mode, clone the repository and install by
typing `pip install -e .` from the module root directory.
_For installing in development mode, clone the repository and install by
typing `pip install -e .` from the module root directory._

Using `pastastore`
------------------
Expand Down
5 changes: 0 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
.. pastastore documentation master file, created by
sphinx-quickstart on Mon Mar 16 16:56:56 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to pastastore's documentation!
======================================

Expand Down
13 changes: 7 additions & 6 deletions pastastore/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ def _parse_names(self, names: Optional[Union[list, str]] = None,
Parameters
----------
names : Union[list, str], optional
str or list of str or None (retrieves all names)
str or list of str or None or 'all' (last two options
retrieves all names)
libname : str, optional
name of library, default is 'oseries'
Expand All @@ -241,7 +242,11 @@ def _parse_names(self, names: Optional[Union[list, str]] = None,
list
list of names
"""
if names is None:
if not isinstance(names, str) and isinstance(names, Iterable):
return names
elif isinstance(names, str) and names != "all":
return [names]
elif names is None or names == "all":
if libname == "oseries":
return getattr(self, "oseries").index.to_list()
elif libname == "stresses":
Expand All @@ -250,10 +255,6 @@ def _parse_names(self, names: Optional[Union[list, str]] = None,
return getattr(self, "models")
else:
raise ValueError(f"No library '{libname}'!")
elif isinstance(names, str):
return [names]
elif isinstance(names, Iterable):
return names
else:
raise NotImplementedError(f"Cannot parse 'names': {names}")

Expand Down
9 changes: 8 additions & 1 deletion pastastore/connectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(self, name: str, connstr: str,
Parameters
----------
connstr : str
connection string
connection string (e.g. 'mongodb://localhost:27017/')
name : str
name of the project
library_map: dict, optional
Expand Down Expand Up @@ -96,6 +96,9 @@ def _initialize(self, library_map: Optional[dict]) -> None:
for libname in libmap.values():
if self._library_name(libname) not in self.arc.list_libraries():
self.arc.initialize_library(self._library_name(libname))
else:
print(f"Arctic library '{self._library_name(libname)}'"
" already exists! Linking to existing library!")
self.libs[libname] = self.get_library(libname)

def _library_name(self, libname: str) -> str:
Expand Down Expand Up @@ -554,7 +557,11 @@ def _initialize(self, library_map: Optional[dict]):
self.library_map = library_map

for libname in self.library_map.values():
if libname in self.store.list_collections():
print(f"Pystore library '{self.path}/{libname}'' already "
"exists! Linking to existing library!")
lib = self.store.collection(libname)

self.libs[libname] = lib

def get_library(self, libname: str):
Expand Down
53 changes: 40 additions & 13 deletions pastastore/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ def get_oseries_distances(self, names: Optional[Union[list, str]] = None) \
return distances

def get_nearest_oseries(self, names: Optional[Union[list, str]] = None,
n: int = 1) -> FrameorSeriesUnion:
n: int = 1, maxdist: Optional[float] = None) \
-> FrameorSeriesUnion:
"""Method to obtain the nearest (n) oseries.
Parameters
Expand All @@ -103,6 +104,8 @@ def get_nearest_oseries(self, names: Optional[Union[list, str]] = None,
string or list of strings with the name(s) of the oseries
n: int
number of oseries to obtain
maxdist : float, optional
maximum distance to consider
Returns
-------
Expand All @@ -111,11 +114,13 @@ def get_nearest_oseries(self, names: Optional[Union[list, str]] = None,
"""

distances = self.get_oseries_distances(names)
if maxdist is not None:
distances = distances.where(distances <= maxdist, np.nan)

data = pd.DataFrame(columns=np.arange(n))

for series in distances.index:
series = pd.Series(distances.loc[series].sort_values().index[:n],
series = pd.Series(distances.loc[series].dropna().sort_values().index[:n],
name=series)
data = data.append(series)
return data
Expand All @@ -132,8 +137,9 @@ def get_distances(self, oseries: Optional[Union[list, str]] = None,
name(s) of the oseries
stresses: str or list of str
name(s) of the stresses
kind: str
string representing which kind of stresses to consider
kind: str, list of str
string or list of strings representing which kind(s) of
stresses to consider
Returns
-------
Expand All @@ -149,10 +155,14 @@ def get_distances(self, oseries: Optional[Union[list, str]] = None,
if stresses is None and kind is None:
stresses = stresses_df.index
elif stresses is not None and kind is not None:
mask = stresses_df.kind == kind
if isinstance(kind, str):
kind = [kind]
mask = stresses_df.kind.isin(kind)
stresses = stresses_df.loc[stresses].loc[mask].index
elif stresses is None:
stresses = stresses_df.loc[stresses_df.kind == kind].index
if isinstance(kind, str):
kind = [kind]
stresses = stresses_df.loc[stresses_df.kind.isin(kind)].index

xo = pd.to_numeric(oseries_df.loc[oseries, "x"])
xt = pd.to_numeric(stresses_df.loc[stresses, "x"])
Expand All @@ -169,7 +179,9 @@ def get_distances(self, oseries: Optional[Union[list, str]] = None,

def get_nearest_stresses(self, oseries: Optional[Union[list, str]] = None,
stresses: Optional[Union[list, str]] = None,
kind: Optional[str] = None, n: int = 1) -> \
kind: Optional[Union[list, str]] = None,
n: int = 1,
maxdist: Optional[float] = None) -> \
FrameorSeriesUnion:
"""Method to obtain the nearest (n) stresses of a specific kind.
Expand All @@ -179,10 +191,13 @@ def get_nearest_stresses(self, oseries: Optional[Union[list, str]] = None,
string with the name of the oseries
stresses: str or list of str
string with the name of the stresses
kind:
string with the name of the stresses
kind: str, list of str, optional
string or list of str with the name of the kind(s)
of stresses to consider
n: int
number of stresses to obtain
maxdist : float, optional
maximum distance to consider
Returns
-------
Expand All @@ -191,11 +206,13 @@ def get_nearest_stresses(self, oseries: Optional[Union[list, str]] = None,
"""

distances = self.get_distances(oseries, stresses, kind)
if maxdist is not None:
distances = distances.where(distances <= maxdist, np.nan)

data = pd.DataFrame(columns=np.arange(n))

for series in distances.index:
series = pd.Series(distances.loc[series].sort_values().index[:n],
series = pd.Series(distances.loc[series].dropna().sort_values().index[:n],
name=series)
data = data.append(series)
return data
Expand Down Expand Up @@ -318,13 +335,16 @@ def get_statistics(self, statistics, modelnames=None, progressbar=False,
s = s.squeeze()
return s.astype(float)

def create_model(self, name: str, add_recharge: bool = True) -> ps.Model:
def create_model(self, name: str, modelname: str = None,
add_recharge: bool = True) -> ps.Model:
"""Create a pastas Model.
Parameters
----------
name : str
name of the oseries to create a model for
modelname : str, optional
name of the model, default is None, which uses oseries name
add_recharge : bool, optional
add recharge to the model by looking for the closest
precipitation and evaporation timeseries in the stresses
Expand All @@ -350,7 +370,9 @@ def create_model(self, name: str, add_recharge: bool = True) -> ps.Model:
if not ts.dropna().empty:
ts = ps.TimeSeries(ts, name=name, settings="oseries",
metadata=meta)
ml = ps.Model(ts, name=name, metadata=meta)
if modelname is None:
modelname = name
ml = ps.Model(ts, name=modelname, metadata=meta)

if add_recharge:
self.add_recharge(ml)
Expand Down Expand Up @@ -446,7 +468,12 @@ def add_recharge(self, ml: ps.Model, rfunc=ps.Gamma) -> None:
except AttributeError:
msg = "No precipitation or evaporation timeseries found!"
raise Exception(msg)
names.append(name)
if isinstance(name, float):
if np.isnan(name):
raise ValueError(f"Unable to find nearest '{var}' stress! "
"Check X and Y coordinates.")
else:
names.append(name)
if len(names) == 0:
msg = "No precipitation or evaporation timeseries found!"
raise Exception(msg)
Expand Down
Loading

0 comments on commit f3317b0

Please sign in to comment.