def datasets_models_validation(dataset_list, model_paramGrid_list, scale_list=None, test_size=0.2, time_series=False, random_state=123, n_folds=5, regr=True, plot=False, plot_train=False, xvalues=None, xlabel='Datasets', title='Datasets validation', figsize=(6, 6), verbose=False, figsize_verbose=(6, 6)):
    "\n    Select the best dataset and the best model (i.e. select the best couple dataset-model).\n\n    For each dataset in `dataset_list`, all the models in `model_paramGrid_list` are tested: each model is tested performing\n    an exhaustive tuning of the specified hyperparameters. In fact, `model_paramGrid_list` also contains, for each model, the\n    grid of the hyperparameters that have to be tested on that model (i.e. the grid which contains the values to test for\n    each specified hyperparameter of the model).\n    In other words, on each dataset the selection of the best model is performed: in fact, on each dataset, the function\n    `models_validation` is applied. (See `models_validation`).\n    In the end, the best couple dataset-model is selected.\n\n    Despite the fact that a couple dataset-model is selected, the main viewpoint is focused with respect to the datasets.\n    It's a validation focused on the datasets.\n    In fact, first of all, for each dataset the model selection is performed: in this way the best model is selected\n    and its relative score is associated with the dataset (i.e. it's the score of the dataset). (In other words, on each\n    dataset the function `models_validation` is applied). Finally, after that, the best dataset is selected.\n    It's a two-levels selection.\n\n    This selection is made using the validation score (i.e. the best couple dataset-model is the one with best validation\n    score).\n    The validation score is computed by splitting each dataset into the training-test sets and then by applying the cross\n    validation on the training set.\n    Additionally, the training and test scores are also computed.\n\n    Optionally, the validation scores of the datasets can be plotted, making a graphical visualization of the dataset\n    selection. This is the 'main' plot.\n    Moreover, still optionally, the 'secondary' plots can be done: for each dataset, the validation scores of the models are\n    plotted, making a graphical visualization of the models selection on that dataset. (As the plot made by the\n    `models_validation` function).\n\n    Parameters\n    ----------\n    dataset_list: list\n        List of couples, where each couple is a dataset.\n            - The first element is X, the two-dimensional np.array containing the explanatory features of the dataset.\n            - The second element is y, the mono dimensional np.array containing the response feature of the dataset.\n    model_paramGrid_list: list\n        List that specifies the models and the relative grid of hyperparameters to be tested.\n        It's a list of triples (i.e. tuples), where each triple represents a model:\n            - the first element is a string, which is a mnemonic name of that model;\n            - the second element is the sklearn model;\n            - the third element is the grid of hyperparameters to test for that model. It's a dictionary, with the same\n              structure of parameter `param_grid` of the function `hyperparameters_validation`.\n    scale_list: list or bool\n        List of booleans, which has as many elements as the number of models to test (i.e. number of elements in the\n        `model_paramGrid_list` list).\n        This list indicates, for each different model, if the features in 'X' have to be scaled or not (for all the datasets).\n        `scale_list` can be None or False: in this case the 'X' features aren't scaled for any model. `scale_list` can be\n        True: in this case the 'X' features are scaled for all the models.\n    test_size: float\n        Decimal number between 0 and 1, which indicates the proportion of the test set (for each dataset).\n    time_series: bool\n        Indicates if the given datasets are time series dataset (i.e. datasets indexed by days).\n        (This affects the computing of the validation score).\n    random_state: int\n        Used in the training-test splitting of the datasets.\n    n_folds: int\n        Indicates how many folds are made in order to compute the k-fold cross validation.\n        (It's used only if `time_series` is False).\n    regr: bool\n        Indicates if it's either a regression or a classification problem.\n    plot: bool\n        Indicates whether to plot or not the validation score values of the datasets (i.e. this is the 'main' plot).\n    plot_train: bool\n        Indicates whether to plot also the training scores (both in the 'main' and 'secondary' plots).\n    xvalues: list (in general, iterable)\n        Values that have to be put in the x axis of the 'main' plot.\n    xlabel: str\n        Label of the x axis of the 'main' plot.\n    title: str\n        Title of the 'main' plot.\n    figsize: tuple\n        Two dimensions of the 'main' plot.\n    verbose: bool\n        If True, for each dataset the validation scores of the models are plotted (i.e. these are the 'secondary' plots).\n        (See 'models_validation').\n    figsize_verbose: tuple\n        Two dimensions of the 'secondary' plots.\n\n    Returns\n    ----------\n    datasets_train_val_score: np.array\n        Two dimensional np.array, containing two columns: the first contains the training scores, the second the validation\n        scores.\n        It has as many rows as the number of datasets to test, i.e. as the number of elements in `dataset_list`.\n    datasets_best_model: list\n        List which has as many elements as the number of the datasets (i.e. number of elements in `dataset_list`). For\n        each dataset, it contains the best model for that dataset.\n        More precisely, it is a list of triple:\n            - the first element is the index of `model_paramGrid_list` which indicates the best model;\n            - the second element is the mnemonic name of the best model;\n            - the third element is the best combination of hyperparameters values on that best model (i.e. it's a dictionary\n              which has as keys the hyperparameters names and as values their associated values).\n    best_index: int\n        Index of `dataset_list` that indicates which is the best dataset.\n    test_score: float\n        Test score associated with the best couple dataset-model.\n    axes: list\n        List of the matplotlib Axes where the plots have been made.\n        Firstly, the 'secondary' plots are put (if any). And, as last, the 'main' plot is put (if any).\n        If no plot has been made, `axes` is an empty list.\n\n    See also\n    ----------\n    models_validation: select the best model on the given dataset.\n\n    Notes\n    ----------\n    - If `regr` is True, the validation scores are errors (MSE, i.e. Mean Squared Errors): this means that the best\n      couple dataset-model is the one associated with the minimum validation score.\n      Otherwise, the validation scores are accuracies: this means that the best couple is the one associated with the\n      maximum validation score.\n    - If `time_series` is False, the training-test splitting of each dataset is made randomly. In addition, the cross\n      validation strategy performed is the classic k-fold cross validation: the number of folds is specified by `n_folds`.\n      Otherwise, if `time_series` is True, the training-test sets are simply obtained by splitting each dataset into two\n      contiguous parts. In addition, the cross validation strategy performed is the sklearn TimeSeriesSplit.\n    "
    datasets_train_val_score = []
    datasets_best_model = []
    datasets_test_score = []
    axes = []
    for (i, dataset) in enumerate(dataset_list):
        (X, y) = dataset
        (models_train_val_score, models_best_params, best_index, test_score, ax) = models_validation(X, y, model_paramGrid_list, scale_list=scale_list, test_size=test_size, time_series=time_series, random_state=random_state, n_folds=n_folds, regr=regr, plot=verbose, plot_train=plot_train, xlabel='Models', title=(('Dataset ' + str(i)) + ' : models validation'), figsize=figsize_verbose)
        datasets_train_val_score.append(tuple(models_train_val_score[best_index, :]))
        datasets_best_model.append((best_index, model_paramGrid_list[best_index][0], models_best_params[best_index]))
        datasets_test_score.append(test_score)
        if ax:
            axes.append(ax)
    datasets_train_val_score = np.array(datasets_train_val_score)
    if regr:
        best_index = np.argmin(datasets_train_val_score, axis=0)[1]
    else:
        best_index = np.argmax(datasets_train_val_score, axis=0)[1]
    test_score = datasets_test_score[best_index]
    if plot:
        if (not xvalues):
            xvalues = range(len(dataset_list))
        ax = _plot_TrainVal_values(xvalues, datasets_train_val_score, plot_train, xlabel, title, figsize, bar=True)
        axes.append(ax)
    return (datasets_train_val_score, datasets_best_model, best_index, test_score, axes)
