This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Custom charts

Create custom charts in your W&B project. Log arbitrary tables of data and visualize them exactly how you want. Control details of fonts, colors, and tooltips with the power of Vega.

Supported charts from vega.github.io/vega

How it works

  1. Log data: From your script, log config and summary data.
  2. Customize the chart: Pull in logged data with a GraphQL query. Visualize the results of your query with Vega, a powerful visualization grammar.
  3. Log the chart: Call your own preset from your script with wandb.plot_table().
PR and ROC curves

If you do not see the expected data, the column you are looking for might not be logged in the selected runs. Save your chart, go back out to the runs table, and verify selected runs using the eye icon.

Log charts from a script

Builtin presets

W&B has a number of builtin chart presets that you can log directly from your script. These include line plots, scatter plots, bar charts, histograms, PR curves, and ROC curves.

wandb.plot.line()

Log a custom line plot—a list of connected and ordered points (x,y) on arbitrary axes x and y.

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

A line plot logs curves on any two dimensions. If you plot two lists of values against each other, the number of values in the lists must match exactly (for example, each point must have an x and a y).

Custom line plot

See an example report or try an example Google Colab notebook.

wandb.plot.scatter()

Log a custom scatter plot—a list of points (x, y) on a pair of arbitrary axes x and y.

with wandb.init() as run:
  data = [[x, y] for (x, y) in zip(class_x_prediction_scores, class_y_prediction_scores)]
  table = wandb.Table(data=data, columns=["class_x", "class_y"])
  run.log({"my_custom_id": wandb.plot.scatter(table, "class_x", "class_y")})

You can use this to log scatter points 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 (for example, each point must have an x and a y).

Scatter plot

See an example report or try an example Google Colab notebook.

wandb.plot.bar()

Log a custom bar chart—a list of labeled values as bars—natively in a few lines:

with wandb.init() as run:
  data = [[label, val] for (label, val) in zip(labels, values)]
  table = wandb.Table(data=data, columns=["label", "value"])
  run.log(
      {
          "my_bar_chart_id": wandb.plot.bar(
              table, "label", "value", title="Custom Bar Chart"
          )
      }
  )

You can use this to log arbitrary bar charts. Note that the number of labels and values in the lists must match exactly (for example, each data point must have both).

Demo bar plot

See an example report or try an example Google Colab notebook.

wandb.plot.histogram()

Log a custom histogram—sort list of values into bins by count/frequency of occurrence—natively in a few lines. Let’s say I have a list of prediction confidence scores (scores) and want to visualize their distribution:

with wandb.init() as run:
  data = [[s] for s in scores]
  table = wandb.Table(data=data, columns=["scores"])
  run.log({"my_histogram": wandb.plot.histogram(table, "scores", title=None)})

You can use this to log arbitrary histograms. Note that data is a list of lists, intended to support a 2D array of rows and columns.

Custom histogram

See an example report or try an example Google Colab notebook.

wandb.plot.pr_curve()

Create a Precision-Recall curve in one line:

with wandb.init() as run:
  plot = wandb.plot.pr_curve(ground_truth, predictions, labels=None, classes_to_plot=None)

  run.log({"pr": plot})

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
Precision-recall curves

See an example report or try an example Google Colab notebook.

wandb.plot.roc_curve()

Create an ROC curve in one line:

with wandb.init() as run:
  # ground_truth is a list of true labels, predictions is a list of predicted scores
  ground_truth = [0, 1, 0, 1, 0, 1]
  predictions = [0.1, 0.4, 0.35, 0.8, 0.7, 0.9]

  # Create the ROC curve plot
  # labels is an optional list of class names, classes_to_plot is an optional subset of those labels to visualize
  plot = wandb.plot.roc_curve(
      ground_truth, predictions, labels=None, classes_to_plot=None
  )

  run.log({"roc": plot})

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 these labels to visualize on the plot
ROC curve

See an example report or try an example Google Colab notebook.

Custom presets

Tweak a builtin 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. Try an example Google Colab notebook.

# 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
my_custom_chart = wandb.plot_table(
    vega_spec_name="carey/new_chart",
    data_table=table,
    fields=fields,
)
Custom chart presets

Log data

