docs: def merge(self, cluster_ids, to=None):
          """Merge several clusters to a new cluster.
  
          Parameters
          ----------
  
          cluster_ids : array-like
              List of clusters to merge.
          to : integer or None
              The id of the new cluster. By default, this is `new_cluster_id()`.
  
          Returns
          -------
  
          up : UpdateInfo instance
  
          """
  
          if not _is_array_like(cluster_ids):
              raise ValueError("The first argument should be a list or "
                               "an array.")
  
          cluster_ids = sorted(cluster_ids)
          if not set(cluster_ids) <= set(self.cluster_ids):
              raise ValueError("Some clusters do not exist.")
  
          # Find the new cluster number.
          if to is None:
              to = self.new_cluster_id()
          if to < self.new_cluster_id():
              raise ValueError("The new cluster numbers should be higher than "
                               "{0}.".format(self.new_cluster_id()))
  
          # NOTE: we could have called self.assign() here, but we don't.
          # We circumvent self.assign() for performance reasons.
          # assign() is a relatively costly operation, whereas merging is a much
          # cheaper operation.
  
          # Find all spikes in the specified clusters.
          spike_ids = _spikes_in_clusters(self.spike_clusters, cluster_ids)
  
          up = self._do_merge(spike_ids, cluster_ids, to)
          undo_state = self.emit('request_undo_state', up)
  
          # Add to stack.
          self._undo_stack.add((spike_ids, [to], undo_state))
  
          self.emit('cluster', up)
          return up