Select the best dataset and the best model (i.e. select the best couple dataset-model).

For each dataset in `dataset_list`, all the models in `model_paramGrid_list` are tested: each model is tested performing
an exhaustive tuning of the specified hyperparameters. In fact, `model_paramGrid_list` also contains, for each model, the
grid of the hyperparameters that have to be tested on that model (i.e. the grid which contains the values to test for
each specified hyperparameter of the model).
In other words, on each dataset the selection of the best model is performed: in fact, on each dataset, the function
`models_validation` is applied. (See `models_validation`).
In the end, the best couple dataset-model is selected.

Despite the fact that a couple dataset-model is selected, the main viewpoint is focused with respect to the datasets.
It's a validation focused on the datasets.
In fact, first of all, for each dataset the model selection is performed: in this way the best model is selected
and its relative score is associated with the dataset (i.e. it's the score of the dataset). (In other words, on each
dataset the function `models_validation` is applied). Finally, after that, the best dataset is selected.
It's a two-levels selection.

This selection is made using the validation score (i.e. the best couple dataset-model is the one with best validation
score).
The validation score is computed by splitting each dataset into the training-test sets and then by applying the cross
validation on the training set.
Additionally, the training and test scores are also computed.

Optionally, the validation scores of the datasets can be plotted, making a graphical visualization of the dataset
selection. This is the 'main' plot.
Moreover, still optionally, the 'secondary' plots can be done: for each dataset, the validation scores of the models are
plotted, making a graphical visualization of the models selection on that dataset. (As the plot made by the
`models_validation` function).