You can log the following data types from your script and use them in a custom chart:

  • Config: Initial settings of your experiment (your independent variables). This includes any named fields you’ve logged as keys to wandb.Run.config at the start of your training. For example: wandb.Run.config.learning_rate = 0.0001
  • Summary: Single values logged during training (your results or dependent variables). For example, wandb.Run.log({"val_acc" : 0.8}). If you write to this key multiple times during training via wandb.Run.log(), the summary is set to the final value of that key.
  • History: The full time series of the logged scalar is available to the query via the history field
  • summaryTable: If you need to log a list of values, use a wandb.Table() to save that data, then query it in your custom panel.
  • historyTable: If you need to see the history data, then query historyTable in your custom chart panel. Each time you call wandb.Table() or log a custom chart, you’re creating a new table in history for that step.

How to log a custom table

Use wandb.Table() to log your data as a 2D array. Typically each row of this table represents one data point, and each column denotes the relevant fields/dimensions for each data point which you’d like to plot. As you configure a custom panel, the whole table will be accessible via the named key passed to wandb.Run.log()(custom_data_table below), and the individual fields will be accessible via the column names (x, y, and z). You can log tables at multiple time steps throughout your experiment. The maximum size of each table is 10,000 rows. Try an example a Google Colab.

with wandb.init() as run:
  # Logging a custom table of data
  my_custom_data = [[x1, y1, z1], [x2, y2, z2]]
  run.log(
      {"custom_data_table": wandb.Table(data=my_custom_data, columns=["x", "y", "z"])}
  )

Customize the chart

Add a new custom chart to get started, then edit the query to select data from your visible runs. The query uses GraphQL to fetch data from the config, summary, and history fields in your runs.

Custom chart creation

Query syntax for different data tables

W&B provides several ways to query your data:

  • summary: Access final metric values (for example, {"summary": ["val_acc", "val_loss"]})
  • history: Access time-series data (for example, {"history": ["loss", "accuracy"]})
  • summaryTable: Access custom tables with specific keys:
    {
      "summaryTable": {
        "tableKey": "eval_results",
        "tableColumns": ["precision", "recall"]
      }
    }
    
  • historyTable: Access tables logged at multiple steps:
    {
      "historyTable": {
        "tableKey": "predictions",
        "tableColumns": ["x", "y", "prediction"]
      }
    }
    

For detailed query examples and patterns, see the Query syntax guide.

Custom visualizations

Select a Chart in the upper right corner to start with a default preset. Next, select Chart fields to map the data you’re pulling in from the query to the corresponding fields in your chart.

The following image shows an example on how to select a metric then mapping that into the bar chart fields below.

Creating a custom bar chart

How to edit Vega

Click Edit at the top of the panel to go into Vega edit mode. Here you can define a Vega specification that creates an interactive chart in the UI. You can change any aspect of the chart. For example, you can change the title, pick a different color scheme, show curves as a series of points instead of as connected lines. You can also make changes to the data itself, such as using a Vega transform to bin an array of values into a histogram. The panel preview will update interactively, so you can see the effect of your changes as you edit the Vega spec or query. Refer to the Vega documentation and tutorials .

Field references

To pull data into your chart from W&B, add template strings of the form "${field:<field-name>}" anywhere in your Vega spec. This will create a dropdown in the Chart Fields area on the right side, which users can use to select a query result column to map into Vega.

To set a default value for a field, use this syntax: "${field:<field-name>:<placeholder text>}"

Saving chart presets

Apply any changes to a specific visualization panel with the button at the bottom of the modal. Alternatively, you can save the Vega spec to use elsewhere in your project. To save the reusable chart definition, click Save as at the top of the Vega editor and give your preset a name.

Using CustomChart with the Reports API

You can programmatically create custom charts in reports using the wr.CustomChart class:

import wandb_workspaces.reports.v2 as wr

# Create a custom chart from a logged table
custom_chart = wr.CustomChart(
    query={
        'summaryTable': {
            'tableKey': 'eval_results',
            'tableColumns': ['class', 'precision', 'recall']
        }
    },
    chart_name='wandb/bar/v0',
    chart_fields={'x': 'class', 'y': 'precision'}
)

