class_docstr: Creates a xp result plot out of the given dataframe: regret with respect to budget for
  each optimizer after averaging on all experiments (it is good practice to use a df
  which is filtered out for one set of input parameters)
  
  Parameters
  ----------
  optim_vals: dict
      output of the make_data static method, containing all information necessary for plotting
  title: str
      title of the plot
  name_style: dict
      a dict or dict-like object providing a line style for each optimizer name.
      (can be helpful for consistency across plots)
code: def __init__(
      self,
      optim_vals: tp.Dict[str, tp.Dict[str, np.ndarray]],
      title: str,
      name_style: tp.Optional[tp.Dict[str, tp.Any]] = None,
      xaxis: str = "budget",
      pure_only: bool = False,
  ) -> None:
      if name_style is None:
          name_style = NameStyle()
      upperbound = max(
          np.max(vals["loss"])
          for vals in optim_vals.values()
          if np.max(vals["loss"]) < np.inf
      )
      for optim, vals in optim_vals.items():
          if optim.lower() in ["stupid", "idiot"] or optim in [
              "Zero",
              "StupidRandom",
          ]:
              upperbound = min(upperbound, np.max(vals["loss"]))
      # plot from best to worst
      lowerbound = np.inf
      sorted_optimizers = sorted(
          optim_vals,
          key=lambda x: optim_vals[x]["loss"][-1],
          reverse=True,
      )
      if pure_only:
          assert len(pure_algorithms) > 0
          sorted_optimizers = [
              o
              for o in sorted_optimizers
              if o + " " in [p[: (len(o) + 1)] for p in pure_algorithms]
          ]
          with open("rnk__" + str(title) + ".cp.txt", "w") as f:
              f.write(compactize(title))
              f.write("ranking:\n")
              for i, algo in reversed(
                  list(enumerate(sorted_optimizers))
              ):
                  f.write(f"  algo {i}: {algo} (x)\n")
          # print(sorted_optimizers, " merged with ", pure_algorithms)
          # print("Leads to ", sorted_optimizers)
      self._fig = plt.figure()
      self._ax = self._fig.add_subplot(111)
      # use log plot? yes, if no negative value
      logplot = not any(
          x <= 0 or x > 10**8
          for ov in optim_vals.values()
          for x in ov["loss"]
      )  # if x < np.inf)
      if logplot:
          self._ax.set_yscale("log")
          for ov in optim_vals.values():
              if ov["loss"].size:
                  ov["loss"] = np.maximum(1e-30, ov["loss"])
      # other setups
      self._ax.autoscale(enable=False)
      self._ax.set_xscale("log")
      self._ax.set_xlabel(xaxis)
      self._ax.set_ylabel("loss")
      self._ax.grid(True, which="both")
      self._overlays: tp.List[tp.Any] = []
      legend_infos: tp.List[LegendInfo] = []
      title_addendum = ""
      for optim_name in (
          sorted_optimizers[:1] + sorted_optimizers[-12:]
          if len(sorted_optimizers) > 13
          else sorted_optimizers
      ):
          vals = optim_vals[optim_name]
          indices = np.where(vals["num_eval"] > 0)
          lowerbound = min(lowerbound, np.min(vals["loss"]))
          # We here add some state of the art results.
          # This adds a cross on figures, x-axis = budget and y-axis = loss.
          for sota_name, sota in [("GP", gp_sota())]:
              for k in sota.keys():
                  if k in title:
                      th = sota[k][0]  # loss of proposed solution.
                      cost = sota[k][
                          1
                      ]  # Computational cost for the proposed result.
                      title_addendum = f"({sota_name}:{th})"
                      lowerbound = min(
                          lowerbound, th, 0.9 * th, 1.1 * th
                      )
                      plt.plot(  # Horizontal line at the obtained GP cost.
                          vals[xaxis][indices],
                          th + 0 * vals["loss"][indices],
                          name_style[optim_name],
                          label="gp",
                      )
                      plt.plot(  # Vertical line, showing the budget of the GP solution.
                          [cost] * 3,
                          [
                              min(vals["loss"][indices]),
                              sum(vals["loss"][indices]) / len(indices),
                              max(vals["loss"][indices]),
                          ],
                          name_style[optim_name],
                          label="gp",
                      )
          line = plt.plot(
              vals[xaxis],
              vals["loss"],
              name_style[optim_name],
              label=optim_name,
          )
          # confidence lines
          for conf in self._get_confidence_arrays(vals, log=logplot):
              plt.plot(
                  vals[xaxis],
                  conf,
                  name_style[optim_name],
                  label=optim_name,
                  alpha=0.1,
              )
          text = "{} ({:.3g} <{:.3g}>)".format(
              optim_name,
              vals["loss"][-1],
              vals["loss"][-2]
              if len(vals["loss"]) > 2
              else float("nan"),
          )
          if vals[xaxis].size:
              legend_infos.append(
                  LegendInfo(
                      vals[xaxis][-1], vals["loss"][-1], line, text
                  )
              )
      if not (np.isnan(upperbound) or np.isinf(upperbound)):
          upperbound_up = upperbound
          if not (np.isnan(lowerbound) or np.isinf(lowerbound)):
              self._ax.set_ylim(bottom=lowerbound)
              upperbound_up += 0.02 * (upperbound - lowerbound)
              if logplot:
                  upperbound_up = 10 ** (
                      np.log10(upperbound)
                      + 0.02
                      * (np.log10(upperbound) - np.log10(lowerbound))
                  )
          self._ax.set_ylim(top=upperbound_up)
      all_x = [v for vals in optim_vals.values() for v in vals[xaxis]]
      self._ax.set_xlim([min(all_x), max(all_x)])
      self.add_legends(legend_infos)
      # global info
      if "tmp" not in title:
          self._ax.set_title(split_long_title(title + title_addendum))
      self._ax.tick_params(axis="both", which="both")
  
imports: import os
  import re
  import string
  import hashlib
  import warnings
  import argparse
  import itertools
  from collections import defaultdict
  from pathlib import Path
  import numpy
  import pandas
  from matplotlib import pyplot
  from matplotlib.legend import Legend
  from matplotlib import cm
  from mpl_toolkits.axes_grid1 import make_axes_locatable
  import nevergrad.common.typing
  from .None import utils
  from .exporttable import export_table
label_desc: Use image node_id 1 to create a new XpPlotter object  with example: obj = XpPlotter(optim_vals, title, name_style, xaxis, pure_only)