smashbox.stats.stats
Created on Tue Jul 22 10:47:46 2025
@author: maxime
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3""" 4Created on Tue Jul 22 10:47:46 2025 5 6@author: maxime 7""" 8 9import numpy as np 10import pandas as pd 11import warnings 12import scipy.stats as stats 13import multiprocessing 14import matplotlib.pyplot as plt 15import os 16from tqdm import tqdm 17import threading 18from smashbox.tools import tools 19from smash.fcore import _mwd_metrics as smash_metrics 20 21 22@tools.autocast_args 23def _get_mask_colum_nodata(arr, nodata=-99.0, t_axis=0): 24 """ 25 Test if all data for axis 1 of an array with 2 dimension are nodata 26 The function return an array with shape equal of the shape of axis 1 of the 27 orignal array `arr`. This returned array is zeros everywhere but np.nan if all 28 values of arr are nodata for axis 1. 29 30 parameter: 31 ---------- 32 arr: np.array 33 nodata: float | int | np.nan 34 t_axis: array axis for time 35 """ 36 37 arr = np.moveaxis(arr, t_axis, 0) 38 c_axis = 1 39 40 res_nodata = None 41 if len(arr.shape) == 2: 42 res_nodata = np.zeros(shape=(arr.shape[c_axis])) 43 for i in range(arr.shape[c_axis]): 44 if np.all(arr[:, i] <= nodata): 45 res_nodata[i] = np.nan 46 47 # print(res_nodata) 48 49 return res_nodata 50 51 52def _test_input_shape(arr): 53 """ 54 Test the shape of the array `arr` 55 :param arr: np.ndarray 56 :type arr: np.ndarray 57 58 """ 59 60 if len(arr.shape) > 2: 61 raise ValueError( 62 "Dimension of the input array must not be greater than 2:" 63 f"given shape is {arr.shape} with {len(arr.shape)} dimension." 64 ) 65 66 67@tools.autocast_args 68def mse( 69 obs: np.ndarray | None = None, 70 sim: np.ndarray | None = None, 71 nodata: float = -99.0, 72 t_axis: int = 0, 73): 74 """ 75 Compute the misfit criteria mse: 76 mse = (1.0 / nb_valid_data) * np.sum((obs - sim) ** 2.0) 77 :param obs: Observed discharges, defaults to None 78 :type obs: np.ndarray | None, optional 79 :param sim: simulated discharges, defaults to None 80 :type sim: np.ndarray | None, optional 81 :param nodata: No data value, defaults to -99.0 82 :type nodata: float, optional 83 :param t_axis: Array axis of the time, defaults to 0 84 :type t_axis: int, optional 85 86 """ 87 88 t_axis = min(t_axis, len(obs.shape) - 1) 89 _test_input_shape(obs) 90 _test_input_shape(sim) 91 92 mask_nodata = obs != nodata 93 nb_valid_data = np.count_nonzero(mask_nodata, axis=t_axis) 94 res_nodata = _get_mask_colum_nodata(obs, nodata=nodata, t_axis=t_axis) 95 mse = np.nan 96 97 if sum(nb_valid_data) > 0: 98 99 if isinstance(obs, np.ndarray) and isinstance(sim, np.ndarray): 100 101 if obs.shape == sim.shape: 102 mse = (1.0 / nb_valid_data) * np.sum( 103 (obs - sim) ** 2.0, axis=t_axis, where=mask_nodata 104 ) 105 else: 106 raise ValueError(f"Error: len(obs)!=len(sim), {len(obs)}!={len(sim)}") 107 else: 108 raise ValueError("Error: obs and sim must be an instance of np.ndarray") 109 110 else: 111 raise ValueError("Error: no valid observation data.") 112 113 if res_nodata is not None: 114 mse = mse + res_nodata 115 116 return mse 117 118 119@tools.autocast_args 120def sm_mse( 121 obs: np.ndarray | None = None, 122 sim: np.ndarray | None = None, 123): 124 """ 125 Compute the misfit criteria mse: 126 mse = (1.0 / nb_valid_data) * np.sum((obs - sim) ** 2.0) 127 :param obs: Observed discharges, defaults to None 128 :type obs: np.ndarray | None, optional 129 :param sim: simulated discharges, defaults to None 130 :type sim: np.ndarray | None, optional 131 """ 132 133 mse = smash_metrics.mse(obs, sim) 134 135 return mse 136 137 138@tools.autocast_args 139def rmse( 140 obs: np.ndarray | None = None, 141 sim: np.ndarray | None = None, 142 nodata: float = -99.0, 143 t_axis: int = 0, 144): 145 """ 146 Compute the misfit criteria rmse: 147 rmse = np.sqrt(res_mse) 148 149 :param obs: Observed discharges, defaults to None 150 :type obs: np.ndarray | None, optional 151 :param sim: simulated discharges, defaults to None 152 :type sim: np.ndarray | None, optional 153 :param nodata: No data value, defaults to -99.0 154 :type nodata: float, optional 155 :param t_axis: Array axis of the time, defaults to 0 156 :type t_axis: int, optional 157 158 """ 159 t_axis = min(t_axis, len(obs.shape) - 1) 160 res_mse = mse(obs, sim, nodata=nodata, t_axis=t_axis) 161 162 rmse = np.sqrt(res_mse) 163 164 return rmse 165 166 167@tools.autocast_args 168def sm_rmse( 169 obs: np.ndarray | None = None, 170 sim: np.ndarray | None = None, 171): 172 """ 173 Compute the misfit criteria rmse: 174 rmse = np.sqrt(res_mse) 175 176 :param obs: Observed discharges, defaults to None 177 :type obs: np.ndarray | None, optional 178 :param sim: simulated discharges, defaults to None 179 :type sim: np.ndarray | None, optional 180 181 """ 182 183 rmse = smash_metrics.rmse(obs, sim) 184 185 return rmse 186 187 188@tools.autocast_args 189def nrmse( 190 obs: np.ndarray | None = None, 191 sim: np.ndarray | None = None, 192 nodata: float = -99.0, 193 t_axis: int = 0, 194): 195 """ 196 Compute the misfit criteria nrmse: 197 nrmse = res_rmse / mean_obs 198 199 :param obs: Observed discharges, defaults to None 200 :type obs: np.ndarray | None, optional 201 :param sim: simulated discharges, defaults to None 202 :type sim: np.ndarray | None, optional 203 :param nodata: No data value, defaults to -99.0 204 :type nodata: float, optional 205 :param t_axis: Array axis of the time, defaults to 0 206 :type t_axis: int, optional 207 208 """ 209 210 t_axis = min(t_axis, len(obs.shape) - 1) 211 res_rmse = rmse(obs, sim, nodata=nodata, t_axis=t_axis) 212 mask_nodata = obs != nodata 213 214 with warnings.catch_warnings(): 215 warnings.simplefilter("ignore", category=RuntimeWarning) 216 mean_obs = np.mean(obs, axis=t_axis, where=mask_nodata) 217 218 nrmse = res_rmse / mean_obs 219 220 return nrmse 221 222 223@tools.autocast_args 224def sm_nmse( 225 obs: np.ndarray | None = None, 226 sim: np.ndarray | None = None, 227): 228 """ 229 Compute the misfit criteria nrmse: 230 nrmse = res_rmse / mean_obs 231 232 :param obs: Observed discharges, defaults to None 233 :type obs: np.ndarray | None, optional 234 :param sim: simulated discharges, defaults to None 235 :type sim: np.ndarray | None, optional 236 237 """ 238 239 mask_nodata = obs < 0.0 240 mean_obs = np.nanmean(obs, where=mask_nodata) 241 mse = smash_metrics.mse(obs, sim) 242 243 return mse 244 245 246@tools.autocast_args 247def se( 248 obs: np.ndarray | None = None, 249 sim: np.ndarray | None = None, 250 nodata: float = -99.0, 251 t_axis: int = 0, 252): 253 """ 254 Compute the misfit criteria se: 255 se = ( 256 np.sum((obs - sim)** 2.0, axis=t_axis, where=mask_nodata) 257 ) 258 259 :param obs: Observed discharges, defaults to None 260 :type obs: np.ndarray | None, optional 261 :param sim: simulated discharges, defaults to None 262 :type sim: np.ndarray | None, optional 263 :param nodata: No data value, defaults to -99.0 264 :type nodata: float, optional 265 :param t_axis: Array axis of the time, defaults to 0 266 :type t_axis: int, optional 267 268 """ 269 270 t_axis = min(t_axis, len(obs.shape) - 1) 271 _test_input_shape(obs) 272 _test_input_shape(sim) 273 274 mask_nodata = obs != nodata 275 nb_valid_data = np.count_nonzero(mask_nodata, axis=t_axis) 276 res_nodata = _get_mask_colum_nodata(obs, nodata=nodata, t_axis=t_axis) 277 se = np.nan 278 279 if sum(nb_valid_data) > 0: 280 281 if isinstance(obs, np.ndarray) and isinstance(sim, np.ndarray): 282 283 if obs.shape == sim.shape: 284 se = np.sum((obs - sim) ** 2.0, axis=t_axis, where=mask_nodata) 285 else: 286 raise ValueError(f"Error: len(obs)!=len(sim), {len(obs)}!={len(sim)}") 287 else: 288 raise ValueError("Error: obs and sim must be an instance of np.ndarray") 289 290 else: 291 raise ValueError("Error: no valid observation data.") 292 293 if res_nodata is not None: 294 se = se + res_nodata 295 296 return se 297 298 299@tools.autocast_args 300def sm_se( 301 obs: np.ndarray | None = None, 302 sim: np.ndarray | None = None, 303): 304 """ 305 Compute the misfit criteria se: 306 se = ( 307 np.sum((obs - sim)** 2.0, axis=t_axis, where=mask_nodata) 308 ) 309 310 :param obs: Observed discharges, defaults to None 311 :type obs: np.ndarray | None, optional 312 :param sim: simulated discharges, defaults to None 313 :type sim: np.ndarray | None, optional 314 """ 315 316 se = smash_metrics.se(obs, sim) 317 318 return se 319 320 321@tools.autocast_args 322def mae( 323 obs: np.ndarray | None = None, 324 sim: np.ndarray | None = None, 325 nodata: float = -99.0, 326 t_axis: int = 0, 327): 328 """ 329 Compute the misfit criteria mae: 330 mae = 1/n*(np.sum(abs(obs - sim)) 331 332 :param obs: Observed discharges, defaults to None 333 :type obs: np.ndarray | None, optional 334 :param sim: simulated discharges, defaults to None 335 :type sim: np.ndarray | None, optional 336 :param nodata: No data value, defaults to -99.0 337 :type nodata: float, optional 338 :param t_axis: Array axis of the time, defaults to 0 339 :type t_axis: int, optional 340 341 """ 342 343 t_axis = min(t_axis, len(obs.shape) - 1) 344 _test_input_shape(obs) 345 _test_input_shape(sim) 346 347 mask_nodata = obs != nodata 348 nb_valid_data = np.count_nonzero(mask_nodata, axis=t_axis) 349 res_nodata = _get_mask_colum_nodata(obs, nodata=nodata, t_axis=t_axis) 350 mae = np.nan 351 352 if sum(nb_valid_data) > 0: 353 354 if isinstance(obs, np.ndarray) and isinstance(sim, np.ndarray): 355 356 if obs.shape == sim.shape: 357 mae = (1.0 / nb_valid_data) * ( 358 np.sum(abs(obs - sim), axis=t_axis, where=mask_nodata) 359 ) 360 else: 361 raise ValueError(f"Error: len(obs)!=len(sim), {len(obs)}!={len(sim)}") 362 else: 363 raise ValueError("Error: obs and sim must be an instance of np.ndarray") 364 365 else: 366 raise ValueError("Error: no valid observation data.") 367 368 if res_nodata is not None: 369 mae = mae + res_nodata 370 371 return mae 372 373 374@tools.autocast_args 375def sm_mae( 376 obs: np.ndarray | None = None, 377 sim: np.ndarray | None = None, 378): 379 """ 380 Compute the misfit criteria mae: 381 mae = np.sqrt(np.sum(abs(obs - sim)) 382 383 :param obs: Observed discharges, defaults to None 384 :type obs: np.ndarray | None, optional 385 :param sim: simulated discharges, defaults to None 386 :type sim: np.ndarray | None, optional 387 :param nodata: No data value, defaults to -99.0 388 :type nodata: float, optional 389 :param t_axis: Array axis of the time, defaults to 0 390 :type t_axis: int, optional 391 392 """ 393 394 mae = smash_metrics.mae(obs, sim) 395 396 return mae 397 398 399@tools.autocast_args 400def mape( 401 obs: np.ndarray | None = None, 402 sim: np.ndarray | None = None, 403 nodata: float = -99.0, 404 t_axis: int = 0, 405): 406 """ 407 Compute the misfit criteria mape: 408 mape = 1/n*( 409 np.sum(abs((obs - sim) / obs)) 410 ) 411 412 :param obs: Observed discharges, defaults to None 413 :type obs: np.ndarray | None, optional 414 :param sim: simulated discharges, defaults to None 415 :type sim: np.ndarray | None, optional 416 :param nodata: No data value, defaults to -99.0 417 :type nodata: float, optional 418 :param t_axis: Array axis of the time, defaults to 0 419 :type t_axis: int, optional 420 421 """ 422 423 t_axis = min(t_axis, len(obs.shape) - 1) 424 _test_input_shape(obs) 425 _test_input_shape(sim) 426 427 mask_nodata = obs != nodata 428 nb_valid_data = np.count_nonzero(mask_nodata, axis=t_axis) 429 res_nodata = _get_mask_colum_nodata(obs, nodata=nodata, t_axis=t_axis) 430 mape = np.nan 431 432 if sum(nb_valid_data) > 0: 433 434 if isinstance(obs, np.ndarray) and isinstance(sim, np.ndarray): 435 436 if obs.shape == sim.shape: 437 mape = (1.0 / nb_valid_data) * ( 438 np.sum(abs((obs - sim) / obs), axis=t_axis, where=mask_nodata) 439 ) 440 else: 441 raise ValueError(f"Error: len(obs)!=len(sim), {len(obs)}!={len(sim)}") 442 else: 443 raise ValueError("Error: obs and sim must be an instance of np.ndarray") 444 445 else: 446 raise ValueError("Error: no valid observation data.") 447 448 if res_nodata is not None: 449 mape = mape + res_nodata 450 451 return mape 452 453 454@tools.autocast_args 455def sm_mape( 456 obs: np.ndarray | None = None, 457 sim: np.ndarray | None = None, 458): 459 """ 460 Compute the misfit criteria mape: 461 mape = np.sqrt( 462 np.sum(abs((obs - sim) / obs)) 463 ) 464 465 :param obs: Observed discharges, defaults to None 466 :type obs: np.ndarray | None, optional 467 :param sim: simulated discharges, defaults to None 468 :type sim: np.ndarray | None, optional 469 470 """ 471 472 mape = smash_metrics.mape(obs, sim) 473 474 return mape 475 476 477@tools.autocast_args 478def lgrm( 479 obs: np.ndarray | None = None, 480 sim: np.ndarray | None = None, 481 nodata: float = -99.0, 482 t_axis: int = 0, 483): 484 """ 485 Compute the misfit criteria lgrm: 486 lgrm = np.sum( 487 obs * (np.log((obs / sim) ** 2.0)), axis=t_axis, where=mask_nodata 488 ) 489 490 :param obs: Observed discharges, defaults to None 491 :type obs: np.ndarray | None, optional 492 :param sim: simulated discharges, defaults to None 493 :type sim: np.ndarray | None, optional 494 :param nodata: No data value, defaults to -99.0 495 :type nodata: float, optional 496 :param t_axis: Array axis of the time, defaults to 0 497 :type t_axis: int, optional 498 499 """ 500 501 t_axis = min(t_axis, len(obs.shape) - 1) 502 _test_input_shape(obs) 503 _test_input_shape(sim) 504 505 mask_nodata = obs != nodata 506 nb_valid_data = np.count_nonzero(mask_nodata, axis=t_axis) 507 res_nodata = _get_mask_colum_nodata(obs, nodata=nodata, t_axis=t_axis) 508 lgrm = np.nan 509 510 if sum(nb_valid_data) > 0: 511 512 if isinstance(obs, np.ndarray) and isinstance(sim, np.ndarray): 513 514 if obs.shape == sim.shape: 515 lgrm = np.sum( 516 obs * (np.log(sim / obs) ** 2.0), axis=t_axis, where=mask_nodata 517 ) 518 519 else: 520 raise ValueError(f"Error: len(obs)!=len(sim), {len(obs)}!={len(sim)}") 521 else: 522 raise ValueError("Error: obs and sim must be an instance of np.ndarray") 523 524 else: 525 raise ValueError("Error: no valid observation data.") 526 527 if res_nodata is not None: 528 lgrm = lgrm + res_nodata 529 530 return lgrm 531 532 533@tools.autocast_args 534def sm_lgrm( 535 obs: np.ndarray | None = None, 536 sim: np.ndarray | None = None, 537): 538 """ 539 Compute the misfit criteria lgrm: 540 lgrm = np.sum( 541 obs * (np.log((obs / sim) ** 2.0)), axis=t_axis, where=mask_nodata 542 ) 543 544 :param obs: Observed discharges, defaults to None 545 :type obs: np.ndarray | None, optional 546 :param sim: simulated discharges, defaults to None 547 :type sim: np.ndarray | None, optional 548 """ 549 550 lgrm = smash_metrics.lgrm(obs, sim) 551 552 return lgrm 553 554 555@tools.autocast_args 556def nse( 557 obs: np.ndarray | None = None, 558 sim: np.ndarray | None = None, 559 nodata: float = -99.0, 560 t_axis: int = 0, 561): 562 """ 563 Compute the misfit criteria nse: 564 numerator = np.sum((obs - sim) ** 2.0, axis=t_axis, where=mask_nodata) 565 566 denominator = np.sum( 567 (obs - mean_obs) ** 2.0, axis=t_axis, where=mask_nodata 568 ) 569 570 denominator = np.where(denominator == 0, np.nan, denominator) 571 572 nse = 1 - numerator / denominator 573 574 :param obs: Observed discharges, defaults to None 575 :type obs: np.ndarray | None, optional 576 :param sim: simulated discharges, defaults to None 577 :type sim: np.ndarray | None, optional 578 :param nodata: No data value, defaults to -99.0 579 :type nodata: float, optional 580 :param t_axis: Array axis of the time, defaults to 0 581 :type t_axis: int, optional 582 583 """ 584 585 t_axis = min(t_axis, len(obs.shape) - 1) 586 _test_input_shape(obs) 587 _test_input_shape(sim) 588 589 mask_nodata = obs != nodata 590 nb_valid_data = np.count_nonzero(mask_nodata, axis=t_axis) 591 592 nse = np.nan 593 594 if sum(nb_valid_data) > 0: 595 596 if isinstance(obs, np.ndarray) and isinstance(sim, np.ndarray): 597 if obs.shape == sim.shape: 598 599 with warnings.catch_warnings(): 600 warnings.simplefilter("ignore", category=RuntimeWarning) 601 mean_obs = np.mean(obs, axis=t_axis, where=mask_nodata, keepdims=True) 602 603 numerator = np.sum((obs - sim) ** 2.0, axis=t_axis, where=mask_nodata) 604 605 denominator = np.sum( 606 (obs - mean_obs) ** 2.0, axis=t_axis, where=mask_nodata 607 ) 608 609 denominator = np.where(denominator == 0, np.nan, denominator) 610 nse = 1 - numerator / denominator 611 else: 612 raise ValueError(f"Error: len(obs)!=len(sim), {len(obs)}!={len(sim)}") 613 else: 614 raise ValueError("Error: obs and sim must be an instance of np.ndarray") 615 616 else: 617 raise ValueError("Error: no valid observation data.") 618 619 return nse 620 621 622@tools.autocast_args 623def sm_nse( 624 obs: np.ndarray | None = None, 625 sim: np.ndarray | None = None, 626): 627 """ 628 Compute the misfit criteria nse: 629 numerator = np.sum((obs - sim) ** 2.0, axis=t_axis, where=mask_nodata) 630 631 denominator = np.sum( 632 (obs - mean_obs) ** 2.0, axis=t_axis, where=mask_nodata 633 ) 634 635 denominator = np.where(denominator == 0, np.nan, denominator) 636 637 nse = 1 - numerator / denominator 638 639 :param obs: Observed discharges, defaults to None 640 :type obs: np.ndarray | None, optional 641 :param sim: simulated discharges, defaults to None 642 :type sim: np.ndarray | None, optional 643 644 """ 645 646 nse = smash_metrics.nse(obs, sim) 647 648 return nse 649 650 651@tools.autocast_args 652def nnse( 653 obs: np.ndarray | None = None, 654 sim: np.ndarray | None = None, 655 nodata: float = -99.0, 656 t_axis: int = 0, 657): 658 """ 659 Compute the misfit criteria nnse: 660 nnse = 1.0 / (2.0 - res_nse) 661 662 :param obs: Observed discharges, defaults to None 663 :type obs: np.ndarray | None, optional 664 :param sim: simulated discharges, defaults to None 665 :type sim: np.ndarray | None, optional 666 :param nodata: No data value, defaults to -99.0 667 :type nodata: float, optional 668 :param t_axis: Array axis of the time, defaults to 0 669 :type t_axis: int, optional 670 671 """ 672 673 t_axis = min(t_axis, len(obs.shape) - 1) 674 res_nse = nse(obs, sim, nodata=nodata, t_axis=t_axis) 675 nnse = 1.0 / (2.0 - res_nse) 676 677 return nnse 678 679 680@tools.autocast_args 681def sm_nnse( 682 obs: np.ndarray | None = None, 683 sim: np.ndarray | None = None, 684): 685 """ 686 Compute the misfit criteria nnse: 687 nnse = 1.0 / (2.0 - res_nse) 688 689 :param obs: Observed discharges, defaults to None 690 :type obs: np.ndarray | None, optional 691 :param sim: simulated discharges, defaults to None 692 :type sim: np.ndarray | None, optional 693 694 """ 695 696 nnse = smash_metrics.nnse(obs, sim) 697 698 return nse 699 700 701@tools.autocast_args 702def kge( 703 obs: np.ndarray | None = None, 704 sim: np.ndarray | None = None, 705 nodata: float = -99.0, 706 t_axis: int = 0, 707): 708 """ 709 Compute the misfit criteria kge: 710 std_obs = np.std( 711 obs, axis=t_axis, where=mask_nodata, keepdims=True, mean=mean_obs 712 ) 713 std_sim = np.std( 714 sim, axis=t_axis, where=mask_nodata, keepdims=True, mean=mean_sim 715 ) 716 717 beta = mean_sim / mean_obs 718 719 alpha = std_sim / std_obs 720 721 r2 = ( 722 1 723 / (sim.shape[t_axis]) 724 * np.sum( 725 ((sim - mean_sim) / std_sim) * ((obs - mean_obs) / std_obs), 726 axis=t_axis, 727 keepdims=True, 728 ) 729 ) 730 731 kge = 1 - np.sqrt( 732 (r2 - 1.0) ** 2.0 + (alpha - 1.0) ** 2.0 + (beta - 1) ** 2.0 733 ) 734 735 :param obs: Observed discharges, defaults to None 736 :type obs: np.ndarray | None, optional 737 :param sim: simulated discharges, defaults to None 738 :type sim: np.ndarray | None, optional 739 :param nodata: No data value, defaults to -99.0 740 :type nodata: float, optional 741 :param t_axis: Array axis of the time, defaults to 0 742 :type t_axis: int, optional 743 744 """ 745 t_axis = min(t_axis, len(obs.shape) - 1) 746 _test_input_shape(obs) 747 _test_input_shape(sim) 748 749 mask_nodata = obs != nodata 750 nb_valid_data = np.count_nonzero(mask_nodata, axis=t_axis) 751 752 kge = np.nan 753 754 if sum(nb_valid_data) > 0: 755 756 if isinstance(obs, np.ndarray) and isinstance(sim, np.ndarray): 757 if obs.shape == sim.shape: 758 759 with warnings.catch_warnings(): 760 warnings.simplefilter("ignore", category=RuntimeWarning) 761 mean_obs = np.mean(obs, axis=t_axis, where=mask_nodata, keepdims=True) 762 mean_sim = np.mean(sim, axis=t_axis, where=mask_nodata, keepdims=True) 763 std_obs = np.std( 764 obs, axis=t_axis, where=mask_nodata, keepdims=True, mean=mean_obs 765 ) 766 std_sim = np.std( 767 sim, axis=t_axis, where=mask_nodata, keepdims=True, mean=mean_sim 768 ) 769 770 mean_obs = np.where(mean_obs == 0, np.nan, mean_obs) 771 beta = mean_sim / mean_obs 772 773 std_obs = np.where(std_obs == 0, np.nan, std_obs) 774 alpha = std_sim / std_obs 775 776 # r2 = np.sum( 777 # (sim - mean_sim) * (obs - mean_obs), axis=t_axis, keepdims=True 778 # ) / ((sim.shape[t_axis]) * (std_obs * std_sim)) 779 780 r2 = ( 781 1 782 / (sim.shape[t_axis]) 783 * np.sum( 784 ((sim - mean_sim) / std_sim) * ((obs - mean_obs) / std_obs), 785 axis=t_axis, 786 keepdims=True, 787 ) 788 ) 789 790 kge = 1 - np.sqrt( 791 (r2 - 1.0) ** 2.0 + (alpha - 1.0) ** 2.0 + (beta - 1) ** 2.0 792 ) 793 else: 794 raise ValueError(f"Error: len(obs)!=len(sim), {len(obs)}!={len(sim)}") 795 else: 796 raise ValueError("Error: obs and sim must be an instance of np.ndarray") 797 798 else: 799 raise ValueError("Error: no valid observation data.") 800 801 return np.squeeze(kge) 802 803 804@tools.autocast_args 805def sm_kge( 806 obs: np.ndarray | None = None, 807 sim: np.ndarray | None = None, 808): 809 """ 810 Compute the misfit criteria kge, see the Smash documentation at https://smash.recover.inrae.fr/math_num_documentation/efficiency_error_metric.html 811 812 :param obs: Observed discharges, defaults to None 813 :type obs: np.ndarray | None, optional 814 :param sim: simulated discharges, defaults to None 815 :type sim: np.ndarray | None, optional 816 """ 817 818 kge = smash_metrics.kge(obs, sim) 819 820 return kge 821 822 823# Fonction de calcul du débit de retour 824def quantile_gumbel(T: float = 1.0, loc: float = 0.0, scale: float = 0.0): 825 """ 826 Compute the quantile for a given return period using the Gumbel law. 827 :param T: The return period, defaults to 1 828 :type T: float, optional 829 :param loc: The localisation parameter of the Gumbel law, defaults to 0.0 830 :type loc: float, optional 831 :param scale: The scale parameter of the Gumbel law, defaults to 0.0 832 :type scale: float, optional 833 :return: The value of the quantile 834 :rtype: float 835 836 """ 837 838 return stats.gumbel_r.ppf(1 - 1 / T, loc=loc, scale=scale) 839 840 841# Fonction de calcul du débit de retour 842def quantile_gev( 843 T: float = 1.0, shape: float = 0.0, loc: float = 0.0, scale: float = 0.0 844): 845 """ 846 Compute the quantile for a given return period using the GEV law. 847 :param T: The return period, defaults to 1 848 :type T: float, optional 849 :param shape: The shape parameter of the GEV law, defaults to 0.0 850 :type shape: float, optional 851 :param loc: The localisation parameter of the GEV law, defaults to 0.0 852 :type loc: float, optional 853 :param scale: The scale parameter of the GEV law, defaults to 0.0 854 :type scale: float, optional 855 :return: The value of the quantile 856 :rtype: float 857 858 """ 859 return stats.genextreme.ppf(1 - 1 / T, shape, loc=loc, scale=scale) 860 861 862def genextreme_fit(data: np.ndarray | None = None, estimate_method: str = "MLE"): 863 """ 864 Return estimates of shape, location, and scale parameters from data. The default 865 estimation method is Maximum Likelihood Estimation (MLE), but Method of Moments (MM) 866 is also available. 867 :param data: Data of maximum values used to fit a GEV, defaults to None 868 :type data: np.ndarray | None, optional 869 :param estimate_method: Method to optimize the parameters: MLE for Maximum Likelihood 870 Estimate, MM for Method of Moments , defaults to "MLE" 871 :type estimate_method: str, optional 872 :return: Estimates for any shape parameters (if applicable), followed by those for 873 location and scale. 874 :rtype: tuple of float 875 876 """ 877 if data is None: 878 raise ValueError("input data is None. You must provide valid data.") 879 res = stats.genextreme.fit(data, method=estimate_method) 880 return res 881 882 883def gumbel_r_fit(data: np.ndarray | None = None, estimate_method: str = "MLE"): 884 """ 885 Return estimates of shape, location, and scale parameters from data. 886 The default estimation method is Maximum Likelihood Estimation (MLE), 887 but Method of Moments (MM) is also available. 888 :param data: Data of maximum values used to fit a Gumbel law, defaults to None 889 :type data: np.ndarray | None, optional 890 :param estimate_method: Method to optimize the parameters: MLE for Maximum Likelihood 891 Estimate, MM for Method of Moments , defaults to "MLE" 892 :type estimate_method: str, optional 893 :return: Estimates for any shape parameters (if applicable), followed by those for 894 location and scale. 895 :rtype: tuple of float 896 897 """ 898 if data is None: 899 raise ValueError("input data is None. You must provide valid data.") 900 901 res = stats.gumbel_r.fit(data, method=estimate_method) 902 return (0, *res) 903 904 905@tools.autocast_args 906def time_resample_array( 907 array: np.ndarray | None, 908 quantile_duration: int | float = 1, 909 model_time_step: float = 3600, 910 quantile_chunk_size: int | None = None, 911 t_axis: int = 2, 912): 913 """ 914 Resample the discharges array for a given time-step. 915 :param array: the matrix containing the discharge with shape (nbx, nby, nbts) 916 :type array: np.ndarray | None 917 :param quantile_duration: The duration of the quantile (hours), defaults to 1 918 :type quantile_duration: int | float, optional 919 :param model_time_step: the time-step of the Smash model (seconds), defaults to 3600 920 :type model_time_step: float, optional 921 :param quantile_chunk_size: the size of the quantile chunk in days 922 :type quantile_chunk_size: int | None 923 :param t_axis: The array axis direction of the time-step, defaults to 2 924 :type t_axis: int, optional 925 :return: The resampled array 926 :rtype: np.ndarray 927 928 """ 929 # R workaround as normal number (int) are passed to float to python 930 # t_axis = int(t_axis) 931 932 # resample array to the new duration 933 if pd.Timedelta(hours=quantile_duration) > pd.Timedelta(seconds=model_time_step): 934 print( 935 f"</> Resampling array with time-step `{pd.Timedelta(seconds=model_time_step)}`" 936 f" to time-step `{pd.Timedelta(hours=quantile_duration)}`" 937 ) 938 chunk_size = int( 939 pd.Timedelta(hours=quantile_duration) / pd.Timedelta(seconds=model_time_step) 940 ) 941 942 array_trans = np.moveaxis(array, t_axis, 0) # Axe à la position 0 943 944 new_shape = (array_trans.shape[0] // chunk_size, chunk_size) + array_trans.shape[ 945 1: 946 ] 947 948 array_trans_reshaped = array_trans[ 949 0 : chunk_size * (array_trans.shape[0] // chunk_size) 950 ].reshape(new_shape) 951 952 array_trans_reshaped_mean = np.mean(array_trans_reshaped, axis=1) 953 954 if quantile_chunk_size is None: 955 remainder = array_trans.shape[0] % chunk_size 956 else: 957 # The final shape must be extended only if it is not a multiple of quantile_chunk_size. 958 remainder = ( 959 array_trans_reshaped_mean.shape[0] 960 * pd.Timedelta(hours=quantile_duration) 961 % pd.Timedelta(days=quantile_chunk_size) 962 ).total_seconds() 963 964 del array_trans 965 966 if remainder > 0: 967 # extend the array with the previous value 968 array_trans_reshaped_mean = np.insert( 969 array_trans_reshaped_mean, 970 obj=-1, 971 axis=0, 972 values=array_trans_reshaped_mean[-1, :], 973 ) 974 975 del array_trans_reshaped 976 977 array = np.moveaxis(array_trans_reshaped_mean, 0, t_axis) 978 979 del array_trans_reshaped_mean 980 981 return array 982 983 984@tools.autocast_args 985def compute_maxima( 986 array: np.ndarray | None, 987 t_axis: int = 2, 988 nb_minimum_chunks: int = 4, 989 chunk_size: int = 365, 990 quantile_duration: int | float = 1, 991): 992 """ 993 Compute the maxima of the discharges for a given chunk_size in the t_axis direction. 994 :param array: the matrix containing the discharge with shape (nbx, nby, nbts). The 995 shape can be smaller or higher. But t_axis must be set to target the nbts 996 (number of time-step) dimension. Ex: if shape=(nbx, nbts), t_axis must be equal to 1. 997 :type array: np.ndarray | None 998 :param t_axis: The array axis direction of the time-step, defaults to 2, defaults to 2 999 :type t_axis: int, optional 1000 :param nb_minimum_chunks: minimal number of chunk required adjust an extrem law and 1001 compute the quantile, defaults to 4 1002 :type nb_minimum_chunks: int, optional 1003 :param chunk_size: The chunk_size (days). It correspond to the 'unit' of the 1004 return period, defaults to 365 (year) 1005 :type chunk_size: int, optional 1006 :param quantile_duration: The duration of the quantile (hour), defaults to 1 1007 :type quantile_duration: int | float, optional 1008 :return: an np.ndarray of spatial maximal values for every chunk 1009 :rtype: np.ndarray 1010 1011 """ 1012 1013 if pd.Timedelta( 1014 hours=quantile_duration, 1015 ) > pd.Timedelta( 1016 days=chunk_size, 1017 ): 1018 raise ValueError( 1019 "The chunk_size {chunk_size} (days) must be" 1020 " greater than the quantile duration {quantile_duration} (hours)" 1021 ) 1022 1023 # compute the nb of chunk in the input data 1024 nbchunks = int( 1025 array.shape[t_axis] 1026 * pd.Timedelta(hours=quantile_duration) 1027 / pd.Timedelta(days=chunk_size) 1028 ) 1029 print(f"</> Data contain {nbchunks} chunks of {pd.Timedelta(days=chunk_size)}") 1030 1031 nb_ts_by_chunk = int(array.shape[t_axis] / nbchunks) 1032 1033 if nbchunks < nb_minimum_chunks: 1034 raise ValueError( 1035 "</> The number of simulated chunks is not enough to compute the quantile:" 1036 f" {nbchunks}<{nb_minimum_chunks}" 1037 ) 1038 1039 # nb year limit to compute the quantile 1040 if nbchunks >= nb_minimum_chunks: 1041 print( 1042 f"</> Compute the maxima for every chunk with size" 1043 f" {nb_ts_by_chunk} time-steps" 1044 f" of {quantile_duration} hours." 1045 ) 1046 out_shape = [] 1047 list_axes = [] 1048 for nshape in range(len(array.shape)): 1049 out_shape.append(0) 1050 list_axes.append(nshape) 1051 1052 out_shape[t_axis] = nbchunks 1053 list_axes.remove(t_axis) 1054 1055 for ax in list_axes: 1056 out_shape[ax] = array.shape[ax] 1057 1058 maxima = np.zeros(shape=out_shape) 1059 1060 nb_ts_by_chunk = int(array.shape[t_axis] / nbchunks) 1061 remainder = array.shape[t_axis] % nbchunks 1062 pos = 0 1063 1064 # original_axis = [i for i in range(len(array.shape))] 1065 # other_axis = original_axis.copy() 1066 # other_axis.remove(t_axis) 1067 # destination_axis = [original_axis[t_axis]] + other_axis 1068 1069 # print(original_axis, destination_axis) 1070 1071 # print(maxima.shape) 1072 # print(array.shape) 1073 # move_axis first to extend the possibiltiy to use this function with different array shape 1074 array = np.moveaxis(array, t_axis, 0) 1075 maxima = np.moveaxis(maxima, t_axis, 0) 1076 # print(maxima.shape) 1077 # print(array.shape) 1078 for i in range(nbchunks): 1079 # Alternate one more cell because nb_ts_by_chunk is not a factor of array.shape[t_axis] 1080 if i % 2 > 0 and remainder > 0: 1081 onemore = 1 1082 else: 1083 onemore = 0 1084 1085 # maxima[:, :, i] = np.max( 1086 # array[:, :, int(i * nb_ts_by_chunk) : int((i + 1) * nb_ts_by_chunk)], 1087 # axis=t_axis, 1088 # ) 1089 # print(array.shape, pos, int(pos + nb_ts_by_chunk + onemore)) 1090 # maxima[:, :, i] = np.max( 1091 # array[:, :, pos : int(pos + nb_ts_by_chunk + onemore)], 1092 # axis=t_axis, 1093 # ) 1094 1095 # Check if value >-99 and check if nb lacuna <20% ? Or remove lowers maxima after all (10%) 1096 # if (array[pos : int(pos + nb_ts_by_chunk + onemore), :]) 1097 1098 maxima[i, :] = np.max( 1099 array[pos : int(pos + nb_ts_by_chunk + onemore), :], 1100 axis=0, 1101 ) 1102 pos = int(pos + nb_ts_by_chunk + onemore) 1103 1104 array = np.moveaxis(array, 0, t_axis) 1105 maxima = np.moveaxis(maxima, 0, t_axis) 1106 1107 return maxima 1108 1109 1110def quantil_obs( 1111 qobs_directory: str | None = None, 1112 code: np.ndarray | list = [], 1113 model_time_step: float = 3600, 1114 nb_minimum_chunks: int = 4, 1115 chunk_size: int = 365, 1116 quantile_duration: int = 1, 1117): 1118 1119 if qobs_directory is None: 1120 print("</> qobs_directory `{qobs_directory}` is not a valid directory") 1121 return 1122 1123 if isinstance(code, list): 1124 code = np.array(code) 1125 1126 qobs = tools.read_hourly_qobs(qobs_directory, code) 1127 1128 array = time_resample_array( 1129 array=qobs, 1130 quantile_duration=quantile_duration, 1131 model_time_step=model_time_step, 1132 quantile_chunk_size=chunk_size, 1133 t_axis=1, 1134 ) 1135 1136 maxima = compute_maxima( 1137 array=array, 1138 t_axis=1, 1139 nb_minimum_chunks=nb_minimum_chunks, 1140 chunk_size=chunk_size, 1141 quantile_duration=quantile_duration, 1142 ) 1143 1144 results = empirical_obs_quantile( 1145 maxima=maxima, 1146 nb_minimum_chunks=nb_minimum_chunks, 1147 chunk_size=chunk_size, 1148 quantile_duration=quantile_duration, 1149 ) 1150 1151 return results 1152 1153 1154def empirical_obs_quantile( 1155 maxima: np.ndarray | None, 1156 t_axis=1, 1157 nb_minimum_chunks: int = 4, 1158 frac_to_remove: float = 0.1, 1159 quantile_duration: int | float = 1, 1160 chunk_size: int = 365, 1161): 1162 """ 1163 :param maxima: Maximal discharge by chunk 1164 :type maxima: np.ndarray | None 1165 :param t_axis: axis of the time series, defaults to 1 1166 :type t_axis: TYPE, optional 1167 :param nb_minimum_chunks: number of minimum chunck required to remove `frac_to_remove` of the maxima distribution, i.e len of the maxima array in the t_axis direction, defaults to 4 1168 :type nb_minimum_chunks: int, optional 1169 :param frac_to_remove: fraction between 0 and 1 to remove lower value of the maxima distribution, incase of incomplete data chunk, defaults to 0.1 1170 :type frac_to_remove: float, default 0.1 1171 :param quantile_duration: duration of the quantile (hour) 1172 :type quantile_duration int | float, default 1 1173 :param chunk_size: size of the chunk used to comute the maxima 1174 :type chunk_size: int, default 365 1175 """ 1176 1177 maxima = np.moveaxis(maxima, t_axis, 0) 1178 maxima_sorted = np.sort(maxima, axis=0) 1179 maxima_sorted = np.where(maxima_sorted < 0, np.nan, maxima_sorted) 1180 T_emp = np.zeros(shape=maxima.shape) + np.nan 1181 # n = maxima.shape[0] 1182 1183 for sta in range(maxima_sorted.shape[1]): 1184 n_valid = np.where(maxima_sorted[:, sta] > 0) 1185 n = len(n_valid[0]) 1186 # remove 10% of the 1187 if n > nb_minimum_chunks: 1188 n_to_remove = max(1, int(frac_to_remove * n)) 1189 else: 1190 n_to_remove = 0 1191 1192 for i in range(n_to_remove): 1193 ind = n_valid[0][i] 1194 maxima_sorted[i] = np.nan 1195 1196 if n > nb_minimum_chunks: 1197 rank = 1 1198 for i in range(n_to_remove, n): 1199 probs = (rank - 0.5) / n 1200 T_emp[n_valid[0][i], sta] = 1 / (1 - probs) 1201 rank = rank + 1 1202 1203 # trim/filter nan cells 1204 index = [] 1205 for t in range(maxima_sorted.shape[0]): 1206 if np.any(maxima_sorted[t, :] >= 0): 1207 index.append(t) 1208 1209 maxima_sorted = maxima_sorted[index, :] 1210 T_emp = T_emp[index, :] 1211 1212 maxima_sorted = np.moveaxis(maxima_sorted, 0, t_axis) 1213 T_emp = np.moveaxis(T_emp, 0, t_axis) 1214 1215 results = { 1216 "maxima": maxima_sorted, 1217 "Temp": T_emp, 1218 "chunk_size": chunk_size, 1219 "nb_chunks": maxima.shape[t_axis], 1220 "quantile_duration": quantile_duration, 1221 } 1222 return results 1223 1224 1225@tools.autocast_args 1226def fit_quantile( 1227 maxima: np.ndarray | None, 1228 t_axis: int = 2, 1229 return_periods: list | tuple = [2, 5, 10, 20, 50, 100], 1230 fit: str = "gumbel", 1231 estimate_method: str = "MLE", 1232 quantile_duration: int | float = 1, 1233 chunk_size: int = 365, 1234 ncpu: int | None = None, 1235): 1236 """ 1237 Proceed to the qunaitle adjustment. 1238 :param maxima: an np.ndarray of spatial maximal values for every chunk 1239 :type maxima: np.ndarray | None 1240 :param t_axis: The array axis direction of the time-step, defaults to 2, 1241 defaults to 2, defaults to 2 1242 :type t_axis: int, optional 1243 :param return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100] 1244 :type return_periods: list | tuple, optional 1245 :param fit: The extrem law to use (gumbel or gev), defaults to "gumbel" 1246 :type fit: str, optional 1247 :param estimate_method: The methode to use for calibrated the parameters 1248 of the extrem law (MLE or MM), defaults to "MLE" 1249 :type estimate_method: str, optional 1250 :param quantile_duration: The duration of the quantile (hour), defaults to 1 1251 :type quantile_duration: int | float, optional 1252 :param chunk_size: the size of the chunks in days, defaults to 365 1253 :type chunk_size: int, optional 1254 :param ncpu: Number of cpu to use, defaults to None 1255 :type ncpu: int | None, optional 1256 :return: A dictionary containing the results od the quantile computation: 1257 Results include the quantile, the empirical return period, 1258 the maxima for each chunk of chunk_size, the fit parameters of the `fit` extrem law. 1259 :rtype: dict 1260 1261 """ 1262 1263 if ncpu is None: 1264 ncpu = int(os.cpu_count() / 2) 1265 else: 1266 ncpu = int(min(ncpu, os.cpu_count() - 1)) 1267 1268 if len(maxima.shape) < 2 or len(maxima.shape) > 3: 1269 raise ValueError( 1270 f"Input array `maxima` must have a dimension of 2 or 3. " 1271 f"Actual input have a dimension of {len(maxima.shape)} with shape {maxima.shape}" 1272 ) 1273 1274 grid_shape = list(maxima.shape) 1275 del grid_shape[t_axis] 1276 1277 fit_shape = np.zeros(shape=grid_shape) 1278 fit_loc = np.zeros(shape=grid_shape) 1279 fit_scale = np.zeros(shape=grid_shape) 1280 quantile = np.zeros(shape=grid_shape + [len(return_periods)]) * np.nan 1281 1282 # list_axes = [ax for ax in range(len(maxima.shape))] 1283 # list_axes.remove(t_axis) 1284 1285 # fit_shape = np.zeros(shape=(maxima.shape[list_axes[0]], maxima.shape[list_axes[1]])) 1286 # fit_loc = np.zeros(shape=(maxima.shape[list_axes[0]], maxima.shape[list_axes[1]])) 1287 # fit_scale = np.zeros(shape=(maxima.shape[list_axes[0]], maxima.shape[list_axes[1]])) 1288 1289 # quantile = ( 1290 # np.zeros( 1291 # shape=( 1292 # maxima.shape[list_axes[0]], 1293 # maxima.shape[list_axes[1]], 1294 # len(return_periods), 1295 # ) 1296 # ) 1297 # * np.nan 1298 # ) 1299 1300 # Total length of the grid, linearize the matrix => 1 dim 1301 nd = 1 1302 for d in grid_shape: 1303 nd = nd * d 1304 1305 original_shape = maxima.shape 1306 newshape = (nd, maxima.shape[-1]) 1307 1308 maxima = maxima.reshape(newshape) 1309 fit_shape = fit_shape.reshape(nd) 1310 fit_loc = fit_loc.reshape(nd) 1311 fit_scale = fit_scale.reshape(nd) 1312 quantile = quantile.reshape((nd, len(return_periods))) 1313 1314 print(f"</> Fitting {fit} law on data using {estimate_method} method.") 1315 1316 pool = multiprocessing.Pool(ncpu) 1317 1318 # Split by chunk to display a progress bar. chunk have the size of cpu 1319 chunksize = ncpu 1320 nbchunk = int(maxima.shape[0] / chunksize) 1321 1322 for chunk in tqdm(range(nbchunk + 1)): 1323 1324 args = [] 1325 index_i = [] 1326 1327 for i in range(chunk * chunksize, chunk * chunksize + chunksize): 1328 1329 if i >= nd: 1330 break 1331 1332 if np.all(maxima[i, :] > 0.0): 1333 args.append((maxima[i, :], estimate_method)) 1334 index_i.append(i) 1335 1336 if fit == "gumbel": 1337 res = pool.starmap(gumbel_r_fit, args, chunksize=1) 1338 1339 if fit == "gev": 1340 res = pool.starmap(genextreme_fit, args, chunksize=1) 1341 1342 k = 0 1343 for item in res: 1344 i = index_i[k] 1345 fit_shape[i] = item[0] 1346 fit_loc[i] = item[1] 1347 fit_scale[i] = item[2] 1348 1349 for index, T in enumerate(return_periods): 1350 if fit == "gumbel": 1351 quantile[i, index] = quantile_gumbel(T, item[1], item[2]) 1352 if fit == "gev": 1353 quantile[i, index] = quantile_gev(T, item[0], item[1], item[2]) 1354 1355 k = k + 1 1356 1357 maxima = maxima.reshape(original_shape) 1358 fit_shape = fit_shape.reshape(grid_shape) 1359 fit_loc = fit_loc.reshape(grid_shape) 1360 fit_scale = fit_scale.reshape(grid_shape) 1361 quantile = quantile.reshape(grid_shape + [len(return_periods)]) 1362 1363 # pool = multiprocessing.Pool(ncpu) 1364 1365 # print(f"</> Fitting {fit} law on data using {estimate_method} method.") 1366 1367 # for i in tqdm(range(maxima.shape[list_axes[0]])): 1368 1369 # args = [] 1370 # index_j = [] 1371 1372 # for j in range(maxima.shape[list_axes[1]]): 1373 # if np.all(maxima[i, j, :] > 0.0): 1374 # args.append((maxima[i, j, :], estimate_method)) 1375 # index_j.append(j) 1376 1377 # if fit == "gumbel": 1378 # res = pool.starmap(gumbel_r_fit, args, chunksize=1) 1379 1380 # if fit == "gev": 1381 # res = pool.starmap(genextreme_fit, args, chunksize=1) 1382 1383 # k = 0 1384 # for item in res: 1385 1386 # j = index_j[k] 1387 1388 # fit_shape[i, j] = item[0] 1389 # fit_loc[i, j] = item[1] 1390 # fit_scale[i, j] = item[2] 1391 1392 # for index, T in enumerate(return_periods): 1393 # if fit == "gumbel": 1394 # quantile[i, j, index] = quantile_gumbel(T, item[1], item[2]) 1395 # if fit == "gev": 1396 # quantile[i, j, index] = quantile_gev(T, item[0], item[1], item[2]) 1397 1398 # k = k + 1 1399 1400 pool.close() 1401 pool.terminate() 1402 1403 n = maxima.shape[t_axis] 1404 ranks = np.arange(1, n + 1) 1405 # probs = (ranks - 0.44) / (n + 0.12) 1406 probs = (ranks - 0.5) / n 1407 T_emp = 1 / (1 - probs) 1408 1409 results = { 1410 "T": np.array(return_periods), 1411 "Q_th": quantile, 1412 "T_emp": T_emp, 1413 "maxima": maxima, 1414 "nb_chunks": maxima.shape[t_axis], 1415 "fit": fit, 1416 "fit_loc": fit_loc, 1417 "fit_scale": fit_scale, 1418 "fit_shape": fit_shape, 1419 "duration": quantile_duration, 1420 "chunk_size": chunk_size, 1421 } 1422 1423 return results 1424 1425 1426@tools.autocast_args 1427def parametric_bootstrap_uncertainties( 1428 q_results: dict | None, 1429 bootstrap_sample=100, 1430 estimate_method: str = "MLE", 1431 ncpu: int | None = None, 1432): 1433 """ 1434 Proceed to the qunaitle adjustment. 1435 :param q_results: A dictionary containing the results of the quantile adjustment 1436 :type q_results: dict | None 1437 :param bootstrap_sample: the number of bootstrap sample, defaults to 100 1438 :type bootstrap_sample: int, optional 1439 :param estimate_method: The methode to use for calibrated the parameters 1440 of the extrem law (MLE or MM), defaults to "MLE" 1441 :type estimate_method: str, optional 1442 :param ncpu: Number of cpu to use, defaults to None 1443 :type ncpu: int | None, optional 1444 :return: A dictionary containing the results od the quantile computation: 1445 Results include the quantile, the empirical return period, 1446 the maxima for each chunk of chunk_size, the fit parameters of the `fit` extrem law. 1447 :rtype: dict 1448 1449 """ 1450 # R workaround as normal number (int) are passed to float to python 1451 # ncpu = int(ncpu) 1452 1453 if ncpu is None: 1454 ncpu = int(os.cpu_count() / 2) 1455 else: 1456 ncpu = min(ncpu, os.cpu_count() - 1) 1457 1458 sample_size = q_results["nb_chunks"] 1459 quantile = q_results["Q_th"] 1460 fit_loc = q_results["fit_loc"] 1461 fit_scale = q_results["fit_scale"] 1462 fit_shape = q_results["fit_shape"] 1463 1464 grid_shape = list(quantile.shape) 1465 del grid_shape[-1] # remove last dimension (return period) 1466 1467 parametric_quantile = ( 1468 np.zeros( 1469 shape=( 1470 *grid_shape, 1471 len(q_results["T"]), 1472 bootstrap_sample, 1473 ) 1474 ) 1475 * np.nan 1476 ) 1477 1478 nd = 1 1479 for d in grid_shape: 1480 nd = nd * d 1481 1482 # original_shape = parametric_quantile.shape 1483 newshape = (nd, len(q_results["T"]), bootstrap_sample) 1484 1485 parametric_quantile = parametric_quantile.reshape(newshape) 1486 fit_shape = fit_shape.reshape(nd) 1487 fit_loc = fit_loc.reshape(nd) 1488 fit_scale = fit_scale.reshape(nd) 1489 quantile = quantile.reshape(nd, len(q_results["T"])) 1490 1491 print(f"</> Compute bootstrap uncertainties...") 1492 1493 pool = multiprocessing.Pool(ncpu) 1494 1495 for i in tqdm(range(nd)): 1496 1497 if np.all(quantile[i, :] > 0.0): 1498 1499 args = [] 1500 index_k = [] 1501 for k in range(bootstrap_sample): 1502 1503 # generate sample 1504 if q_results["fit"] == "gumbel": 1505 random_q = stats.gumbel_r.rvs( 1506 loc=fit_loc[i], 1507 scale=fit_scale[i], 1508 size=sample_size, 1509 ) 1510 if q_results["fit"] == "gev": 1511 random_q = stats.genextreme_fit.rvs( 1512 fit_shape[i], 1513 loc=fit_loc[i], 1514 scale=fit_scale[i], 1515 size=sample_size, 1516 ) 1517 1518 args.append((random_q, q_results["fit"])) 1519 index_k.append(k) 1520 1521 if q_results["fit"] == "gumbel": 1522 res = pool.starmap(gumbel_r_fit, args, chunksize=1) 1523 1524 if q_results["fit"] == "gev": 1525 res = pool.starmap(genextreme_fit, args, chunksize=1) 1526 1527 for k, item in enumerate(res): 1528 1529 ik = index_k[k] 1530 1531 for indexT, T in enumerate(q_results["T"]): 1532 if q_results["fit"] == "gumbel": 1533 parametric_quantile[i, indexT, ik] = quantile_gumbel( 1534 T, item[1], item[2] 1535 ) 1536 if q_results["fit"] == "gev": 1537 parametric_quantile[i, indexT, ik] = quantile_gev( 1538 T, item[0], item[1], item[2] 1539 ) 1540 1541 Umax = np.max(parametric_quantile, axis=2) 1542 Umin = np.min(parametric_quantile, axis=2) 1543 1544 Umax = Umax.reshape(grid_shape + [len(q_results["T"])]) 1545 Umin = Umin.reshape(grid_shape + [len(q_results["T"])]) 1546 1547 # parametric_quantile = parametric_quantile.reshape(original_shape) 1548 # fit_shape = fit_shape.reshape(grid_shape) 1549 # fit_loc = fit_loc.reshape(grid_shape) 1550 # fit_scale = fit_scale.reshape(grid_shape) 1551 # quantile = quantile.reshape(grid_shape + [len(q_results["T"])]) 1552 1553 ##############################################" 1554 # for i in tqdm(range(q_results["Q_th"].shape[0])): 1555 # for j in range(q_results["Q_th"].shape[1]): 1556 1557 # if np.all(q_results["Q_th"][i, j] > 0.0): 1558 1559 # args = [] 1560 # index_k = [] 1561 # for k in range(bootstrap_sample): 1562 1563 # # generate sample 1564 # if q_results["fit"] == "gumbel": 1565 # random_q = stats.gumbel_r.rvs( 1566 # loc=q_results["fit_loc"][i, j], 1567 # scale=q_results["fit_scale"][i, j], 1568 # size=sample_size, 1569 # ) 1570 # if q_results["fit"] == "gev": 1571 # random_q = stats.genextreme_fit.rvs( 1572 # q_results["fit_shape"][i, j], 1573 # loc=q_results["fit_loc"][i, j], 1574 # scale=q_results["fit_scale"][i, j], 1575 # size=sample_size, 1576 # ) 1577 1578 # args.append((random_q, q_results["fit"])) 1579 # index_k.append(k) 1580 1581 # if q_results["fit"] == "gumbel": 1582 # res = pool.starmap(gumbel_r_fit, args, chunksize=1) 1583 1584 # if q_results["fit"] == "gev": 1585 # res = pool.starmap(genextreme_fit, args, chunksize=1) 1586 1587 # # k = 0 1588 # for k, item in enumerate(res): 1589 1590 # ik = index_k[k] 1591 1592 # for indexT, T in enumerate(return_periods): 1593 # if q_results["fit"] == "gumbel": 1594 # parametric_quantile[i, j, indexT, ik] = quantile_gumbel( 1595 # T, item[1], item[2] 1596 # ) 1597 # if q_results["fit"] == "gev": 1598 # parametric_quantile[i, j, indexT, ik] = quantile_gev( 1599 # T, item[0], item[1], item[2] 1600 # ) 1601 1602 pool.close() 1603 pool.terminate() 1604 1605 q_results.update({"Umax": Umax, "Umin": Umin}) 1606 1607 return q_results 1608 1609 1610@tools.autocast_args 1611def fit_quantile_unparallel( 1612 maxima: np.ndarray | None, 1613 t_axis: int = 2, 1614 return_periods: list | tuple = [2, 5, 10, 20, 50, 100], 1615 fit: str = "gumbel", 1616 estimate_method: str = "MLE", 1617 quantile_duration: int | float = 1.0, 1618 chunk_size: int = 365, 1619): 1620 """ 1621 Proceed to the qunaitle adjustment (unparrallel computation). 1622 :param maxima: an np.ndarray of spatial maximal values for every chunk 1623 :type maxima: np.ndarray | None 1624 :param t_axis: The array axis direction of the time-step, defaults to 2, 1625 defaults to 2, defaults to 2 1626 :type t_axis: int, optional 1627 :param return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100] 1628 :type return_periods: list | tuple, optional 1629 :param fit: The extrem law to use (gumbel or gev), defaults to "gumbel" 1630 :type fit: str, optional 1631 :param estimate_method: The methode to use for calibrated the parameters 1632 of the extrem law (MLE or MM), defaults to "MLE" 1633 :type estimate_method: str, optional 1634 :param quantile_duration: The duration of the quantile (hour), defaults to 1 1635 :type quantile_duration: int | float, optional 1636 :param chunk_size: the size of the chunks in days, defaults to 365 1637 :type chunk_size: int, optional 1638 :return: A dictionary containing the results od the quantile computation: 1639 Results include the quantile, the empirical return period, 1640 the maxima for each chunk of chunk_size, the fit parameters of the `fit` extrem law. 1641 :rtype: dict 1642 1643 """ 1644 # # R workaround as normal number (int) are passed to float to python 1645 # t_axis = int(t_axis) 1646 # chunk_size = int(chunk_size) 1647 1648 list_axes = [0, 1, 2] 1649 list_axes.remove(t_axis) 1650 1651 fit_shape = np.zeros(shape=(maxima.shape[list_axes[0]], maxima.shape[list_axes[1]])) 1652 fit_loc = np.zeros(shape=(maxima.shape[list_axes[0]], maxima.shape[list_axes[1]])) 1653 fit_scale = np.zeros(shape=(maxima.shape[list_axes[0]], maxima.shape[list_axes[1]])) 1654 1655 quantile = ( 1656 np.zeros( 1657 shape=( 1658 maxima.shape[list_axes[0]], 1659 maxima.shape[list_axes[1]], 1660 len(return_periods), 1661 ) 1662 ) 1663 * np.nan 1664 ) 1665 1666 print(f"</> Fitting {fit} law on data using {estimate_method} method.") 1667 1668 for i in range(maxima.shape[list_axes[0]]): 1669 1670 for j in range(maxima.shape[list_axes[1]]): 1671 1672 if np.all(maxima[i, j, :] > 0.0): 1673 1674 if fit == "gumbel": 1675 res = gumbel_r_fit(maxima[i, j, :], estimate_method) 1676 1677 if fit == "gev": 1678 res = genextreme_fit(maxima[i, j, :], estimate_method) 1679 1680 fit_shape[i, j] = res[0] 1681 fit_loc[i, j] = res[1] 1682 fit_scale[i, j] = res[2] 1683 1684 for index, T in enumerate(return_periods): 1685 if fit == "gumbel": 1686 quantile[i, j, index] = quantile_gumbel(T, res[1], res[2]) 1687 if fit == "gev": 1688 quantile[i, j, index] = quantile_gev(T, res[0], res[1], res[2]) 1689 1690 n = maxima.shape[t_axis] 1691 ranks = np.arange(1, n + 1) 1692 # probs = (ranks - 0.44) / (n + 0.12) 1693 probs = (ranks - 0.5) / n 1694 T_emp = 1 / (1 - probs) 1695 1696 results = { 1697 "T": np.array(return_periods), 1698 "Q_th": quantile, 1699 "T_emp": T_emp, 1700 "maxima": maxima, 1701 "nb_chunks": maxima.shape[t_axis], 1702 "fit": fit, 1703 "fit_loc": fit_loc, 1704 "fit_scale": fit_scale, 1705 "fit_shape": fit_shape, 1706 "duration": quantile_duration, 1707 "chunk_size": chunk_size, 1708 } 1709 1710 return results 1711 1712 1713@tools.autocast_args 1714def parametric_bootstrap_uncertainties_unparallel( 1715 q_results: dict | None, 1716 bootstrap_sample=100, 1717 return_periods: list | tuple = [2, 5, 10, 20, 50, 100], 1718 estimate_method: str = "MLE", 1719): 1720 """ 1721 Proceed to the qunaitle adjustment. 1722 :param q_results: A dictionary containing the results of the quantile adjustment 1723 :type q_results: dict | None 1724 :param bootstrap_sample: the number of bootstrap sample, defaults to 100 1725 :type bootstrap_sample: int, optional 1726 :param return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100] 1727 :type return_periods: list | tuple, optional 1728 :param estimate_method: The methode to use for calibrated the parameters 1729 of the extrem law (MLE or MM), defaults to "MLE" 1730 :type estimate_method: str, optional 1731 :return: A dictionary containing the results od the quantile computation: 1732 Results include the quantile, the empirical return period, 1733 the maxima for each chunk of chunk_size, the fit parameters of the `fit` extrem law. 1734 :rtype: dict 1735 1736 """ 1737 1738 sample_size = q_results["nb_chunks"] 1739 1740 parametric_quantile = ( 1741 np.zeros( 1742 shape=( 1743 q_results["Q_th"].shape[0], 1744 q_results["Q_th"].shape[1], 1745 len(q_results["T"]), 1746 bootstrap_sample, 1747 ) 1748 ) 1749 * np.nan 1750 ) 1751 1752 for i in tqdm(range(q_results["Q_th"].shape[0])): 1753 for j in range(q_results["Q_th"].shape[1]): 1754 1755 if np.all(q_results["Q_th"][i, j] > 0.0): 1756 1757 for k in range(bootstrap_sample): 1758 1759 # generate sample 1760 if q_results["fit"] == "gumbel": 1761 random_q = stats.gumbel_r.rvs( 1762 loc=q_results["fit_loc"][i, j], 1763 scale=q_results["fit_scale"][i, j], 1764 size=sample_size, 1765 ) 1766 if q_results["fit"] == "gev": 1767 random_q = stats.genextreme_fit.rvs( 1768 q_results["shape_loc"][i, j], 1769 loc=q_results["fit_loc"][i, j], 1770 scale=q_results["fit_scale"][i, j], 1771 size=sample_size, 1772 ) 1773 1774 if q_results["fit"] == "gumbel": 1775 res = gumbel_r_fit(random_q, estimate_method) 1776 1777 if q_results["fit"] == "gev": 1778 res = genextreme_fit(random_q, estimate_method) 1779 1780 for indexT, T in enumerate(return_periods): 1781 if q_results["fit"] == "gumbel": 1782 parametric_quantile[i, j, indexT, k] = quantile_gumbel( 1783 T, res[1], res[2] 1784 ) 1785 if q_results["fit"] == "gev": 1786 parametric_quantile[i, j, indexT, k] = quantile_gev( 1787 T, res[0], res[1], res[2] 1788 ) 1789 1790 Umax = np.max(parametric_quantile, axis=3) 1791 Umin = np.min(parametric_quantile, axis=3) 1792 1793 q_results.update({"Umax": Umax, "Umin": Umin}) 1794 1795 return q_results 1796 1797 1798@tools.autocast_args 1799def spatial_quantiles( 1800 array: np.ndarray | None, 1801 t_axis: int = 2, 1802 return_periods: list | tuple = [2, 5, 10, 20, 50, 100], 1803 fit: str = "gumbel", 1804 nb_minimum_chunks: int = 4, 1805 model_time_step: float = 3600, 1806 estimate_method: str = "MLE", 1807 chunk_size: int = 365, 1808 quantile_duration: int | float = 1, 1809 ncpu: int | None = None, 1810 compute_uncertainties=False, 1811 bootstrap_sample=100, 1812 maxima: np.ndarray | None = None, 1813): 1814 """ 1815 Proceed to the quantile adjustment (parrallel computation). 1816 :param array: an np.ndarray of spatial dicharges values for every time-step 1817 :type array: np.ndarray | None 1818 :param t_axis: The array axis direction of the time-step, defaults to 2, 1819 defaults to 2, defaults to 2 1820 :type t_axis: int, optional 1821 :param return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100] 1822 :type return_periods: list | tuple, optional 1823 :param fit: The extrem law to use (gumbel or gev), defaults to "gumbel" 1824 :type fit: str, optional 1825 :param estimate_method: The methode to use for calibrated the parameters 1826 of the extrem law (MLE or MM), defaults to "MLE" 1827 :type estimate_method: str, optional 1828 :param quantile_duration: The duration of the quantile (hour), defaults to 1 1829 :type quantile_duration: int | float, optional 1830 :param chunk_size: the size of the chunks in days, defaults to 365 1831 :type chunk_size: int, optional 1832 :param ncpu: Number of cpu to use, defaults to None 1833 :type ncpu: int | None, optional 1834 :param compute_uncertainties: Compute the uncertainties usung the parametric bootstrap method 1835 :type compute_uncertainties: bool, True 1836 :param bootstrap_sample: the size of bootstrap sample, default is 100 1837 :type bootstrap_sample: int, optional 1838 :return: A dictionary containing the results od the quantile computation: 1839 Results include the quantile, the empirical return period, 1840 the maxima for each chunk of chunk_size, the fit parameters of the `fit` extrem law. 1841 :rtype: dict 1842 1843 """ 1844 # # R workaround as normal number (int) are passed to float to python 1845 # t_axis = int(t_axis) 1846 # chunk_size = int(chunk_size) 1847 1848 if ncpu is None: 1849 ncpu = int(os.cpu_count() / 2) 1850 else: 1851 ncpu = int(min(ncpu, os.cpu_count() - 1)) 1852 1853 if pd.Timedelta( 1854 hours=quantile_duration, 1855 ) < pd.Timedelta( 1856 seconds=model_time_step, 1857 ): 1858 raise ValueError( 1859 "The quantile duration {quantile_duration} (hours) must be" 1860 "greater or equal than the model time step {model_time_step} (seconds)" 1861 ) 1862 1863 if pd.Timedelta( 1864 hours=quantile_duration, 1865 ) > pd.Timedelta( 1866 days=chunk_size, 1867 ): 1868 raise ValueError( 1869 "The chunk_size {chunk_size} (days) must be" 1870 " greater or equal than the quantile duration {quantile_duration} (hours)" 1871 ) 1872 1873 if array is None: 1874 raise ValueError("Input array is None, no data to compute quantile.") 1875 1876 if maxima is None: 1877 array = time_resample_array( 1878 array=array, 1879 quantile_duration=quantile_duration, 1880 model_time_step=model_time_step, 1881 quantile_chunk_size=chunk_size, 1882 t_axis=t_axis, 1883 ) 1884 1885 maxima = compute_maxima( 1886 array=array, 1887 t_axis=t_axis, 1888 nb_minimum_chunks=nb_minimum_chunks, 1889 chunk_size=chunk_size, 1890 quantile_duration=quantile_duration, 1891 ) 1892 1893 results = fit_quantile( 1894 maxima=maxima, 1895 t_axis=t_axis, 1896 return_periods=return_periods, 1897 fit=fit, 1898 estimate_method=estimate_method, 1899 quantile_duration=quantile_duration, 1900 chunk_size=chunk_size, 1901 ncpu=ncpu, 1902 ) 1903 1904 if compute_uncertainties: 1905 # calcul des incertitudes méthode bootstrap 1906 results = parametric_bootstrap_uncertainties( 1907 q_results=results, 1908 bootstrap_sample=bootstrap_sample, 1909 estimate_method=estimate_method, 1910 ncpu=ncpu, 1911 ) 1912 1913 return results 1914 1915 1916@tools.autocast_args 1917def spatial_quantiles_unparallel( 1918 array: np.ndarray | None = None, 1919 t_axis: int = 2, 1920 return_periods: list | tuple = [2, 5, 10, 20, 50, 100], 1921 fit: str = "gumbel", 1922 nb_minimum_chunks: int = 4, 1923 model_time_step: float = 3600, 1924 estimate_method: str = "MLE", 1925 chunk_size: int = 365, 1926 compute_uncertainties=False, 1927 bootstrap_sample=100, 1928 quantile_duration: int | float = 1, 1929 maxima: np.ndarray | None = None, 1930): 1931 """ 1932 Proceed to the quantile adjustment (unparrallel computation). 1933 :param array: an np.ndarray of spatial dicharges values for every time-step 1934 :type array: np.ndarray | None 1935 :param t_axis: The array axis direction of the time-step, defaults to 2, 1936 defaults to 2, defaults to 2 1937 :type t_axis: int, optional 1938 :param return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100] 1939 :type return_periods: list | tuple, optional 1940 :param fit: The extrem law to use (gumbel or gev), defaults to "gumbel" 1941 :type fit: str, optional 1942 :param estimate_method: The methode to use for calibrated the parameters 1943 of the extrem law (MLE or MM), defaults to "MLE" 1944 :type estimate_method: str, optional 1945 :param quantile_duration: The duration of the quantile (hour), defaults to 1 1946 :type quantile_duration: int | float, optional 1947 :param chunk_size: the size of the chunks in days, defaults to 365 1948 :type chunk_size: int, optional 1949 :param compute_uncertainties: Compute the uncertainties usung the parametric bootstrap method 1950 :type compute_uncertainties: bool, True 1951 :param bootstrap_sample: the size of bootstrap sample, default is 100 1952 :type bootstrap_sample: int, optional 1953 :return: A dictionary containing the results od the quantile computation: 1954 Results include the quantile, the empirical return period, 1955 the maxima for each chunk of chunk_size, the fit parameters of the `fit` extrem law. 1956 :rtype: dict 1957 1958 """ 1959 # # R workaround as normal number (int) are passed to float to python 1960 # t_axis = int(t_axis) 1961 # chunk_size = int(chunk_size) 1962 1963 if pd.Timedelta( 1964 hours=quantile_duration, 1965 ) < pd.Timedelta( 1966 seconds=model_time_step, 1967 ): 1968 raise ValueError( 1969 "The quantile duration {quantile_duration} (hours) must be" 1970 "greater or equal than the model time step {model_time_step} (seconds)" 1971 ) 1972 1973 if pd.Timedelta( 1974 hours=quantile_duration, 1975 ) > pd.Timedelta( 1976 days=chunk_size, 1977 ): 1978 raise ValueError( 1979 "The chunk_size {chunk_size} (days) must be" 1980 " greater or equal than the quantile duration {quantile_duration} (hours)" 1981 ) 1982 1983 if array is None: 1984 raise ValueError("Input array is None, no data to compute quantile.") 1985 1986 if maxima is None: 1987 array = time_resample_array( 1988 array=array, 1989 quantile_duration=quantile_duration, 1990 model_time_step=model_time_step, 1991 quantile_chunk_size=chunk_size, 1992 t_axis=t_axis, 1993 ) 1994 1995 maxima = compute_maxima( 1996 array=array, 1997 t_axis=t_axis, 1998 nb_minimum_chunks=nb_minimum_chunks, 1999 chunk_size=chunk_size, 2000 quantile_duration=quantile_duration, 2001 ) 2002 2003 results = fit_quantile_unparallel( 2004 maxima=maxima, 2005 t_axis=t_axis, 2006 return_periods=return_periods, 2007 fit=fit, 2008 estimate_method=estimate_method, 2009 quantile_duration=quantile_duration, 2010 chunk_size=chunk_size, 2011 ) 2012 2013 if compute_uncertainties: 2014 # calcul des incertitudes méthode bootstrap 2015 parametric_bootstrap_uncertainties_unparallel( 2016 q_results=results, 2017 bootstrap_sample=bootstrap_sample, 2018 return_periods=return_periods, 2019 estimate_method=estimate_method, 2020 ) 2021 2022 return results
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria mse: mse = (1.0 / nb_valid_data) * np.sum((obs - sim) ** 2.0)
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria mse: mse = (1.0 / nb_valid_data) * np.sum((obs - sim) ** 2.0)
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria rmse: rmse = np.sqrt(res_mse)
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria rmse: rmse = np.sqrt(res_mse)
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria nrmse: nrmse = res_rmse / mean_obs
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria nrmse: nrmse = res_rmse / mean_obs
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria se: se = ( np.sum((obs - sim)** 2.0, axis=t_axis, where=mask_nodata) )
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria se: se = ( np.sum((obs - sim)** 2.0, axis=t_axis, where=mask_nodata) )
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria mae: mae = 1/n*(np.sum(abs(obs - sim))
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria mae: mae = np.sqrt(np.sum(abs(obs - sim))
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria mape: mape = 1/n*( np.sum(abs((obs - sim) / obs)) )
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria mape: mape = np.sqrt( np.sum(abs((obs - sim) / obs)) )
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria lgrm: lgrm = np.sum( obs * (np.log((obs / sim) ** 2.0)), axis=t_axis, where=mask_nodata )
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria lgrm: lgrm = np.sum( obs * (np.log((obs / sim) ** 2.0)), axis=t_axis, where=mask_nodata )
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria nse: numerator = np.sum((obs - sim) ** 2.0, axis=t_axis, where=mask_nodata)
denominator = np.sum(
(obs - mean_obs) ** 2.0, axis=t_axis, where=mask_nodata
)
denominator = np.where(denominator == 0, np.nan, denominator)
nse = 1 - numerator / denominator
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria nse: numerator = np.sum((obs - sim) ** 2.0, axis=t_axis, where=mask_nodata)
denominator = np.sum(
(obs - mean_obs) ** 2.0, axis=t_axis, where=mask_nodata
)
denominator = np.where(denominator == 0, np.nan, denominator)
nse = 1 - numerator / denominator
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria nnse: nnse = 1.0 / (2.0 - res_nse)
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria nnse: nnse = 1.0 / (2.0 - res_nse)
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria kge: std_obs = np.std( obs, axis=t_axis, where=mask_nodata, keepdims=True, mean=mean_obs ) std_sim = np.std( sim, axis=t_axis, where=mask_nodata, keepdims=True, mean=mean_sim )
beta = mean_sim / mean_obs
alpha = std_sim / std_obs
r2 = ( 1 / (sim.shape[t_axis]) * np.sum( ((sim - mean_sim) / std_sim) * ((obs - mean_obs) / std_obs), axis=t_axis, keepdims=True, ) )
kge = 1 - np.sqrt( (r2 - 1.0) * 2.0 + (alpha - 1.0) * 2.0 + (beta - 1) ** 2.0 )
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
- nodata: No data value, defaults to -99.0
- t_axis: Array axis of the time, defaults to 0
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the misfit criteria kge, see the Smash documentation at https://smash.recover.inrae.fr/math_num_documentation/efficiency_error_metric.html
Parameters
- obs: Observed discharges, defaults to None
- sim: simulated discharges, defaults to None
825def quantile_gumbel(T: float = 1.0, loc: float = 0.0, scale: float = 0.0): 826 """ 827 Compute the quantile for a given return period using the Gumbel law. 828 :param T: The return period, defaults to 1 829 :type T: float, optional 830 :param loc: The localisation parameter of the Gumbel law, defaults to 0.0 831 :type loc: float, optional 832 :param scale: The scale parameter of the Gumbel law, defaults to 0.0 833 :type scale: float, optional 834 :return: The value of the quantile 835 :rtype: float 836 837 """ 838 839 return stats.gumbel_r.ppf(1 - 1 / T, loc=loc, scale=scale)
Compute the quantile for a given return period using the Gumbel law.
Parameters
- T: The return period, defaults to 1
- loc: The localisation parameter of the Gumbel law, defaults to 0.0
- scale: The scale parameter of the Gumbel law, defaults to 0.0
Returns
The value of the quantile
843def quantile_gev( 844 T: float = 1.0, shape: float = 0.0, loc: float = 0.0, scale: float = 0.0 845): 846 """ 847 Compute the quantile for a given return period using the GEV law. 848 :param T: The return period, defaults to 1 849 :type T: float, optional 850 :param shape: The shape parameter of the GEV law, defaults to 0.0 851 :type shape: float, optional 852 :param loc: The localisation parameter of the GEV law, defaults to 0.0 853 :type loc: float, optional 854 :param scale: The scale parameter of the GEV law, defaults to 0.0 855 :type scale: float, optional 856 :return: The value of the quantile 857 :rtype: float 858 859 """ 860 return stats.genextreme.ppf(1 - 1 / T, shape, loc=loc, scale=scale)
Compute the quantile for a given return period using the GEV law.
Parameters
- T: The return period, defaults to 1
- shape: The shape parameter of the GEV law, defaults to 0.0
- loc: The localisation parameter of the GEV law, defaults to 0.0
- scale: The scale parameter of the GEV law, defaults to 0.0
Returns
The value of the quantile
863def genextreme_fit(data: np.ndarray | None = None, estimate_method: str = "MLE"): 864 """ 865 Return estimates of shape, location, and scale parameters from data. The default 866 estimation method is Maximum Likelihood Estimation (MLE), but Method of Moments (MM) 867 is also available. 868 :param data: Data of maximum values used to fit a GEV, defaults to None 869 :type data: np.ndarray | None, optional 870 :param estimate_method: Method to optimize the parameters: MLE for Maximum Likelihood 871 Estimate, MM for Method of Moments , defaults to "MLE" 872 :type estimate_method: str, optional 873 :return: Estimates for any shape parameters (if applicable), followed by those for 874 location and scale. 875 :rtype: tuple of float 876 877 """ 878 if data is None: 879 raise ValueError("input data is None. You must provide valid data.") 880 res = stats.genextreme.fit(data, method=estimate_method) 881 return res
Return estimates of shape, location, and scale parameters from data. The default estimation method is Maximum Likelihood Estimation (MLE), but Method of Moments (MM) is also available.
Parameters
- data: Data of maximum values used to fit a GEV, defaults to None
- estimate_method: Method to optimize the parameters: MLE for Maximum Likelihood Estimate, MM for Method of Moments , defaults to "MLE"
Returns
Estimates for any shape parameters (if applicable), followed by those for location and scale.
884def gumbel_r_fit(data: np.ndarray | None = None, estimate_method: str = "MLE"): 885 """ 886 Return estimates of shape, location, and scale parameters from data. 887 The default estimation method is Maximum Likelihood Estimation (MLE), 888 but Method of Moments (MM) is also available. 889 :param data: Data of maximum values used to fit a Gumbel law, defaults to None 890 :type data: np.ndarray | None, optional 891 :param estimate_method: Method to optimize the parameters: MLE for Maximum Likelihood 892 Estimate, MM for Method of Moments , defaults to "MLE" 893 :type estimate_method: str, optional 894 :return: Estimates for any shape parameters (if applicable), followed by those for 895 location and scale. 896 :rtype: tuple of float 897 898 """ 899 if data is None: 900 raise ValueError("input data is None. You must provide valid data.") 901 902 res = stats.gumbel_r.fit(data, method=estimate_method) 903 return (0, *res)
Return estimates of shape, location, and scale parameters from data. The default estimation method is Maximum Likelihood Estimation (MLE), but Method of Moments (MM) is also available.
Parameters
- data: Data of maximum values used to fit a Gumbel law, defaults to None
- estimate_method: Method to optimize the parameters: MLE for Maximum Likelihood Estimate, MM for Method of Moments , defaults to "MLE"
Returns
Estimates for any shape parameters (if applicable), followed by those for location and scale.
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Resample the discharges array for a given time-step.
Parameters
- array: the matrix containing the discharge with shape (nbx, nby, nbts)
- quantile_duration: The duration of the quantile (hours), defaults to 1
- model_time_step: the time-step of the Smash model (seconds), defaults to 3600
- quantile_chunk_size: the size of the quantile chunk in days
- t_axis: The array axis direction of the time-step, defaults to 2
Returns
The resampled array
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Compute the maxima of the discharges for a given chunk_size in the t_axis direction.
Parameters
- array: the matrix containing the discharge with shape (nbx, nby, nbts). The shape can be smaller or higher. But t_axis must be set to target the nbts (number of time-step) dimension. Ex: if shape=(nbx, nbts), t_axis must be equal to 1.
- t_axis: The array axis direction of the time-step, defaults to 2, defaults to 2
- nb_minimum_chunks: minimal number of chunk required adjust an extrem law and compute the quantile, defaults to 4
- chunk_size: The chunk_size (days). It correspond to the 'unit' of the return period, defaults to 365 (year)
- quantile_duration: The duration of the quantile (hour), defaults to 1
Returns
an np.ndarray of spatial maximal values for every chunk
1111def quantil_obs( 1112 qobs_directory: str | None = None, 1113 code: np.ndarray | list = [], 1114 model_time_step: float = 3600, 1115 nb_minimum_chunks: int = 4, 1116 chunk_size: int = 365, 1117 quantile_duration: int = 1, 1118): 1119 1120 if qobs_directory is None: 1121 print("</> qobs_directory `{qobs_directory}` is not a valid directory") 1122 return 1123 1124 if isinstance(code, list): 1125 code = np.array(code) 1126 1127 qobs = tools.read_hourly_qobs(qobs_directory, code) 1128 1129 array = time_resample_array( 1130 array=qobs, 1131 quantile_duration=quantile_duration, 1132 model_time_step=model_time_step, 1133 quantile_chunk_size=chunk_size, 1134 t_axis=1, 1135 ) 1136 1137 maxima = compute_maxima( 1138 array=array, 1139 t_axis=1, 1140 nb_minimum_chunks=nb_minimum_chunks, 1141 chunk_size=chunk_size, 1142 quantile_duration=quantile_duration, 1143 ) 1144 1145 results = empirical_obs_quantile( 1146 maxima=maxima, 1147 nb_minimum_chunks=nb_minimum_chunks, 1148 chunk_size=chunk_size, 1149 quantile_duration=quantile_duration, 1150 ) 1151 1152 return results
1155def empirical_obs_quantile( 1156 maxima: np.ndarray | None, 1157 t_axis=1, 1158 nb_minimum_chunks: int = 4, 1159 frac_to_remove: float = 0.1, 1160 quantile_duration: int | float = 1, 1161 chunk_size: int = 365, 1162): 1163 """ 1164 :param maxima: Maximal discharge by chunk 1165 :type maxima: np.ndarray | None 1166 :param t_axis: axis of the time series, defaults to 1 1167 :type t_axis: TYPE, optional 1168 :param nb_minimum_chunks: number of minimum chunck required to remove `frac_to_remove` of the maxima distribution, i.e len of the maxima array in the t_axis direction, defaults to 4 1169 :type nb_minimum_chunks: int, optional 1170 :param frac_to_remove: fraction between 0 and 1 to remove lower value of the maxima distribution, incase of incomplete data chunk, defaults to 0.1 1171 :type frac_to_remove: float, default 0.1 1172 :param quantile_duration: duration of the quantile (hour) 1173 :type quantile_duration int | float, default 1 1174 :param chunk_size: size of the chunk used to comute the maxima 1175 :type chunk_size: int, default 365 1176 """ 1177 1178 maxima = np.moveaxis(maxima, t_axis, 0) 1179 maxima_sorted = np.sort(maxima, axis=0) 1180 maxima_sorted = np.where(maxima_sorted < 0, np.nan, maxima_sorted) 1181 T_emp = np.zeros(shape=maxima.shape) + np.nan 1182 # n = maxima.shape[0] 1183 1184 for sta in range(maxima_sorted.shape[1]): 1185 n_valid = np.where(maxima_sorted[:, sta] > 0) 1186 n = len(n_valid[0]) 1187 # remove 10% of the 1188 if n > nb_minimum_chunks: 1189 n_to_remove = max(1, int(frac_to_remove * n)) 1190 else: 1191 n_to_remove = 0 1192 1193 for i in range(n_to_remove): 1194 ind = n_valid[0][i] 1195 maxima_sorted[i] = np.nan 1196 1197 if n > nb_minimum_chunks: 1198 rank = 1 1199 for i in range(n_to_remove, n): 1200 probs = (rank - 0.5) / n 1201 T_emp[n_valid[0][i], sta] = 1 / (1 - probs) 1202 rank = rank + 1 1203 1204 # trim/filter nan cells 1205 index = [] 1206 for t in range(maxima_sorted.shape[0]): 1207 if np.any(maxima_sorted[t, :] >= 0): 1208 index.append(t) 1209 1210 maxima_sorted = maxima_sorted[index, :] 1211 T_emp = T_emp[index, :] 1212 1213 maxima_sorted = np.moveaxis(maxima_sorted, 0, t_axis) 1214 T_emp = np.moveaxis(T_emp, 0, t_axis) 1215 1216 results = { 1217 "maxima": maxima_sorted, 1218 "Temp": T_emp, 1219 "chunk_size": chunk_size, 1220 "nb_chunks": maxima.shape[t_axis], 1221 "quantile_duration": quantile_duration, 1222 } 1223 return results
Parameters
- maxima: Maximal discharge by chunk
- t_axis: axis of the time series, defaults to 1
- nb_minimum_chunks: number of minimum chunck required to remove
frac_to_removeof the maxima distribution, i.e len of the maxima array in the t_axis direction, defaults to 4 - frac_to_remove: fraction between 0 and 1 to remove lower value of the maxima distribution, incase of incomplete data chunk, defaults to 0.1
- quantile_duration: duration of the quantile (hour) :type quantile_duration int | float, default 1
- chunk_size: size of the chunk used to comute the maxima
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Proceed to the qunaitle adjustment.
Parameters
- maxima: an np.ndarray of spatial maximal values for every chunk
- t_axis: The array axis direction of the time-step, defaults to 2, defaults to 2, defaults to 2
- return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100]
- fit: The extrem law to use (gumbel or gev), defaults to "gumbel"
- estimate_method: The methode to use for calibrated the parameters of the extrem law (MLE or MM), defaults to "MLE"
- quantile_duration: The duration of the quantile (hour), defaults to 1
- chunk_size: the size of the chunks in days, defaults to 365
- ncpu: Number of cpu to use, defaults to None
Returns
A dictionary containing the results od the quantile computation: Results include the quantile, the empirical return period, the maxima for each chunk of chunk_size, the fit parameters of the
fitextrem law.
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Proceed to the qunaitle adjustment.
Parameters
- q_results: A dictionary containing the results of the quantile adjustment
- bootstrap_sample: the number of bootstrap sample, defaults to 100
- estimate_method: The methode to use for calibrated the parameters of the extrem law (MLE or MM), defaults to "MLE"
- ncpu: Number of cpu to use, defaults to None
Returns
A dictionary containing the results od the quantile computation: Results include the quantile, the empirical return period, the maxima for each chunk of chunk_size, the fit parameters of the
fitextrem law.
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Proceed to the qunaitle adjustment (unparrallel computation).
Parameters
- maxima: an np.ndarray of spatial maximal values for every chunk
- t_axis: The array axis direction of the time-step, defaults to 2, defaults to 2, defaults to 2
- return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100]
- fit: The extrem law to use (gumbel or gev), defaults to "gumbel"
- estimate_method: The methode to use for calibrated the parameters of the extrem law (MLE or MM), defaults to "MLE"
- quantile_duration: The duration of the quantile (hour), defaults to 1
- chunk_size: the size of the chunks in days, defaults to 365
Returns
A dictionary containing the results od the quantile computation: Results include the quantile, the empirical return period, the maxima for each chunk of chunk_size, the fit parameters of the
fitextrem law.
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Proceed to the qunaitle adjustment.
Parameters
- q_results: A dictionary containing the results of the quantile adjustment
- bootstrap_sample: the number of bootstrap sample, defaults to 100
- return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100]
- estimate_method: The methode to use for calibrated the parameters of the extrem law (MLE or MM), defaults to "MLE"
Returns
A dictionary containing the results od the quantile computation: Results include the quantile, the empirical return period, the maxima for each chunk of chunk_size, the fit parameters of the
fitextrem law.
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Proceed to the quantile adjustment (parrallel computation).
Parameters
- array: an np.ndarray of spatial dicharges values for every time-step
- t_axis: The array axis direction of the time-step, defaults to 2, defaults to 2, defaults to 2
- return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100]
- fit: The extrem law to use (gumbel or gev), defaults to "gumbel"
- estimate_method: The methode to use for calibrated the parameters of the extrem law (MLE or MM), defaults to "MLE"
- quantile_duration: The duration of the quantile (hour), defaults to 1
- chunk_size: the size of the chunks in days, defaults to 365
- ncpu: Number of cpu to use, defaults to None
- compute_uncertainties: Compute the uncertainties usung the parametric bootstrap method
- bootstrap_sample: the size of bootstrap sample, default is 100
Returns
A dictionary containing the results od the quantile computation: Results include the quantile, the empirical return period, the maxima for each chunk of chunk_size, the fit parameters of the
fitextrem law.
95 def wrapper(*args, **kwargs): 96 97 bound = sig.bind(*args, **kwargs) 98 bound.apply_defaults() 99 100 for name, value in bound.arguments.items(): 101 if name in annotations: 102 103 target_type = annotations[name] 104 105 args_ = get_args(target_type) 106 107 if target_type is None and len(args_) == 0: 108 args_ = (type(None),) 109 target_type = type(None) 110 111 if not type(value) in args_: 112 113 if len(args_) > 1 and type(None) in args_: 114 115 converted = False 116 for t in args_: 117 118 if t is not type(None): 119 120 if value is not None: 121 try: 122 print( 123 f"</> Warning: Arg '{name}' of type {type(value)} is being" 124 f" converted to {t}" 125 ) 126 bound.arguments[name] = t(value) 127 converted = True 128 except: 129 pass 130 131 if converted: 132 break 133 134 if not converted: 135 raise TypeError( 136 f"</> Error: Arg '{name}' must be a type of " 137 f" {args_}, got {value}" 138 f" ({type(value).__name__})" 139 ) 140 141 else: 142 if not isinstance(value, target_type): 143 try: 144 print( 145 f"</> Warning: Arg '{name}' of type {type(value)} is being" 146 f" converted to {target_type}" 147 ) 148 bound.arguments[name] = target_type(value) 149 except Exception: 150 raise TypeError( 151 f"</> Error: Arg '{name}' must be a type of " 152 f" {target_type.__name__}, got {value}" 153 f" ({type(value).__name__})" 154 ) 155 156 return func(*bound.args, **bound.kwargs)
Proceed to the quantile adjustment (unparrallel computation).
Parameters
- array: an np.ndarray of spatial dicharges values for every time-step
- t_axis: The array axis direction of the time-step, defaults to 2, defaults to 2, defaults to 2
- return_periods: A list of the return period, defaults to [2, 5, 10, 20, 50, 100]
- fit: The extrem law to use (gumbel or gev), defaults to "gumbel"
- estimate_method: The methode to use for calibrated the parameters of the extrem law (MLE or MM), defaults to "MLE"
- quantile_duration: The duration of the quantile (hour), defaults to 1
- chunk_size: the size of the chunks in days, defaults to 365
- compute_uncertainties: Compute the uncertainties usung the parametric bootstrap method
- bootstrap_sample: the size of bootstrap sample, default is 100
Returns
A dictionary containing the results od the quantile computation: Results include the quantile, the empirical return period, the maxima for each chunk of chunk_size, the fit parameters of the
fitextrem law.