# Add to a report
report = wr.Report(
    project="my-project",
    title="Evaluation Report"
)

panel_grid = wr.PanelGrid(
    panels=[custom_chart],
    runsets=[wr.RunSet(entity="my-entity", project="my-project")]
)

report.blocks = [panel_grid]
report.save()

For more examples, including how to import existing charts to reports, see the Query syntax guide.

Articles and guides

  1. Query syntax for custom charts
  2. The W&B Machine Learning Visualization IDE
  3. Visualizing NLP Attention Based Models
  4. Visualizing The Effect of Attention on Gradient Flow
  5. Logging arbitrary curves

Common use cases

  • Customize bar plots with error bars
  • Show model validation metrics which require custom x-y coordinates (like precision-recall curves)
  • Overlay data distributions from two different models/experiments as histograms
  • Show changes in a metric via snapshots at multiple points during training
  • Create a unique visualization not yet available in W&B (and hopefully share it with the world)

1 - Query syntax for custom charts

Learn how to build queries for custom charts and access different data tables

Learn how to build queries for custom charts to access different data tables in W&B. This guide covers both UI-based custom charts and programmatic chart creation using the Reports API.

Understanding data tables

W&B provides different data tables you can query in custom charts:

Summary data

Access final values from your runs using summary:

  • Contains the last logged value for each metric
  • Useful for comparing final results across runs
  • Example: final accuracy, best validation score

History data

Access time-series data using history:

  • Contains all logged values over time
  • Useful for plotting training curves
  • Example: loss over epochs, metrics over steps

Summary tables

Access custom tables logged to summary using summaryTable:

  • Contains wandb.Table() objects logged once per run
  • Useful for structured data like evaluation results
  • Requires specifying tableKey and tableColumns

History tables

Access custom tables logged during training using historyTable:

  • Contains wandb.Table() objects logged at multiple steps
  • Each wandb.log() with a table creates a new entry
  • Useful for progressive results like per-epoch predictions

Query syntax examples

Accessing summary metrics

To query summary metrics:

{
  "summary": ["val_loss", "val_acc", "train_loss"]
}

Accessing history metrics

To query time-series data:

{
  "history": ["loss", "accuracy", "learning_rate"]
}

Accessing summary tables

To query a custom table logged to summary:

{
  "summaryTable": {
    "tableKey": "evaluation_results",
    "tableColumns": ["precision", "recall", "f1_score"]
  }
}

Accessing history tables

To query tables logged at different time steps:

{
  "historyTable": {
    "tableKey": "predictions_per_epoch",
    "tableColumns": ["epoch", "image", "prediction", "ground_truth"]
  }
}

Using CustomChart with the reports API

When creating reports programmatically, use wr.CustomChart to add custom visualizations that query your logged data.

Basic CustomChart examples

import wandb_workspaces.reports.v2 as wr

# Create a chart from summary data
custom_chart = wr.CustomChart(
    query={'summary': ['val_loss', 'val_acc']},
    chart_name='wandb/scatter/v0',
    chart_fields={'x': 'val_loss', 'y': 'val_acc'}
)

# Add to a report
report = wr.Report(
    project="my-project",
    title="Custom Charts Report"
)

panel_grid = wr.PanelGrid(
    panels=[custom_chart],
    runsets=[wr.RunSet(entity="my-entity", project="my-project")]
)

report.blocks = [panel_grid]
report.save()

Querying summary tables

To visualize data from a logged wandb.Table:

# First, log a table in your training script
wandb.init()
eval_table = wandb.Table(
    columns=["class", "precision", "recall"],
    data=[
        ["cat", 0.95, 0.92],
        ["dog", 0.89, 0.94]
    ]
)
wandb.log({"eval_results": eval_table})

# Then, create a custom chart in your report
custom_chart = wr.CustomChart(
    query={
        'summaryTable': {
            'tableKey': 'eval_results',
            'tableColumns': ['class', 'precision', 'recall']
        }
    },
    chart_name='wandb/bar/v0',
    chart_fields={'x': 'class', 'y': 'precision'}
)

Querying history tables

For tables logged at different time steps:

# In your training script
for epoch in range(num_epochs):
    predictions_table = wandb.Table(
        columns=["x", "y", "prediction"],
        data=[[x, y, pred] for x, y, pred in epoch_predictions]
    )
    wandb.log({"predictions": predictions_table})

