Error when plotting radar chart using matplotlib plot

Hi, I am getting the below error when trying to plot a radar chart using matplotlib plot object via wandb.log(). I can generate the plot successfully and save it to a local file so the plotting code works fine. There seems to be some error when converting the matplotlib plot to plotly as inferred from the stack trace below.

matplotlib version: 3.5.0
wandb version: 0.15.10
plotly version: 5.16.1

Stacktrace -

Traceback (most recent call last):
  File "/mnt/eval/main.py", line 159, in <module>
    wandb.log({'radar_chart' : fig })
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/wandb_run.py", line 419, in wrapper
    return func(self, *args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/wandb_run.py", line 370, in wrapper_fn
    return func(self, *args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/wandb_run.py", line 360, in wrapper
    return func(self, *args, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/wandb_run.py", line 1792, in log
    self._log(data=data, step=step, commit=commit)
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/wandb_run.py", line 1567, in _log
    self._partial_history_callback(data, step, commit)
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/wandb_run.py", line 1439, in _partial_history_callback
    self._backend.interface.publish_partial_history(
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/interface/interface.py", line 528, in publish_partial_history
    data = history_dict_to_json(run, data, step=user_step, ignore_copy_err=True)
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/data_types/utils.py", line 52, in history_dict_to_json
    payload[key] = val_to_json(
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/data_types/utils.py", line 86, in val_to_json
    val = Plotly.make_plot_media(val)
  File "/usr/local/lib/python3.10/dist-packages/wandb/sdk/data_types/plotly.py", line 49, in make_plot_media
    val = util.matplotlib_to_plotly(val)
  File "/usr/local/lib/python3.10/dist-packages/wandb/util.py", line 513, in matplotlib_to_plotly
    return tools.mpl_to_plotly(obj)
  File "/tmp/.local/lib/python3.10/site-packages/plotly/tools.py", line 111, in mpl_to_plotly
    matplotlylib.Exporter(renderer).run(fig)
  File "/tmp/.local/lib/python3.10/site-packages/plotly/matplotlylib/mplexporter/exporter.py", line 53, in run
    self.crawl_fig(fig)
  File "/tmp/.local/lib/python3.10/site-packages/plotly/matplotlylib/mplexporter/exporter.py", line 124, in crawl_fig
    self.crawl_ax(ax)
  File "/tmp/.local/lib/python3.10/site-packages/plotly/matplotlylib/mplexporter/exporter.py", line 128, in crawl_ax
    with self.renderer.draw_axes(ax=ax, props=utils.get_axes_properties(ax)):
  File "/usr/lib/python3.10/contextlib.py", line 135, in __enter__
    return next(self.gen)
  File "/tmp/.local/lib/python3.10/site-packages/plotly/matplotlylib/mplexporter/renderers/base.py", line 57, in draw_axes
    self.open_axes(ax=ax, props=props)
  File "/tmp/.local/lib/python3.10/site-packages/plotly/matplotlylib/renderer.py", line 168, in open_axes
    bottom_spine = mpltools.get_spine_visible(ax, "bottom")
  File "/tmp/.local/lib/python3.10/site-packages/plotly/matplotlylib/mpltools.py", line 366, in get_spine_visible
    spine = ax.spines[spine_key]
  File "/tmp/.local/lib/python3.10/site-packages/matplotlib/spines.py", line 572, in __getitem__
    return self._dict[key]
KeyError: 'bottom'

Hi @sarda014 thank you for reporting this issue. Would it be please possible to share a minimal matplotlib code of the radar chart to investigate this further?

@thanos-wandb please find code to create plotlib for radar chart below -

# df structure
# model, score1, score2, score3
# openai-1, 1.0, 0.9, 0.8
# openai-2, 1.0, 0.9, 0.8
def plot_radar_view(title, df, file_path=""):
    # clear previous memory
    plt.clf()

    # number of variable
    categories = list(df)[1:]
    N = len(categories)

    rows = df.shape[0]

    # We are going to plot the first line of the data frame.
    # But we need to repeat the first value to close the circular graph:
    values = []
    models = []
    for i in range(rows):
        values.append([])
        models.append(df.loc[i]["model"])

        # drop first column 'model' and flatten
        values[i] = df.loc[i].drop("model").values.flatten().tolist()
        values[i] += values[i][:1]

    # What will be the angle of each axis in the plot? (we divide the plot / number of variable)
    angles = [n / float(N) * 2 * pi for n in range(N)]
    angles += angles[:1]

    # Initialise the spider plot
    ax = plt.subplot(111, polar=True)

    # Draw one axe per variable + add labels
    plt.xticks(angles[:-1], categories, color="grey", size=8)

    # Draw ylabels
    ax.set_rlabel_position(0)
    plt.yticks(
        [0.2, 0.4, 0.6, 0.8, 1.0],
        ["0.2", "0.4", "0.6", "0.8", "1.0"],
        color="grey",
        size=7,
    )
    plt.ylim(0, 1)

    # Plot data
    for i in range(rows):
        ax.plot(angles, values[i], linewidth=1, linestyle="solid")

    # set plot title
    plt.title(title)

    # legend to explain line color mapping with model
    ax.legend(models, loc="best", fontsize="xx-small")

    # save to a file or show based on input path
    if file_path is not None and len(file_path) > 0:
        plt.savefig(file_path)
    else:
        plt.show()

Hi @sarda014 thanks so much for the code snippet. I was able to create the chart. Can you please also share what you pass as fig in wandb.log() call from the created plt object? please note that we convert the matplotlib to Plotly chart and due to this conversion some elements may not be supported (docs).

@thanos-wandb I pass ax (returned from plt.subplot(111, polar=True)) to wandb.log

Thank you @sarda014 I have adjusted the code to log the fig instead of ax, but I get to same error as you for either case:

/usr/local/lib/python3.10/dist-packages/matplotlib/spines.py in __getitem__(self, key)
    572                     'Spines does not support slicing except for the fully '
    573                     'open slice [:] to access all spines.')
--> 574         return self._dict[key]
    575 
    576     def __setitem__(self, key, value):

KeyError: 'bottom'

This seems to be raised from matplotlib when we do the conversion to Plotly. Unfortunately, this isn’t maintained by us and has been deprecated. There are the following alternative workarounds:

  1. User our custom charts functionality:
    

Vega spec for radar chart: Radar Chart Example | Vega

  1. Convert your plots to Plotly charts explicitly and log them with wandb.Plotly.
    
  2. Convert the matplotlib objects to images with wandb.Image(figure_or_plot)
    

Please let us know if you had any follow-up questions.

Hi @sarda014 We wanted to follow up with you regarding your support request as we have not heard back from you. Please let us know if we can be of further assistance or if your issue has been resolved.

Hi @sarda014 since we have not heard back from you we are going to close this request. If you would like to re-open the conversation, please let us know!

@thanos-wandb Thanks for your help in debugging this issue. I have already tried Solution 1. and looks like that is also not supported by wandb - Radar Plot using Vega Spec - #2 by nathank

Solution 3. will not work for me since I need the plots to be interactive. I have not explored Solution 2. yet.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.