docs: def scatter(self, x, y, xfunc=None, yfunc=None, xscale=None, yscale=None,
                  xlab=None, ylab=None, genes_to_highlight=None,
                  label_genes=False, marginal_histograms=False,
                  general_kwargs=dict(color="k", alpha=0.2, picker=True),
                  general_hist_kwargs=None, offset_kwargs={}, label_kwargs=None,
                  ax=None, one_to_one=None, callback=None, xlab_prefix=None,
                  ylab_prefix=None, sizefunc=None, hist_size=0.3, hist_pad=0.0,
                  nan_offset=0.015, pos_offset=0.99, linelength=0.01,
                  neg_offset=0.005, figure_kwargs=None):
          """
          Do-it-all method for making annotated scatterplots.
  
          Parameters
          ----------
  
          x, y : array-like
              Variables to plot.  Must be names in self.data's DataFrame.  For
              example, "baseMeanA" and "baseMeanB"
  
          xfunc, yfunc : callable
              Functions to apply to `xvar` and `yvar` respectively. Default is
              log2; set to None to have no transformation.
  
          xlab, ylab : string
              Labels for x and y axes; default is to use function names for
              `xfunc` and `yfunc` and variable names `xvar` and `yvar`, e.g.,
              "log2(baseMeanA)"
  
          ax : None or Axes object
              If `ax=None`, then makes a new fig and returns the Axes object,
              otherwise, plots onto `ax`
  
          general_kwargs : dict
              Kwargs for matplotlib.scatter; specifies how all points look
  
          genes_to_highlight : list of (index, dict) tuples
              Provides lots of control to colors.  It is a list of (`ind`,
              `kwargs`) tuples, where each `ind` specifies genes to plot with
              `kwargs`.  Each dictionary updates a copy of `general_kwargs`. If
              `genes_to_highlight` has a "name" kwarg, this must be a list that't
              the same length as `ind`.  It will be used to label the genes in
              `ind` using `label_kwargs`.
  
          callback : callable
              Function to call upon clicking a point. Must accept a single
              argument which is the gene ID. Default is to print the gene name,
              but an example of another useful callback would be a mini-browser
              connected to a genomic_signal object from which the expression data
              were calculated.
  
          one_to_one : None or dict
              If not None, a dictionary of matplotlib.plot kwargs that will be
              used to plot a 1:1 line.
  
          label_kwargs : dict
              Kwargs for labeled genes (e.g., dict=(style='italic')).  Will only
              be used if an entry in `genes_to_highlight` has a `name` key.
  
          offset_kwargs : dict
              Kwargs to be passed to matplotlib.transforms.offset_copy, used for
              adjusting the positioning of gene labels in relation to the actual
              point.
  
          xlab_prefix, ylab_prefix : str
              Optional label prefix that will be added to the beginning of `xlab`
              and/or `ylab`.
  
          hist_size : float
              Size of marginal histograms
  
          hist_pad : float
              Spacing between marginal histograms
  
          nan_offset, pos_offset, neg_offset : float
              Offset, in units of "fraction of axes" for the NaN, +inf, and -inf
              "rug plots"
  
          linelength : float
              Line length for the rug plots
  
          """
          _x = self.data[x]
          _y = self.data[y]
  
          # Construct defaults---------------------------------------------------
          def identity(x):
              return x.copy()
  
          # Axis label setup
          if xlab_prefix is None:
              xlab_prefix = ""
  
          if ylab_prefix is None:
              ylab_prefix = ""
  
          if xlab is None:
              xlab = x
  
              if xfunc is not None:
                  xlab = xlab_prefix + "%s(%s)" % (xfunc.__name__, str(x))
              else:
                  xlab = xlab_prefix + "%s" % (str(x))
  
          if ylab is None:
              ylab = y
              if yfunc is not None:
                  ylab = ylab_prefix + "%s(%s)" % (yfunc.__name__, str(y))
              else:
                  ylab = ylab_prefix + "%s" % (str(y))
  
          if xfunc is None:
              xfunc = identity
  
          if yfunc is None:
              yfunc = identity
  
          if general_kwargs is None:
              general_kwargs = {}
  
          if general_hist_kwargs is None:
              general_hist_kwargs = {}
  
          if genes_to_highlight is None:
              genes_to_highlight = []
  
          if ax is None:
              if figure_kwargs is None:
                  figure_kwargs = {}
              fig = plt.figure(**figure_kwargs)
              ax = fig.add_subplot(111)
  
          if label_kwargs is None:
              label_kwargs = dict(
                  horizontalalignment='right',
                  verticalalignment='center',
                  style='italic',
                  bbox=dict(facecolor='w', edgecolor='None', alpha=0.5)
              )
  
          # Clean data ---------------------------------------------------------
          xi = xfunc(_x)
          yi = yfunc(_y)
  
          # handle inf, -inf, and NaN
          x_is_pos_inf = np.isinf(xi) & (xi > 0)
          x_is_neg_inf = np.isinf(xi) & (xi < 0)
          x_is_nan = np.isnan(xi)
          y_is_pos_inf = np.isinf(yi) & (yi > 0)
          y_is_neg_inf = np.isinf(yi) & (yi < 0)
          y_is_nan = np.isnan(yi)
  
          # Indexes for valid values
          x_valid = ~(x_is_pos_inf | x_is_neg_inf | x_is_nan)
          y_valid = ~(y_is_pos_inf | y_is_neg_inf | y_is_nan)
  
          # global min/max
          gmin = max(xi[x_valid].min(), yi[y_valid].min())
          gmax = min(xi[x_valid].max(), yi[y_valid].max())
  
          # Convert any integer indexes into boolean, and create a new list of
          # genes to highlight.  This handles optional hist kwargs.
  
          # We'll compile a new list of genes to highlight.
          _genes_to_highlight = []
  
          for block in genes_to_highlight:
              ind = block[0]
  
              # Convert to boolean
              if ind.dtype != 'bool':
                  new_ind = (np.zeros_like(xi) == 0)
                  new_ind[ind] = True
                  _genes_to_highlight.append(
                      tuple([new_ind] + list(block[1:]))
                  )
  
              # If it's a DataFrame, we only want the boolean values;
              else:
                  if hasattr(ind, 'values'):
                      ind = ind.values
                  _genes_to_highlight.append(
                      tuple([ind] + list(block[1:]))
                  )
  
          # Now we remove any genes from in allind (which will be plotted using
          # `general_kwargs`) that will be plotted by genes_to_highlight.  This
          # avoids double-plotting.
          allind = np.zeros_like(xi) == 0
          for block in _genes_to_highlight:
              ind = block[0]
              allind[ind] = False
  
          # Copy over the color and alpha if they're not specified
          general_hist_kwargs = plotutils._updatecopy(
              orig=general_hist_kwargs, update_with=general_kwargs,
              keys=['color', 'alpha'])
  
          # Put the non-highlighted genes at the beginning of _genes_to_highlight
          # list so we can just iterate over one list
          _genes_to_highlight.insert(
              0,
              (allind, general_kwargs, general_hist_kwargs)
          )
  
          # Set up the object that will handle the marginal histograms
          self.marginal = plotutils.MarginalHistScatter(
              ax, hist_size=hist_size, pad=hist_pad)
  
          # Set up kwargs for x and y rug plots
          rug_x_kwargs = dict(
              linelength=linelength,
              transform=blended_transform_factory(ax.transData, ax.transAxes),
          )
          rug_y_kwargs = dict(
              linelength=linelength,
              transform=blended_transform_factory(ax.transAxes, ax.transData),
              orientation='vertical',
          )
  
          # EventCollection objects need a color as a 3-tuple, so set up
          # a converter here.
          color_converter = matplotlib.colors.ColorConverter().to_rgb
  
          # Plot the one-to-one line, if kwargs were specified
          if one_to_one:
              ax.plot([gmin, gmax],
                      [gmin, gmax],
                      **one_to_one)
  
          # Plot 'em all, and label if specified
  
          # In order to avoid calling the callback function multiple times when
          # we have overlapping genes to highlight (e.g., a gene that is both
          # upregulated AND has a peak), keep track of everything that's been
          # added so far.
          self._seen = np.ones_like(xi) == 0
          for block in _genes_to_highlight:
              ind = block[0]
              kwargs = block[1]
  
              if len(block) == 3:
                  hist_kwargs = block[2]
              else:
                  hist_kwargs = {}
  
              names = kwargs.pop('names', None)
              _marginal_histograms = (
                  kwargs.pop('marginal_histograms', False) or
                  marginal_histograms)
  
              updated_kwargs = plotutils._updatecopy(
                  orig=kwargs, update_with=general_kwargs)
  
              updated_hist_kwargs = plotutils._updatecopy(
                  orig=hist_kwargs, update_with=general_hist_kwargs)
              updated_hist_kwargs = plotutils._updatecopy(
                  orig=updated_hist_kwargs, update_with=kwargs,
                  keys=['color', 'alpha'], override=True)
  
              xhist_kwargs = updated_kwargs.pop('xhist_kwargs', None)
              yhist_kwargs = updated_kwargs.pop('yhist_kwargs', None)
  
              self.marginal.append(
                  xi[ind & x_valid & y_valid],
                  yi[ind & x_valid & y_valid],
                  scatter_kwargs=dict(**updated_kwargs),
                  hist_kwargs=updated_hist_kwargs,
                  xhist_kwargs=xhist_kwargs,
                  yhist_kwargs=yhist_kwargs,
                  marginal_histograms=_marginal_histograms,
              )
  
              # This is important for callbacks: here we grab the last-created
              # collection,
              coll = self.marginal.scatter_ax.collections[-1]
              coll.df = self.data
              coll.ind = ind & x_valid & y_valid
  
              color = color_converter(updated_kwargs['color'])
              rug_x_kwargs['color'] = color
              rug_y_kwargs['color'] = color
  
              # Note: if both x and y are not valid, then they will not be on the
              # plot.
              items = [
                  # top rug, y is +inf and x is valid
                  (xi, ind & x_valid & y_is_pos_inf, pos_offset, rug_x_kwargs),
  
                  # one of the bottom rugs, where y is NaN
                  (xi, ind & x_valid & y_is_nan, nan_offset, rug_x_kwargs),
  
                  # bottom rug, y is -inf
                  (xi, ind & x_valid & y_is_neg_inf, neg_offset, rug_x_kwargs),
  
                  # right rug, x is +inf
                  (yi, ind & y_valid & x_is_pos_inf, pos_offset, rug_y_kwargs),
  
                  # one of the left rugs; x is NaN
                  (yi, ind & y_valid & x_is_nan, nan_offset, rug_y_kwargs),
  
                  # left rug, x is -inf
                  (yi, ind & y_valid & x_is_neg_inf, neg_offset, rug_y_kwargs),
              ]
              for values, index, offset, kwargs in items:
                  coll = EventCollection(
                      values[index], lineoffset=offset, **kwargs)
                  coll.df = self.data
                  coll.ind = index
                  ax.add_collection(coll)
  
              if names:
                  transOffset = matplotlib.transforms.offset_copy(
                      ax.transData, fig=ax.figure, **offset_kwargs)
  
                  for xii, yii, name in zip(xi[ind], yi[ind], names):
                      ax.text(xii,
                              yii,
                              name,
                              transform=transOffset,
                              **label_kwargs)
  
          # register callback
          if callback is None:
              callback = self._default_callback
  
          def wrapped_callback(event):
              for _id in self._id_callback(event):
                  callback(_id)
  
          ax.figure.canvas.mpl_connect('pick_event', wrapped_callback)
  
          ax.set_xlabel(xlab)
          ax.set_ylabel(ylab)
          ax.axis('tight')
  
          return ax