# In your report
custom_chart = wr.CustomChart(
    query={
        'historyTable': {
            'tableKey': 'predictions',
            'tableColumns': ['x', 'y', 'prediction']
        }
    },
    chart_name='wandb/scatter/v0',
    chart_fields={'x': 'x', 'y': 'y', 'color': 'prediction'}
)

Combining data sources

You can query different tables in a single chart:

custom_chart = wr.CustomChart(
    query={
        'summary': ['final_score'],
        'summaryTable': {
            'tableKey': 'class_scores',
            'tableColumns': ['class', 'score']
        }
    },
    chart_name='wandb/custom/v0',
    chart_fields={
        'x': 'class',
        'y': 'score',
        'title': 'Class Performance'
    }
)

Importing existing charts to reports

To programmatically add charts that you’ve already created in the UI to a report:

Method 1: Import charts via UI then access programmatically

# Step 1: Create a report with imported charts in the UI
# Use "Create Report > Import charts" to add existing charts

# Step 2: Load the report and extract panels
import wandb_workspaces.reports.v2 as wr

# Load the report containing imported charts
source_report = wr.Report.from_url("https://wandb.ai/entity/project/reports/...")
imported_panels = source_report.blocks[0].panels

# Step 3: Add specific panels to a new report
target_report = wr.Report(
    project="my-project",
    title="Report with Imported Charts"
)

# Select the panel you want (by index or by filtering)
selected_panel = imported_panels[0]  # First panel

# Add to new report
panel_grid = wr.PanelGrid(
    panels=[selected_panel],
    runsets=[wr.RunSet(entity="my-entity", project="my-project")]
)

target_report.blocks = [panel_grid]
target_report.save()

Method 2: Clone and update existing panels

# Load a report as a template
template_report = wr.Report.from_url("https://wandb.ai/entity/project/reports/...")

# Extract and modify panels
for block in template_report.blocks:
    if isinstance(block, wr.PanelGrid):
        # Access existing panels
        existing_panels = block.panels
        
        # Modify or filter panels as needed
        modified_panels = []
        for panel in existing_panels:
            if isinstance(panel, wr.CustomChart):
                # You can access and modify the query
                panel.query['summaryTable']['tableKey'] = 'new_table_name'
            modified_panels.append(panel)
        
        # Create new panel grid with modified panels
        new_panel_grid = wr.PanelGrid(
            panels=modified_panels,
            runsets=block.runsets
        )

# Save the modified report
new_report = wr.Report(
    project="my-project", 
    title="Modified Report"
)
new_report.blocks = [new_panel_grid]
new_report.save()

Common query patterns

Filtering and grouping

When building queries, you can filter runs and group data:

# Filter runs by config values
custom_chart = wr.CustomChart(
    query={
        'summary': ['accuracy'],
        'config': ['model_type', 'learning_rate']
    },
    chart_name='wandb/scatter/v0',
    chart_fields={
        'x': 'learning_rate',
        'y': 'accuracy',
        'color': 'model_type'
    }
)

Multi-axis charts

Create charts with two y-axes:

custom_chart = wr.CustomChart(
    query={'history': ['train_loss', 'val_loss', 'learning_rate']},
    chart_name='wandb/line/v0',
    chart_fields={
        'x': '_step',
        'y': ['train_loss', 'val_loss'],
        'y2': 'learning_rate'  # Secondary y-axis
    }
)

Best practices

  1. Use appropriate table types:

    • Use summaryTable for final results that don’t change
    • Use historyTable for data that evolves during training
  2. Limit table size: Keep tables under 10,000 rows for optimal performance

  3. Name keys consistently: Use descriptive, consistent names for table keys across your project

  4. Test queries in UI first: Build and test your query in the UI custom chart editor, then transfer to code

  5. Cache imported panels: When importing charts, load the source report once and reuse the panels

Troubleshooting

Data not appearing

If your data doesn’t appear in the custom chart:

  • Verify the table key matches exactly what you logged
  • Check that selected runs contain the logged tables
  • Ensure column names in tableColumns match your table’s column names

Query errors

