Skip to main content

Log Plots

Using the methods in wandb.plot, you can track charts with wandb.log, including charts that change over time during training. To learn more about our custom charting framework, check out this guide.

Basic Charts

These simple charts make it easy to construct basic visualizations of metrics and results.


Log a custom line plot—a list of connected and ordered points on arbitrary axes.

data = [[x, y] for (x, y) in zip(x_values, y_values)]
table = wandb.Table(data=data, columns = ["x", "y"])
{"my_custom_plot_id" : wandb.plot.line(table, "x", "y",
title="Custom Y vs X Line Plot")})

You can use this to log curves on any two dimensions. Note that if you're plotting two lists of values against each other, the number of values in the lists must match exactly (i.e. each point must have an x and a y).

See in the app →

Run the code →

Model Evaluation Charts

These preset charts have built-in wandb.plot methods that make it quick and easy to log charts directly from your script and see the exact information you're looking for in the UI.


Create a Precision-Recall curve in one line:

wandb.log({"pr": wandb.plot.pr_curve(ground_truth, predictions)})

You can log this whenever your code has access to:

  • a model's predicted scores (predictions) on a set of examples
  • the corresponding ground truth labels (ground_truth) for those examples
  • (optionally) a list of the labels/class names (labels=["cat", "dog", "bird"...] if label index 0 means cat, 1 = dog, 2 = bird, etc.)
  • (optionally) a subset (still in list format) of the labels to visualize in the plot

See in the app →

Run the code →

Interactive Custom Charts

For full customization, tweak a built-in Custom Chart preset or create a new preset, then save the chart. Use the chart ID to log data to that custom preset directly from your script.

# Create a table with the columns to plot
table = wandb.Table(data=data,
columns=["step", "height"])

# Map from the table's columns to the chart's fields
fields = {"x": "step",
"value": "height"}

# Use the table to populate the new custom chart preset
# To use your own saved chart preset, change the vega_spec_name
# To edit the title, change the string_fields
my_custom_chart = wandb.plot_table(
string_fields={"title": "Height Histogram"})

Run the code →

Matplotlib and Plotly Plots

Instead of using W&B Custom Charts with wandb.plot, you can log charts generated with matplotlib and Plotly.

import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4])
plt.ylabel("some interesting numbers")
wandb.log({"chart": plt})

Just pass a matplotlib plot or figure object to wandb.log(). By default we'll convert the plot into a Plotly plot. If you'd rather log the plot as an image, you can pass the plot into wandb.Image. We also accept Plotly charts directly.


If you’re getting an error “You attempted to log an empty plot” then you can store the figure separately from the plot with fig = plt.figure() and then log fig in your call to wandb.log.

Log Custom HTML to W&B Tables

W&B supports logging interactive charts from Plotly and Bokeh as HTML and adding them to Tables.

Log Plotly figures to Tables as HTML

You can log interactive Plotly charts to wandb Tables by converting them to HTML.

import wandb
import as px

# Initialize a new run
run = wandb.init(
project="log-plotly-fig-tables", name="plotly_html"

# Create a table
table = wandb.Table(columns = ["plotly_figure"])

# Create path for Plotly figure
path_to_plotly_html = "./plotly_figure.html"

# Example Plotly figure
fig = px.scatter(x = [0, 1, 2, 3, 4], y = [0, 1, 4, 9, 16])

# Write Plotly figure to HTML
# Set auto_play to False prevents animated Plotly charts
# from playing in the table automatically
fig.write_html(path_to_plotly_html, auto_play = False)

# Add Plotly figure as HTML file into Table

# Log Table
run.log({"test_table": table})

Log Bokeh figures to Tables as HTML

You can log interactive Bokeh charts to wandb Tables by converting them to HTML.

from scipy.signal import spectrogram
import holoviews as hv
import panel as pn
from import wavfile
import numpy as np
from bokeh.resources import INLINE
hv.extension("bokeh", logo=False)
import wandb

def save_audio_with_bokeh_plot_to_html(audio_path, html_file_name):
sr, wav_data =
duration = len(wav_data)/sr
f, t, sxx = spectrogram(wav_data, sr)
spec_gram = hv.Image((t, f, np.log10(sxx)), ["Time (s)", "Frequency (hz)"]).opts(width=500, height=150, labelled=[])
audio = pn.pane.Audio(wav_data, sample_rate=sr, name='Audio', throttle=500)
slider = pn.widgets.FloatSlider(end=duration, visible=False)
line = hv.VLine(0).opts(color='white')
slider.jslink(audio, value='time', bidirectional=True)
slider.jslink(line, value='glyph.location')
combined = pn.Row(audio, spec_gram * line, slider).save(html_file_name)

html_file_name = 'audio_with_plot.html'
audio_path = 'hello.wav'
save_audio_with_bokeh_plot_to_html(audio_path, html_file_name)

wandb_html = wandb.Html(html_file_name)
run = wandb.init(project='audio_test')
my_table = wandb.Table(columns=['audio_with_plot'], data=[[wandb_html], [wandb_html]])
run.log({"audio_table": my_table})
Was this page helpful?👍👎