Skip to content

Commit

Permalink
Merge pull request #92 from DHI/misc_fix
Browse files Browse the repository at this point in the history
Misc fix
  • Loading branch information
jsmariegaard authored Oct 5, 2021
2 parents 7fc6574 + 4393efb commit 04517eb
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 72 deletions.
Binary file added docs/images/scatter_plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/ts_plot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
146 changes: 80 additions & 66 deletions docs/simple_compare.rst
Original file line number Diff line number Diff line change
@@ -1,66 +1,80 @@
.. _simple_compare:

Simple time series comparison
#############################

If all you need to do is to compare two point time series, the workflow is
very simple and described below. The general many-to-many comparison is decribed
in the `getting started guide <getting_started.html>`_.


Workflow
********

The simplified fmskill workflow consists of these four steps:

#. Specify **model result**
#. Specify **observation**
#. **compare()**
#. Analysis and plotting


1. Specify model result
=======================

The model result can be either a dfs0 or a DataFrame. It needs to have a single item only.

.. code-block:: python
from mikeio import Dfs0
fn_mod = '../tests/testdata/SW/ts_storm_4.dfs0'
df_mod = Dfs0(fn_mod).read(items=0).to_dataframe()
2. Specify Observation
======================
The observation can be either a dfs0, a DataFrame or a PointObservation object.
It needs to have a single item only.

.. code-block:: python
fn_obs = '../tests/testdata/SW/eur_Hm0.dfs0'
3. compare()
============
The `compare() <api.html#fmskill.connection.compare>`_ method will interpolate the modelresult to the time of the observation
and return an object that can be used for analysis and plotting

.. code-block:: python
import fmskill
c = fmskill.compare(fn_obs, df_mod)
4. Analysis and plotting
========================

The returned `PointComparer <api.html#fmskill.comparison.PointComparer>`_ can make
scatter plots, skill assessment, time series plots etc.


.. code-block:: python
c.plot_timeseries()
c.skill()
c.scatter()
.. _simple_compare:

Simple time series comparison
#############################

If all you need to do is to compare two point time series, the workflow is
very simple and described below. The general many-to-many comparison is decribed
in the `getting started guide <getting_started.html>`_.


Workflow
********

The simplified fmskill workflow consists of these four steps:

#. Specify **model result**
#. Specify **observation**
#. **compare()**
#. Analysis and plotting


1. Specify model result
=======================

The model result can be either a dfs0 or a DataFrame.

.. code-block:: python
from mikeio import Dfs0
fn_mod = '../tests/testdata/SW/ts_storm_4.dfs0'
2. Specify Observation
======================
The observation can be either a dfs0, a DataFrame or a PointObservation object.

.. code-block:: python
fn_obs = '../tests/testdata/SW/eur_Hm0.dfs0'
3. compare()
============
The `compare() <api.html#fmskill.connection.compare>`_ method will interpolate the modelresult to the time of the observation
and return an object that can be used for analysis and plotting

.. code-block:: python
import fmskill
c = fmskill.compare(fn_obs, fn_mod, mod_item=0)
4. Analysis and plotting
========================

The returned `PointComparer <api.html#fmskill.comparison.PointComparer>`_ can make
scatter plots, skill assessment, time series plots etc.


.. code-block:: python
>>> c.plot_timeseries()
.. image:: images/ts_plot.png


.. code-block:: python
>>> c.scatter()
.. image:: images/scatter_plot.png

.. code-block:: python
>>> c.skill()
n bias rmse urmse mae cc si r2
observation
eur_Hm0 66 0.05321 0.229957 0.223717 0.177321 0.967972 0.081732 0.929005
21 changes: 21 additions & 0 deletions fmskill/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,27 @@ def __init__(self, observation, modeldata):
def plot_timeseries(
self, title=None, ylim=None, figsize=None, backend="matplotlib", **kwargs
):
"""Timeseries plot showing compared data: observation vs modelled
Parameters
----------
title : str, optional
plot title, by default None
ylim : tuple, optional
plot range for the model (ymin, ymax), by default None
figsize : (float, float), optional
figure size, by default None
backend : str, optional
use "plotly" (interactive) or "matplotlib" backend, by default "matplotlib"backend:
Examples
------
>>> comparer.plot_timeseries()
>>> comparer.plot_timeseries(title="")
>>> comparer.plot_timeseries(ylim=[0,6])
>>> comparer.plot_timeseries(backend="plotly")
>>> comparer.plot_timeseries(backend="plotly", showlegend=False)
"""

if title is None:
title = self.name
Expand Down
6 changes: 3 additions & 3 deletions fmskill/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ def _parse_model(mod, item=None):

assert mod.shape[1] == 1 # A single item

mod.columns = ["Model"]

return mod


Expand Down Expand Up @@ -488,9 +490,7 @@ def _get_obs_id(self, obs):
if obs >= 0 and obs < n_con:
obs_id = obs
else:
raise IndexError(
f"connection id {obs} is out of range (0, {n_con-1})"
)
raise IndexError(f"connection id {obs} is out of range (0, {n_con-1})")
else:
raise KeyError("connection must be None, str or int")
return obs_id
Expand Down
7 changes: 4 additions & 3 deletions fmskill/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,9 @@ def hit_ratio(obs: np.ndarray, model: np.ndarray, a=0.1) -> float:
>>> hit_ratio(obs, model, a=0.15)
1.0
"""
assert obs.size == model.size

return np.mean(np.abs(obs - model) < a)
return np.mean(np.abs(obs.ravel() - model.ravel()) < a)


def lin_slope(obs: np.ndarray, model: np.ndarray, reg_method="ols") -> float:
Expand All @@ -449,14 +450,14 @@ def lin_slope(obs: np.ndarray, model: np.ndarray, reg_method="ols") -> float:
Range: :math:`(-\\infty, \\infty )`; Best: 1
"""
return _linear_regression(obs, model, reg_method)[0]
assert obs.size == model.size
return _linear_regression(obs.ravel(), model.ravel(), reg_method)[0]


def _linear_regression(
obs: np.ndarray, model: np.ndarray, reg_method="ols"
) -> Tuple[float, float]:

assert obs.size == model.size
if len(obs) == 0:
return np.nan

Expand Down
33 changes: 33 additions & 0 deletions fmskill/skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,39 @@ def plot_bar(self, field, level=0, **kwargs):
kwargs["title"] = field
return df.plot.bar(**kwargs)

def plot_barh(self, field, level=0, **kwargs):
"""plot statistic as horizontal bar chart using pd.DataFrame.plot.barh()
Parameters
----------
field : str
field (statistic) to plot e.g. "rmse"
level : int or str, optional
level to unstack, by default 0
kwargs : dict, optional
key word arguments to be passed to pd.DataFrame.plot.bar()
e.g. color, title, figsize, ...
Returns
-------
AxesSubplot
Examples
--------
>>> s = comparer.skill()
>>> s.plot_barh("rmse")
>>> s.plot_barh("mae", level="observation")
>>> s.plot_barh(field="si", title="scatter index")
"""
if isinstance(self.index, pd.MultiIndex):
df = self.df[field].unstack(level=level)
else:
df = self.df[field]
if "title" not in kwargs:
if isinstance(field, str):
kwargs["title"] = field
return df.plot.barh(**kwargs)

def plot_grid(
self,
field,
Expand Down

0 comments on commit 04517eb

Please sign in to comment.