Common query issues and solutions:

  • Missing quotes: Ensure all strings in the query are properly quoted
  • Wrong field type: summaryTable requires an object with tableKey and tableColumns, not an array
  • Typos in field names: Double-check spelling of table keys and column names

See also

2 - Tutorial: Use custom charts

Tutorial of using the custom charts feature in the W&B UI

Use custom charts to control the data you’re loading in to a panel and its visualization.

1. Log data to W&B

First, log data in your script. Use wandb.Run.config for single points set at the beginning of training, like hyperparameters. Use wandb.Run.log() for multiple points over time, and log custom 2D arrays with wandb.Table(). We recommend logging up to 10,000 data points per logged key.

with wandb.init() as run: 

  # Logging a custom table of data
  my_custom_data = [[x1, y1, z1], [x2, y2, z2]]
  run.log(
    {"custom_data_table": wandb.Table(data=my_custom_data, columns=["x", "y", "z"])}
  )

Try a quick example notebook to log the data tables, and in the next step we’ll set up custom charts. See what the resulting charts look like in the live report.

2. Create a query

Once you’ve logged data to visualize, go to your project page and click the + button to add a new panel, then select Custom Chart. You can follow along in the custom charts demo workspace.

Blank custom chart

Add a query

  1. Click summary and select historyTable to set up a new query pulling data from the run history.
  2. Type in the key where you logged the wandb.Table(). In the code snippet above, it was my_custom_table . In the example notebook, the keys are pr_curve and roc_curve.

Set Vega fields

Now that the query is loading in these columns, they’re available as options to select in the Vega fields dropdown menus:

Pulling in columns from the query results to set Vega fields
  • x-axis: runSets_historyTable_r (recall)
  • y-axis: runSets_historyTable_p (precision)
  • color: runSets_historyTable_c (class label)

3. Customize the chart

Now that looks pretty good, but I’d like to switch from a scatter plot to a line plot. Click Edit to change the Vega spec for this built in chart. Follow along in the custom charts demo workspace.

Custom chart selection

I updated the Vega spec to customize the visualization:

  • add titles for the plot, legend, x-axis, and y-axis (set “title” for each field)
  • change the value of “mark” from “point” to “line”
  • remove the unused “size” field
PR curve Vega spec

To save this as a preset that you can use elsewhere in this project, click Save as at the top of the page. Here’s what the result looks like, along with an ROC curve:

PR curve chart

Bonus: Composite Histograms

Histograms can visualize numerical distributions to help us understand larger datasets. Composite histograms show multiple distributions across the same bins, letting us compare two or more metrics across different models or across different classes within our model. For a semantic segmentation model detecting objects in driving scenes, we might compare the effectiveness of optimizing for accuracy versus intersection over union (IOU), or we might want to know how well different models detect cars (large, common regions in the data) versus traffic signs (much smaller, less common regions). In the demo Colab, you can compare the confidence scores for two of the ten classes of living things.

Composite histogram

To create your own version of the custom composite histogram panel:

  1. Create a new Custom Chart panel in your Workspace or Report (by adding a “Custom Chart” visualization). Hit the “Edit” button in the top right to modify the Vega spec starting from any built-in panel type.
  2. Replace that built-in Vega spec with my MVP code for a composite histogram in Vega. You can modify the main title, axis titles, input domain, and any other details directly in this Vega spec using Vega syntax (you could change the colors or even add a third histogram :)
  3. Modify the query in the right hand side to load the correct data from your wandb logs. Add the field summaryTable and set the corresponding tableKey to class_scores to fetch the wandb.Table logged by your run. This will let you populate the two histogram bin sets (red_bins and blue_bins) via the dropdown menus with the columns of the wandb.Table logged as class_scores. For my example, I chose the animal class prediction scores for the red bins and plant for the blue bins.
  4. You can keep making changes to the Vega spec and query until you’re happy with the plot you see in the preview rendering. Once you’re done, click Save as in the top and give your custom plot a name so you can reuse it. Then click Apply from panel library to finish your plot.

Here’s what my results look like from a very brief experiment: training on only 1000 examples for one epoch yields a model that’s very confident that most images are not plants and very uncertain about which images might be animals.

Chart configuration Chart result