docs: def dapply(self, fn, pairwise=False, symmetric=True, diagonal=False, block=None, **kwargs):
      """
      Apply function to each step object in the index
  
      Args:
          fn: function to apply. If a list then each function is applied
          pairwise: whether to apply the function to pairs of steps
          symmetric, diagonal, block: passed to apply_pairwise when pairwise=True
          kwargs: a keyword arguments to pass to each function. Arguments
              with list value are grid searched using util.dict_product.
  
      Returns: a StepFrame or StepSeries
      """
      search_keys = [k for k, v in kwargs.items() if isinstance(v, list) and len(v) > 1]
      functions = util.make_list(fn)
      search = list(product(functions, util.dict_product(kwargs)))
  
      results = []
      for fn, kw in search:
          if not pairwise:
              r = self.index.to_series().apply(lambda step: fn(step, **kw))
          else:
              r = apply_pairwise(self, fn,
                                 symmetric=symmetric, diagonal=diagonal, block=block,
                                 **kw)
  
          name = [] if len(functions) == 1 else [fn.__name__]
          name += util.dict_subset(kw, search_keys).values()
  
          if isinstance(r, pd.DataFrame):
              columns = pd.MultiIndex.from_tuples(
                      [tuple(name + util.make_list(c)) for c in r.columns])
              r.columns = columns
          else:
              r.name = tuple(name)
          results.append(r)
  
      if len(results) > 1:
          result = pd.concat(results, axis=1)
          # get subset of parameters that were searched over
          column_names = [] if len(functions) == 1 else [None]
          column_names += search_keys
          column_names += [None]*(len(result.columns.names)-len(column_names))
          result.columns.names = column_names
  
          return StepFrame(result)
      else:
          result = results[0]
          if isinstance(result, pd.DataFrame):
              return StepFrame(result)
          else:
              result.name = functions[0].__name__
              return StepSeries(result)