code: def _grid_search_double(grid_scores, change, subset, cmap, ax, sort):
      # check that the two different parameters were passed
      if len(set(change)) == 1:
          raise ValueError("You need to pass two different parameters")
  
      # if a value in subset was passed, use it to filter the groups
      if subset is not None:
          groups = _group_by(
              grid_scores, _get_params_value(subset.keys())
          )
          keys = _mapping_to_tuple_pairs(subset)
          groups = {
              k: v
              for k, v in _sorted_map_iter(groups, sort)
              if k in keys
          }
          grid_scores = _flatten_list(groups.values())
          if not groups:
              raise ValueError(
                  (
                      "Your subset didn't match any data"
                      " verify that the values are correct."
                  )
              )
  
      # group by every possible combination in change
      matrix_elements = _group_by(
          grid_scores, _get_params_value(change)
      )
  
      for k, v in matrix_elements.items():
          if len(v) > 1:
              raise ValueError(
                  (
                      "More than one result matched your criteria."
                      " Make sure you specify parameters using change"
                      " and subset so only one group matches."
                  )
              )
  
      # on each group there must be only one element, get it
      matrix_elements = {k: v[0] for k, v in matrix_elements.items()}
  
      # get the unique values for each element
      # and sort the results to make sure the matrix axis
      # is showed in increasing order
      row_names = sorted(
          set([t[0] for t in matrix_elements.keys()]),
          key=lambda x: (x[1] is None, x[1]),
      )
      col_names = sorted(
          set([t[1] for t in matrix_elements.keys()]),
          key=lambda x: (x[1] is None, x[1]),
      )
  
      # size of the matrix
      cols = len(col_names)
      rows = len(row_names)
  
      # map values to coordinates to populate the marix
      x_coord = {k: v for k, v in zip(col_names, range(cols))}
      y_coord = {k: v for k, v in zip(row_names, range(rows))}
  
      # replace keys in matrix_elements with their corresponding indexes
      # improve variable naming
      m = {
          (x_coord[k[1]], y_coord[k[0]]): v
          for k, v in matrix_elements.items()
      }
  
      matrix = np.zeros((rows, cols))
  
      for (j, i), v in m.items():
          matrix[i][j] = v.mean_validation_score
  
      # ticks for the axis
      row_labels = ["{}={}".format(*x) for x in row_names]
      col_labels = ["{}={}".format(*y) for y in col_names]
  
      im = ax.imshow(matrix, interpolation="nearest", cmap=cmap)
  
      # set text on cells
      for (x, y), v in m.items():
          label = "{:.3}".format(v.mean_validation_score)
          ax.text(
              x,
              y,
              label,
              horizontalalignment="center",
              verticalalignment="center",
          )
  
      ax.set_xticks(range(cols))
      ax.set_xticklabels(col_labels, rotation=45)
      ax.set_yticks(range(rows))
      ax.set_yticklabels(row_labels)
      plt.colorbar(im, ax=ax)
      ax.get_figure().tight_layout()
      return ax
  
file_path: sklearn-evaluation/src/sklearn_evaluation/plot/grid_search.py
imports: import collections
  import matplotlib.pyplot
  import numpy
  from six import string_types
  from ..telemetry import SKLearnEvaluationLogger
  from sklearn_evaluation.plot.matplotlib.bar import BarShifter
  from ploomber_core.exceptions import modify_exceptions
  from ..util import _group_by, _get_params_value, _mapping_to_tuple_pairs, default_heatmap, _sorted_map_iter, _flatten_list
label_desc: Use image node_id 3 for calling a global function with example usage: _grid_search_double(grid_scores, change, subset, cmap, ax, sort) and returns: ax