code: def evaluate_classifier(
      features,
      class_names,
      classifier_name,
      params,
      parameter_mode,
      list_of_ids=None,
      n_exp=-1,
      train_percentage=0.90,
      smote=False,
  ):
      """
      ARGUMENTS:
          features:     a list ([numOfClasses x 1]) whose elements containt
                        np matrices of features. Each matrix features[i] of
                        class i is [n_samples x numOfDimensions]
          class_names:    list of class names (strings)
          classifier_name: svm or knn or randomforest
          params:        list of classifier parameters (for parameter
                         tuning during cross-validation)
          parameter_mode:    0: choose parameters that lead to maximum overall
                               classification ACCURACY
                            1: choose parameters that lead to maximum overall
                            f1 MEASURE
          n_exp:        number of cross-validation experiments
                        (use -1 for auto calculation based on the num of samples)
          train_percentage: percentage of training (vs validation) data
                            default 0.90
  
      RETURNS:
           bestParam:    the value of the input parameter that optimizes the
           selected performance measure
      """
  
      # transcode list of feature matrices to X, y (sklearn)
      X, y = features_to_matrix(features)
  
      # features_norm = features;
      n_classes = len(features)
      ac_all = []
      f1_all = []
      f1_std_all = []
      pre_class_all = []
      rec_classes_all = []
      f1_classes_all = []
      cms_all = []
  
      # dynamically compute total number of samples:
      # (so that if number of samples is >10K only one train-val repetition
      # is performed)
      n_samples_total = X.shape[0]
  
      if n_exp == -1:
          n_exp = int(50000 / n_samples_total) + 1
  
      if list_of_ids:
          train_indeces, test_indeces = [], []
          gss = GroupShuffleSplit(n_splits=n_exp, train_size=0.8)
          for train_index, test_index in gss.split(X, y, list_of_ids):
              train_indeces.append(train_index)
              test_indeces.append(test_index)
  
      for Ci, C in enumerate(params):
          # for each param value
          cm = np.zeros((n_classes, n_classes))
          f1_per_exp = []
          y_pred_all = []
          y_test_all = []
          for e in range(n_exp):
              y_pred = []
              # for each cross-validation iteration:
              print(
                  "Param = {0:.5f} - classifier Evaluation "
                  "Experiment {1:d} of {2:d}".format(C, e + 1, n_exp)
              )
              # split features:
  
              if list_of_ids:
                  X_train, X_test, y_train, y_test = group_split(
                      X, y, train_indeces, test_indeces, e
                  )
              else:
                  X_train, X_test, y_train, y_test = train_test_split(
                      X, y, test_size=1 - train_percentage
                  )
  
              # mean/std scale the features:
              scaler = StandardScaler()
              if smote:
                  sm = SMOTE(random_state=2)
                  # sm = RandomUnderSampler(random_state=0)
                  X_train, y_train = sm.fit_resample(X_train, y_train)
              scaler.fit(X_train)
              X_train = scaler.transform(X_train)
  
              # train multi-class svms:
              if classifier_name == "svm":
                  classifier = train_svm(X_train, y_train, C)
              elif classifier_name == "svm_rbf":
                  classifier = train_svm(
                      X_train, y_train, C, kernel="rbf"
                  )
              elif classifier_name == "knn":
                  classifier = train_knn(X_train, y_train, C)
              elif classifier_name == "randomforest":
                  classifier = train_random_forest(X_train, y_train, C)
              elif classifier_name == "gradientboosting":
                  classifier = train_gradient_boosting(
                      X_train, y_train, C
                  )
              elif classifier_name == "extratrees":
                  classifier = train_extra_trees(X_train, y_train, C)
  
              # get predictions and compute current comfusion matrix
              cmt = np.zeros((n_classes, n_classes))
              X_test = scaler.transform(X_test)
              for i_test_sample in range(X_test.shape[0]):
                  y_pred.append(
                      classifier_wrapper(
                          classifier,
                          classifier_name,
                          X_test[i_test_sample, :],
                      )[0]
                  )
              # current confusion matrices and F1:
              cmt = sklearn.metrics.confusion_matrix(y_test, y_pred)
              f1t = sklearn.metrics.f1_score(
                  y_test, y_pred, average="macro"
              )
              # aggregated predicted and ground truth labels
              # (used for the validation of final F1)
              y_pred_all += y_pred
              y_test_all += y_test.tolist()
  
              f1_per_exp.append(f1t)
              if cmt.size != cm.size:
                  all_classes = set(y)
                  split_classes = set(y_test.tolist() + y_pred)
                  missing_classes = all_classes.difference(
                      split_classes
                  )
                  missing_classes = list(missing_classes)
                  missing_classes = [int(x) for x in missing_classes]
                  for mm in missing_classes:
                      cmt = np.insert(cmt, mm, 0, axis=0)
                  for mm in missing_classes:
                      cmt = np.insert(cmt, mm, 0, axis=1)
              cm = cm + cmt
          cm = cm + 0.0000000010
  
          rec = np.array(
              [
                  cm[ci, ci] / np.sum(cm[ci, :])
                  for ci in range(cm.shape[0])
              ]
          )
          pre = np.array(
              [
                  cm[ci, ci] / np.sum(cm[:, ci])
                  for ci in range(cm.shape[0])
              ]
          )
  
          pre_class_all.append(pre)
          rec_classes_all.append(rec)
  
          f1 = 2 * rec * pre / (rec + pre)
  
          # this is just for debugging (it should be equal to f1)
          f1_b = sklearn.metrics.f1_score(
              y_test_all, y_pred_all, average="macro"
          )
          # Note: np.mean(f1_per_exp) will not be exacty equal to the
          # overall f1 (i.e. f1 and f1_b because these are calculated on a
          # per-sample basis)
          f1_std = np.std(f1_per_exp)
          # print(np.mean(f1), f1_b, f1_std)
  
          f1_classes_all.append(f1)
          ac_all.append(np.sum(np.diagonal(cm)) / np.sum(cm))
  
          cms_all.append(cm)
          f1_all.append(np.mean(f1))
          f1_std_all.append(f1_std)
  
      print("\t\t", end="")
      for i, c in enumerate(class_names):
          if i == len(class_names) - 1:
              print("{0:s}\t\t".format(c), end="")
          else:
              print("{0:s}\t\t\t".format(c), end="")
      print("OVERALL")
      print("\tC", end="")
      for c in class_names:
          print("\tPRE\tREC\tf1", end="")
      print("\t{0:s}\t{1:s}".format("ACC", "f1"))
      best_ac_ind = np.argmax(ac_all)
      best_f1_ind = np.argmax(f1_all)
      for i in range(len(pre_class_all)):
          print("\t{0:.3f}".format(params[i]), end="")
          for c in range(len(pre_class_all[i])):
              print(
                  "\t{0:.1f}\t{1:.1f}\t{2:.1f}".format(
                      100.0 * pre_class_all[i][c],
                      100.0 * rec_classes_all[i][c],
                      100.0 * f1_classes_all[i][c],
                  ),
                  end="",
              )
          print(
              "\t{0:.1f}\t{1:.1f}".format(
                  100.0 * ac_all[i], 100.0 * f1_all[i]
              ),
              end="",
          )
          if i == best_f1_ind:
              print("\t best f1", end="")
          if i == best_ac_ind:
              print("\t best Acc", end="")
          print("")
  
      if parameter_mode == 0:
          # keep parameters that maximize overall classification accuracy:
          print("Confusion Matrix:")
          print_confusion_matrix(cms_all[best_ac_ind], class_names)
          return params[best_ac_ind]
      elif parameter_mode == 1:
          # keep parameters that maximize overall f1 measure:
          print("Confusion Matrix:")
          print_confusion_matrix(cms_all[best_f1_ind], class_names)
          print(f"Best macro f1 {100 * f1_all[best_f1_ind]:.1f}")
          print(
              f"Best macro f1 std {100 * f1_std_all[best_f1_ind]:.1f}"
          )
          return params[best_f1_ind]
  
imports: from __future__ import print_function
  from imblearn.under_sampling import RandomUnderSampler
  from imblearn.over_sampling import SMOTE
  from sklearn.model_selection import GroupShuffleSplit
  from pyAudioAnalysis import audioBasicIO
  from pyAudioAnalysis import MidTermFeatures
  import sys
  import numpy
  import os
  import glob
  import pickle
  import csv
  import ntpath
  from scipy import linalg
  from scipy.spatial import distance
  import sklearn.svm
  import sklearn.decomposition
  import sklearn.ensemble
  import plotly
  import plotly.subplots
  import plotly.graph_objs
  import sklearn.metrics
  from sklearn.model_selection import train_test_split
  from sklearn.preprocessing import StandardScaler
label_desc: Use image node_id 16 for calling a global function with example usage: evaluate_classifier(features, class_names, classifier_name, params, parameter_mode, list_of_ids, n_exp, train_percentage, smote) and returns: params, params