docs: def apply_pairwise(self, function, symmetric=True, diagonal=False, block=None, **kwargs):
      """
      Helper function for pairwise apply.
      Args:
          steps: an ordered collection of steps
          function: function to apply, first two positional arguments are steps
          symmetric: whether function is symmetric in the two steps
          diagonal: whether to apply on the diagonal
          block: apply only when the given columns match
          kwargs: keyword arguments to pass to the function
  
      Returns:
          DataFrame with index and columns equal to the steps argument
      """
      steps = self.index
      r = pd.DataFrame(index=steps, columns=steps)
      for i, s1 in enumerate(steps):
          j = range(i+1 if symmetric else len(steps))
          if not diagonal:
              j.remove(i)
          other = set(steps[j])
          if block is not None:
              df = self.reset_index()
              df = df.merge(df, on=block)
              other &= set(df[df.index_x == s1].index_y)
  
          for s2 in other:
              r.ix[s1, s2] = function(s1, s2, **kwargs)
      return r