Using a split dataset, estimate parameters using optimisation on data subsets and evaluate their performance on all other subsets.
crossValidate(
MODEL,
periods,
name.Model.str = paste(MODEL$sma, MODEL$routing),
name.Cal.objfn = "unknown",
name.Catchment = as.character(MODEL$call$DATA),
fitBy,
...,
trace = isTRUE(hydromad.getOption("trace")),
parallel = hydromad.getOption("parallel")[["crossValidate"]]
)
an object of class hydromad
.
named list of start and end dates, passed to
splitData
Name to give to this model structure to allow a combined analysis
Name to give to this model identification process (e.g. name of objective function and/or optimisation algorithm), to allow a combined analysis
Name to give to this catchment to allow a combined analysis
function to estimate parameters of MODEL
, e.g.
fitByOptim
, fitBySCE
Arguments passed to fitBy
Whether to report messages.
name of method to use for parallelisation ("foreach" or "none"), or list giving settings for parallelisation. See hydromad_parallelisation.
A runlist of n*n models evaluated in each of n periods with
parameters estimated from each of n periods, of subclass
crossvalidation
crossValidate
optionally allows the
separate optimisations to be run concurrently with the parallel option
method="foreach"
. This is usually only worthwhile for longer running
optimisations such as fitBySCE
rather than relatively fast
methods such as fitByOptim
.
The total runtime is limited by the runtime of the slowest of optimisations, e.g. the longest data subset, most complex objective function response surface, or slowest computer on which an optimisation is being run. Some of the workers may therefore be idle (potentially wasting money) even though others are still running.
The evaluation of parameters on validation data subsets is also optionally
parallelised through the function update.runlist
, by setting
hydromad.options(parallel=list(update.runlist="clusterApply"))
. The
advantage of this is likely to be minor, unless a large number of cross
validation periods are used, due to the overhead involved and its relative
speed compared to the optimisation. Note that this requires parallelisation
to be setup on the worker, which is where the evaluation occurs.
If the parallelisation backend for foreach
supports it, the
cross-validations can be set to occur in the background using
parallel=list(method="foreach",async=TRUE)
. In this case, the
function returns immediately and the progress and results can be retrieved
using functions provided by the parallelisation backend. This can be useful
to submit a number of cross-validations for which the results are not
immediately needed. As with a single cross-validation, mixing long and short
running optimisations can make it difficult to fully utilise available
workers.
In future, it may also be possible to parallelise each optimisation itself in addition to or instead of parallelising the optimisation of each data period.
data(Cotter)
modx <- hydromad(Cotter,
sma = "cwi", routing = "expuh",
tau_s = c(2, 100), v_s = c(0, 1)
)
periods <- list(
P1 = as.Date(c("1966-05-01", "1976-04-30")),
P2 = as.Date(c("1976-05-1", "1986-04-30"))
)
## Estimate parameters using single fitByOptim run
## from single initial parameter set
runs <- crossValidate(modx, periods = periods, fitBy = fitByOptim, samples = 1)
summary(runs)
#> timesteps missing mean.P mean.Q runoff rel.bias r.squared
#> P1_calP1 3553 0 2.936248 1.0010380 0.3409242 1.647550e-17 0.7838178
#> P2_calP1 3552 0 2.791132 0.7544418 0.2702996 2.220760e-01 0.6733349
#> P1_calP2 3553 0 2.936248 1.0010380 0.3409242 -1.916880e-01 0.7043730
#> P2_calP2 3552 0 2.791132 0.7544418 0.2702996 -2.930517e-09 0.7614177
#> r.sq.sqrt r.sq.log sim.period calib.period Model.str Cal.objfn
#> P1_calP1 0.7887613 0.7629356 P1 P1 cwi expuh unknown
#> P2_calP1 0.7552158 0.7761833 P2 P1 cwi expuh unknown
#> P1_calP2 0.7532489 0.7349019 P1 P2 cwi expuh unknown
#> P2_calP2 0.8411148 0.8464667 P2 P2 cwi expuh unknown
#> Catchment
#> P1_calP1 Cotter
#> P2_calP1 Cotter
#> P1_calP2 Cotter
#> P2_calP2 Cotter
## Cross-validation statistics can then be analysed with other methods
paretoTimeAnalysis_areModelsDominated(summary(runs))
#> calib.period Model.str Cal.objfn Catchment objective P1 P2
#> 1 P1 cwi expuh unknown Cotter r.squared 0.7838178 0.6733349
#> 2 P2 cwi expuh unknown Cotter r.squared 0.7043730 0.7614177
#> dominated
#> 1 FALSE
#> 2 FALSE
cast(
melt(summary(runs), id.vars = c("calib.period", "sim.period")),
calib.period ~ variable + sim.period
)
#> calib.period timesteps_P1 timesteps_P2 missing_P1 missing_P2 mean.P_P1
#> 1 P1 3553 3552 0 0 2.93624824092316
#> 2 P2 3553 3552 0 0 2.93624824092316
#> mean.P_P2 mean.Q_P1 mean.Q_P2 runoff_P1
#> 1 2.79113175675676 1.00103801325108 0.75444183767805 0.340924176402861
#> 2 2.79113175675676 1.00103801325108 0.75444183767805 0.340924176402861
#> runoff_P2 rel.bias_P1 rel.bias_P2
#> 1 0.270299614431207 1.64755036639954e-17 0.22207598877803
#> 2 0.270299614431207 -0.191687990812397 -2.93051684043601e-09
#> r.squared_P1 r.squared_P2 r.sq.sqrt_P1 r.sq.sqrt_P2
#> 1 0.78381782651507 0.673334854810548 0.788761332725465 0.755215815313714
#> 2 0.704372971173514 0.761417709020757 0.753248892472808 0.841114791953054
#> r.sq.log_P1 r.sq.log_P2 Model.str_P1 Model.str_P2 Cal.objfn_P1
#> 1 0.76293564308086 0.776183292137543 cwi expuh cwi expuh unknown
#> 2 0.734901880638413 0.846466696367463 cwi expuh cwi expuh unknown
#> Cal.objfn_P2 Catchment_P1 Catchment_P2
#> 1 unknown Cotter Cotter
#> 2 unknown Cotter Cotter
paretoTimeAnalysis(runs)
#>
#> Cross-validation Pareto analysis
#> Which models cannot be rejected, due to dataset uncertainty/non-stationarity?
#>
#> == Eliminating Pareto-dominated models ==
#> (worse than another model in all periods)
#>
#> How many models are dominated (and therefore eliminated)
#> Cotter.FALSE
#> 2
#>
#> Which model structures are non-dominated in each catchment?
#> Proportion of model instances that are non-dominated
#> cwi expuh
#> Cotter 1
#>
#> Specify show.models=TRUE to show non-dominated and dominated models
#> Specify show.models=prefix to obtain csv of whether models are dominated
#>
#> == Performance across all periods ==
#> What is the range of non-dominated performance (RNDP) across all periods?
#> Is it large - is changing datasets causing problems?
#> Catchment value.min value.max RNDP
#> Cotter 0.6733349 0.7838178 0.110483
#>
#> == Performance in each period ==
#> What is the RNDP in each period?
#> Is it low even though total RNDP is high? Why?
#> Is there reason to believe the objective function is not comparable over time?
#> Catchment sim.period value.min value.max RNDP
#> Cotter P1 0.7043730 0.7838178 0.07944486
#> Cotter P2 0.6733349 0.7614177 0.08808285
#>
#> == Worst non-dominated models in each period ==
#> Do any non-dominated models have unacceptable performance?
#> Which non-dominated model has the worst performance in each period? Why?
#> Is it consistently the same dataset? Is there reason for that dataset to be problematic?
#> Is it consistently the same model structure? Should another model structure have been selected?
#> Is it consistently the same calibration objective function? Is it overfitting part of the record?
#>
#> Cotter
#> sim.period worst.performance calib.period Model.str Cal.objfn P1
#> P1 0.7043730 P2 cwi expuh unknown 0.7043730
#> P2 0.6733349 P1 cwi expuh unknown 0.7838178
#> P2
#> 0.7614177
#> 0.6733349
#>
#> == Variation in inferred internal behaviour - Range of non-dominated parameters ==
#> Is the range of parameters of non-dominated models large for any single model structure?
#> Does the difference in performance correspond to different internal model behaviour?
#>
#> Catchment Model.str variable value.mean value.min value.max range
#> Cotter cwi expuh tw 70.079841318 59.078470550 81.081212085 22.003
#> Cotter cwi expuh f 2.465770963 2.189511638 2.742030287 0.553
#> Cotter cwi expuh scale 0.001281642 0.001277323 0.001285962 0.000
#> Cotter cwi expuh l 0.000000000 0.000000000 0.000000000 0.000
#> Cotter cwi expuh p 1.000000000 1.000000000 1.000000000 0.000
#> Cotter cwi expuh t_ref 20.000000000 20.000000000 20.000000000 0.000
#> Cotter cwi expuh tau_s 14.818779726 11.550794573 18.086764880 6.536
#> Cotter cwi expuh v_s 0.885253120 0.880118594 0.890387646 0.010
#> range.as.%.of.mean
#> 31.40
#> 22.43
#> 0.00
#> NaN
#> 0.00
#> 0.00
#> 44.11
#> 1.13
#>
#> == Performance with other statistics ==
#> Use the set of non-dominated models as an ensemble to predict a quantity of interest
#> Is the model unacceptable in any period? Is the uncertainty too large?
#> variable sim.period min max range
#> q90.in.units.of.runoff P1 1.8186625 2.2699434 0.45128086
#> q90.in.units.of.runoff P2 1.8974348 2.3766883 0.47925350
#> r.sq.log P1 0.7349019 0.7629356 0.02803376
#> r.sq.log P2 0.7761833 0.8464667 0.07028340