Model skill assessment#

Simple comparison#

Sometimes all your need is a simple comparison of two time series. The modelskill.compare() method does just that.

import mikeio
import modelskill as ms

The model#

Can be either a dfs0 or a DataFrame.

fn_mod = 'data/SW/ts_storm_4.dfs0'
df_mod = mikeio.read(fn_mod, items=0).to_dataframe()

The observation#

Can be either a dfs0, a DataFrame or a PointObservation object.

fn_obs = 'data/SW/eur_Hm0.dfs0'

Match observation to model#

The match() method will return an object that can be used for scatter plots, skill assessment, time series plots etc.

cmp = ms.match(fn_obs, df_mod)
cmp.plot.timeseries();
_images/252015b5ce0d36f4d29f0c508a99c0535eda833ee418eae6c04e7fdaf4c60235.png

Systematic vs random errors#

A model is an simplified version of a natural system, such as the ocean, and as such does not reflect every detail of the natural system.

In order to validate if a model does capture the essential dynamics of the natural system, it can be helpful to classify the mismatch of the model and observations in two broad categories:

  • systematic errors

  • random errors

A quantitativate assesment of a model involves calculating one or more model score, skill metrics, which in varying degrees capture systematic errors, random errors or a combination.

Metrics#

Bias is an indication of systematic error. In the left figure above, the model has negative bias (modelled wave heights are lower thatn observed). Thus it is an indication that the model can be improved.

Root Mean Square Error (rmse) is a combination of systematic and random error. It is a common metric to indicate the quality of a calibrated model, but less useful to understand the potential for further calibration since it captures both systematic and random errors.

Unbiased Root Mean Square Error (urmse) is the unbiased version of Root Mean Square Error. Since the bias is removed, it only captures the random error.

For a complete list of possible metrics, see the Metrics section in the ModelSkill docs.

To get a quantitative model skill, we use the .skill() method, which returns a table (similar to a DataFrame).

cmp.skill()
n bias rmse urmse mae cc si r2
observation
eur_Hm0 66 0.05321 0.229957 0.223717 0.177321 0.967972 0.086125 0.929005

The default is a number of common metrics, but you are free to pick your favorite metrics.

cmp.skill(metrics=["mae","rho","lin_slope"])
n mae rho lin_slope
observation
eur_Hm0 66 0.177321 0.970199 0.999428

A very common way to visualize model skill is to use a scatter plot.

The scatter plot includes some additional features such as a 2d histogram, a Q-Q line and a regression line, but the appearance is highly configurable.

cmp.plot.scatter();
_images/99ac99d9ab0b37a2e3d4e37553f92841e76fa2ada45cd67a0efb013c94e434f6.png
cmp.plot.scatter(binsize=0.5, 
          show_points=False,
          xlim=[0,6], ylim=[0,6],
          title="A calibrated model!");
_images/9e48051f8236861cf4380f16e31112569c374f2f39807420bb6a2c6e9bac21a0.png

Taylor diagram#

A taylor diagram is a way to combine several statistics in a single plot, and is very useful to compare the skill of several models, or observations in a single plot.

cmp.plot.taylor();

Elaborate comparison#

fn = 'data/SW/HKZN_local_2017_DutchCoast.dfsu'
mr = ms.model_result(fn, name='HKZN_local', item=0)
mr
<DfsuModelResult>: HKZN_local
Time: 2017-10-27 00:00:00 - 2017-10-29 18:00:00
Quantity: Significant wave height [m]
o1 = ms.PointObservation('data/SW/HKNA_Hm0.dfs0', item=0, x=4.2420, y=52.6887, name="HKNA")
o2 = ms.PointObservation("data/SW/eur_Hm0.dfs0", item=0, x=3.2760, y=51.9990, name="EPL")
o1.plot.hist();
_images/3b2fdc671deb66719908d2c93121928c222a4af0a7151dfead3b84cb8b36df12.png
o1.plot(); 
_images/efabf550ff7c5f60724129c5e7b3eaa9011d022d290c9ceaeef7148114b65165.png

Overview#

ms.plotting.spatial_overview(obs=[o1,o2], mod=mr);
_images/5ae79cc79a103c37985fb0de08c560e430ab36d26d7a6511f2bda2ffba71f967.png
cc = ms.match(obs=[o1,o2], mod=mr)
cc
<ComparerCollection>
Comparers:
0: HKNA - Significant wave height [m]
1: EPL - Significant wave height [m]
cc.skill().style(precision=2)
/opt/hostedtoolcache/Python/3.9.18/x64/lib/python3.9/site-packages/modelskill/skill.py:821: FutureWarning: precision is deprecated, it has been renamed to decimals
  warnings.warn(
  n bias rmse urmse mae cc si r2
observation                
HKNA 386 -0.32 0.45 0.32 0.34 0.97 0.10 0.85
EPL 66 -0.08 0.23 0.22 0.19 0.97 0.08 0.93
cc["EPL"].skill(metrics="mean_absolute_error")
n mean_absolute_error
observation
EPL 66 0.194178
cc["HKNA"].plot.timeseries(figsize=(10,5));
_images/0085eb9e90951c250d45529f7f027f02f63fdad15ef7fbb1f73a4dc7ab776da1.png
cc["EPL"].plot.scatter(figsize=(8,8), show_hist=True);
_images/8411bad6bb2752aaf4f399be411394ff6e975a44b153f789703d7a20d706c50f.png
cc["EPL"].plot.hist(bins=20);
_images/b260a0f2883cb65bf27386f1b7ee4de4a7ef9df076164fe4e9c69d6f2299fa71.png
cc["HKNA"].plot.scatter();
_images/e3834578457ba4a6e011370161a40bb3abb2aadcc61a06200a7806eba4028884.png