Parameters
----------
dataset_list: list
    List of couples, where each couple is a dataset.
        - The first element is X, the two-dimensional np.array containing the explanatory features of the dataset.
        - The second element is y, the mono dimensional np.array containing the response feature of the dataset.
model_paramGrid_list: list
    List that specifies the models and the relative grid of hyperparameters to be tested.
    It's a list of triples (i.e. tuples), where each triple represents a model:
        - the first element is a string, which is a mnemonic name of that model;
        - the second element is the sklearn model;
        - the third element is the grid of hyperparameters to test for that model. It's a dictionary, with the same
          structure of parameter `param_grid` of the function `hyperparameters_validation`.
scale_list: list or bool
    List of booleans, which has as many elements as the number of models to test (i.e. number of elements in the
    `model_paramGrid_list` list).
    This list indicates, for each different model, if the features in 'X' have to be scaled or not (for all the datasets).
    `scale_list` can be None or False: in this case the 'X' features aren't scaled for any model. `scale_list` can be
    True: in this case the 'X' features are scaled for all the models.
test_size: float
    Decimal number between 0 and 1, which indicates the proportion of the test set (for each dataset).
time_series: bool
    Indicates if the given datasets are time series dataset (i.e. datasets indexed by days).
    (This affects the computing of the validation score).
random_state: int
    Used in the training-test splitting of the datasets.
