docstr: Draw the plot onto an axes, passing matplotlib kwargs.
docstr_tok: ["Draw","the","plot","onto","an","axes",",","passing","matplotlib","kwargs","."]
code: def plot(self, ax, kws):
      """Draw the plot onto an axes, passing matplotlib kwargs."""
  
      # Draw a test plot, using the passed in kwargs. The goal here is to
      # honor both (a) the current state of the plot cycler and (b) the
      # specified kwargs on all the lines we will draw, overriding when
      # relevant with the data semantics. Note that we won't cycle
      # internally; in other words, if `hue` is not used, all elements will
      # have the same color, but they will have the color that you would have
      # gotten from the corresponding matplotlib function, and calling the
      # function will advance the axes property cycle.
  
      kws = normalize_kwargs(kws, mpl.lines.Line2D)
      kws.setdefault("markeredgewidth", 0.75)
      kws.setdefault("markeredgecolor", "w")
  
      # Set default error kwargs
      err_kws = self.err_kws.copy()
      if self.err_style == "band":
          err_kws.setdefault("alpha", 0.2)
      elif self.err_style == "bars":
          pass
      elif self.err_style is not None:
          err = "`err_style` must be 'band' or 'bars', not {}"
          raise ValueError(err.format(self.err_style))
  
      # Initialize the aggregation object
      weighted = "weight" in self.plot_data
      agg = (WeightedAggregator if weighted else EstimateAggregator)(
          self.estimator,
          self.errorbar,
          n_boot=self.n_boot,
          seed=self.seed,
      )
  
      # TODO abstract variable to aggregate over here-ish. Better name?
      orient = self.orient
      if orient not in {"x", "y"}:
          err = f"`orient` must be either 'x' or 'y', not {orient!r}."
          raise ValueError(err)
      other = {"x": "y", "y": "x"}[orient]
  
      # TODO How to handle NA? We don't want NA to propagate through to the
      # estimate/CI when some values are present, but we would also like
      # matplotlib to show "gaps" in the line when all values are missing.
      # This is straightforward absent aggregation, but complicated with it.
      # If we want to use nas, we need to conditionalize dropna in iter_data.
  
      # Loop over the semantic subsets and add to the plot
      grouping_vars = "hue", "size", "style"
      for sub_vars, sub_data in self.iter_data(
          grouping_vars, from_comp_data=True
      ):
          if self.sort:
              sort_vars = ["units", orient, other]
              sort_cols = [
                  var for var in sort_vars if var in self.variables
              ]
              sub_data = sub_data.sort_values(sort_cols)
  
          if (
              self.estimator is not None
              and sub_data[orient].value_counts().max() > 1
          ):
              if "units" in self.variables:
                  # TODO eventually relax this constraint
                  err = "estimator must be None when specifying units"
                  raise ValueError(err)
              grouped = sub_data.groupby(orient, sort=self.sort)
              # Could pass as_index=False instead of reset_index,
              # but that fails on a corner case with older pandas.
              sub_data = grouped.apply(agg, other).reset_index()
          else:
              sub_data[f"{other}min"] = np.nan
              sub_data[f"{other}max"] = np.nan
  
          # Apply inverse axis scaling
          for var in "xy":
              _, inv = _get_transform_functions(ax, var)
              for col in sub_data.filter(regex=f"^{var}"):
                  sub_data[col] = inv(sub_data[col])
  
          # --- Draw the main line(s)
  
          if (
              "units" in self.variables
          ):  # XXX why not add to grouping variables?
              lines = []
              for _, unit_data in sub_data.groupby("units"):
                  lines.extend(
                      ax.plot(unit_data["x"], unit_data["y"], **kws)
                  )
          else:
              lines = ax.plot(sub_data["x"], sub_data["y"], **kws)
  
          for line in lines:
              if "hue" in sub_vars:
                  line.set_color(self._hue_map(sub_vars["hue"]))
  
              if "size" in sub_vars:
                  line.set_linewidth(self._size_map(sub_vars["size"]))
  
              if "style" in sub_vars:
                  attributes = self._style_map(sub_vars["style"])
                  if "dashes" in attributes:
                      line.set_dashes(attributes["dashes"])
                  if "marker" in attributes:
                      line.set_marker(attributes["marker"])
  
          line_color = line.get_color()
          line_alpha = line.get_alpha()
          line_capstyle = line.get_solid_capstyle()
  
          # --- Draw the confidence intervals
  
          if self.estimator is not None and self.errorbar is not None:
              # TODO handling of orientation will need to happen here
  
              if self.err_style == "band":
                  func = {"x": ax.fill_between, "y": ax.fill_betweenx}[
                      orient
                  ]
                  func(
                      sub_data[orient],
                      sub_data[f"{other}min"],
                      sub_data[f"{other}max"],
                      color=line_color,
                      **err_kws,
                  )
  
              elif self.err_style == "bars":
                  error_param = {
                      f"{other}err": (
                          sub_data[other] - sub_data[f"{other}min"],
                          sub_data[f"{other}max"] - sub_data[other],
                      )
                  }
                  ebars = ax.errorbar(
                      sub_data["x"],
                      sub_data["y"],
                      **error_param,
                      linestyle="",
                      color=line_color,
                      alpha=line_alpha,
                      **err_kws,
                  )
  
                  # Set the capstyle properly on the error bars
                  for obj in ebars.get_children():
                      if isinstance(
                          obj, mpl.collections.LineCollection
                      ):
                          obj.set_capstyle(line_capstyle)
  
      # Finalize the axes details
      self._add_axis_labels(ax)
      if self.legend:
          legend_artist = partial(mpl.lines.Line2D, xdata=[], ydata=[])
          attrs = {"hue": "color", "size": "linewidth", "style": None}
          self.add_legend_data(ax, legend_artist, kws, attrs)
          handles, _ = ax.get_legend_handles_labels()
          if handles:
              legend = ax.legend(title=self.legend_title)
              adjust_legend_subtitles(legend)
  
imports: from functools import partial
  import warnings
  import numpy
  import pandas
  import matplotlib
  import matplotlib.pyplot
  from matplotlib.cbook import normalize_kwargs
  from ._base import VectorPlotter
  from .utils import adjust_legend_subtitles, _default_color, _deprecate_ci, _get_transform_functions, _scatter_legend_artist
  from ._statistics import EstimateAggregator, WeightedAggregator
  from .axisgrid import FacetGrid, _facet_docs
  from ._docstrings import DocstringComponents, _core_docs
label_desc: Use image node_id 2 for calling the _LinePlotter obj's underlying member method code with example usage: obj.plot(ax, kws) without return types