n_folds: int
    Indicates how many folds are made in order to compute the k-fold cross validation.
    (It's used only if `time_series` is False).
regr: bool
    Indicates if it's either a regression or a classification problem.
plot: bool
    Indicates whether to plot or not the validation score values of the datasets (i.e. this is the 'main' plot).
plot_train: bool
    Indicates whether to plot also the training scores (both in the 'main' and 'secondary' plots).
xvalues: list (in general, iterable)
    Values that have to be put in the x axis of the 'main' plot.
xlabel: str
    Label of the x axis of the 'main' plot.
title: str
    Title of the 'main' plot.
figsize: tuple
    Two dimensions of the 'main' plot.
verbose: bool
    If True, for each dataset the validation scores of the models are plotted (i.e. these are the 'secondary' plots).
    (See 'models_validation').
figsize_verbose: tuple
    Two dimensions of the 'secondary' plots.

Returns
----------
datasets_train_val_score: np.array
    Two dimensional np.array, containing two columns: the first contains the training scores, the second the validation
    scores.
    It has as many rows as the number of datasets to test, i.e. as the number of elements in `dataset_list`.
datasets_best_model: list
    List which has as many elements as the number of the datasets (i.e. number of elements in `dataset_list`). For
    each dataset, it contains the best model for that dataset.
    More precisely, it is a list of triple:
        - the first element is the index of `model_paramGrid_list` which indicates the best model;
        - the second element is the mnemonic name of the best model;
        - the third element is the best combination of hyperparameters values on that best model (i.e. it's a dictionary
          which has as keys the hyperparameters names and as values their associated values).
best_index: int
    Index of `dataset_list` that indicates which is the best dataset.
test_score: float
    Test score associated with the best couple dataset-model.
axes: list
    List of the matplotlib Axes where the plots have been made.
    Firstly, the 'secondary' plots are put (if any). And, as last, the 'main' plot is put (if any).
    If no plot has been made, `axes` is an empty list.

See also
----------
models_validation: select the best model on the given dataset.

Notes
----------
- If `regr` is True, the validation scores are errors (MSE, i.e. Mean Squared Errors): this means that the best
  couple dataset-model is the one associated with the minimum validation score.
  Otherwise, the validation scores are accuracies: this means that the best couple is the one associated with the
  maximum validation score.
- If `time_series` is False, the training-test splitting of each dataset is made randomly. In addition, the cross
  validation strategy performed is the classic k-fold cross validation: the number of folds is specified by `n_folds`.
  Otherwise, if `time_series` is True, the training-test sets are simply obtained by splitting each dataset into two
  contiguous parts. In addition, the cross validation strategy performed is the sklearn TimeSeriesSplit.
def datasets_models_validation(dataset_list, model_paramGrid_list, scale_list=None, test_size=0.2, time_series=False, random_state=123, n_folds=5, regr=True, plot=False, plot_train=False, xvalues=None, xlabel='Datasets', title='Datasets validation', figsize=(6, 6), verbose=False, figsize_verbose=(6, 6)):
    "\n    Select the best dataset and the best model (i.e. select the best couple dataset-model).\n\n    For each dataset in `dataset_list`, all the models in `model_paramGrid_list` are tested: each model is tested performing\n    an exhaustive tuning of the specified hyperparameters. In fact, `model_paramGrid_list` also contains, for each model, the\n    grid of the hyperparameters that have to be tested on that model (i.e. the grid which contains the values to test for\n    each specified hyperparameter of the model).\n    In other words, on each dataset the selection of the best model is performed: in fact, on each dataset, the function\n    `models_validation` is applied. (See `models_validation`).\n    In the end, the best couple dataset-model is selected.\n\n    Despite the fact that a couple dataset-model is selected, the main viewpoint is focused with respect to the datasets.\n    It's a validation focused on the datasets.\n    In fact, first of all, for each dataset the model selection is performed: in this way the best model is selected\n    and its relative score is associated with the dataset (i.e. it's the score of the dataset). (In other words, on each\n    dataset the function `models_validation` is applied). Finally, after that, the best dataset is selected.\n    It's a two-levels selection.\n\n    This selection is made using the validation score (i.e. the best couple dataset-model is the one with best validation\n    score).\n    The validation score is computed by splitting each dataset into the training-test sets and then by applying the cross\n    validation on the training set.\n    Additionally, the training and test scores are also computed.\n\n    Optionally, the validation scores of the datasets can be plotted, making a graphical visualization of the dataset\n    selection. This is the 'main' plot.\n    Moreover, still optionally, the 'secondary' plots can be done: for each dataset, the validation scores of the models are\n    plotted, making a graphical visualization of the models selection on that dataset. (As the plot made by the\n    `models_validation` function).\n\n    Parameters\n    ----------\n    dataset_list: list\n        List of couples, where each couple is a dataset.\n            - The first element is X, the two-dimensional np.array containing the explanatory features of the dataset.\n            - The second element is y, the mono dimensional np.array containing the response feature of the dataset.\n    model_paramGrid_list: list\n        List that specifies the models and the relative grid of hyperparameters to be tested.\n        It's a list of triples (i.e. tuples), where each triple represents a model:\n            - the first element is a string, which is a mnemonic name of that model;\n            - the second element is the sklearn model;\n            - the third element is the grid of hyperparameters to test for that model. It's a dictionary, with the same\n              structure of parameter `param_grid` of the function `hyperparameters_validation`.\n    scale_list: list or bool\n        List of booleans, which has as many elements as the number of models to test (i.e. number of elements in the\n        `model_paramGrid_list` list).\n        This list indicates, for each different model, if the features in 'X' have to be scaled or not (for all the datasets).\n        `scale_list` can be None or False: in this case the 'X' features aren't scaled for any model. `scale_list` can be\n        True: in this case the 'X' features are scaled for all the models.\n    test_size: float\n        Decimal number between 0 and 1, which indicates the proportion of the test set (for each dataset).\n    time_series: bool\n        Indicates if the given datasets are time series dataset (i.e. datasets indexed by days).\n        (This affects the computing of the validation score).\n    random_state: int\n        Used in the training-test splitting of the datasets.\n    n_folds: int\n        Indicates how many folds are made in order to compute the k-fold cross validation.\n        (It's used only if `time_series` is False).\n    regr: bool\n        Indicates if it's either a regression or a classification problem.\n    plot: bool\n        Indicates whether to plot or not the validation score values of the datasets (i.e. this is the 'main' plot).\n    plot_train: bool\n        Indicates whether to plot also the training scores (both in the 'main' and 'secondary' plots).\n    xvalues: list (in general, iterable)\n        Values that have to be put in the x axis of the 'main' plot.\n    xlabel: str\n        Label of the x axis of the 'main' plot.\n    title: str\n        Title of the 'main' plot.\n    figsize: tuple\n        Two dimensions of the 'main' plot.\n    verbose: bool\n        If True, for each dataset the validation scores of the models are plotted (i.e. these are the 'secondary' plots).\n        (See 'models_validation').\n    figsize_verbose: tuple\n        Two dimensions of the 'secondary' plots.\n\n    Returns\n    ----------\n    datasets_train_val_score: np.array\n        Two dimensional np.array, containing two columns: the first contains the training scores, the second the validation\n        scores.\n        It has as many rows as the number of datasets to test, i.e. as the number of elements in `dataset_list`.\n    datasets_best_model: list\n        List which has as many elements as the number of the datasets (i.e. number of elements in `dataset_list`). For\n        each dataset, it contains the best model for that dataset.\n        More precisely, it is a list of triple:\n            - the first element is the index of `model_paramGrid_list` which indicates the best model;\n            - the second element is the mnemonic name of the best model;\n            - the third element is the best combination of hyperparameters values on that best model (i.e. it's a dictionary\n              which has as keys the hyperparameters names and as values their associated values).\n    best_index: int\n        Index of `dataset_list` that indicates which is the best dataset.\n    test_score: float\n        Test score associated with the best couple dataset-model.\n    axes: list\n        List of the matplotlib Axes where the plots have been made.\n        Firstly, the 'secondary' plots are put (if any). And, as last, the 'main' plot is put (if any).\n        If no plot has been made, `axes` is an empty list.\n\n    See also\n    ----------\n    models_validation: select the best model on the given dataset.\n\n    Notes\n    ----------\n    - If `regr` is True, the validation scores are errors (MSE, i.e. Mean Squared Errors): this means that the best\n      couple dataset-model is the one associated with the minimum validation score.\n      Otherwise, the validation scores are accuracies: this means that the best couple is the one associated with the\n      maximum validation score.\n    - If `time_series` is False, the training-test splitting of each dataset is made randomly. In addition, the cross\n      validation strategy performed is the classic k-fold cross validation: the number of folds is specified by `n_folds`.\n      Otherwise, if `time_series` is True, the training-test sets are simply obtained by splitting each dataset into two\n      contiguous parts. In addition, the cross validation strategy performed is the sklearn TimeSeriesSplit.\n    "
    datasets_train_val_score = []
    datasets_best_model = []
    datasets_test_score = []
    axes = []
    for (i, dataset) in enumerate(dataset_list):
        (X, y) = dataset
        (models_train_val_score, models_best_params, best_index, test_score, ax) = models_validation(X, y, model_paramGrid_list, scale_list=scale_list, test_size=test_size, time_series=time_series, random_state=random_state, n_folds=n_folds, regr=regr, plot=verbose, plot_train=plot_train, xlabel='Models', title=(('Dataset ' + str(i)) + ' : models validation'), figsize=figsize_verbose)
        datasets_train_val_score.append(tuple(models_train_val_score[best_index, :]))
        datasets_best_model.append((best_index, model_paramGrid_list[best_index][0], models_best_params[best_index]))
        datasets_test_score.append(test_score)
        if ax:
            axes.append(ax)
    datasets_train_val_score = np.array(datasets_train_val_score)
    if regr:
        best_index = np.argmin(datasets_train_val_score, axis=0)[1]
    else:
        best_index = np.argmax(datasets_train_val_score, axis=0)[1]
    test_score = datasets_test_score[best_index]
    if plot:
        if (not xvalues):
            xvalues = range(len(dataset_list))
        ax = _plot_TrainVal_values(xvalues, datasets_train_val_score, plot_train, xlabel, title, figsize, bar=True)
        axes.append(ax)
    return (datasets_train_val_score, datasets_best_model, best_index, test_score, axes)