Skip to main content
Use wandb.Table to log structured data that you can visualize and query in W&B. Tables let you inspect model predictions, dataset samples, and other per-row results alongside your metrics, so you can debug model behavior and share findings with collaborators. This guide walks through the typical table workflow:
  1. Create tables
  2. Add data
  3. Retrieve data
  4. Save tables

Create tables

To define a table, specify the columns you want to see for each row of data. Each row might be a single item in your training dataset, a step or epoch during training, a prediction your model made on a test item, or an object your model generated. Each column has a fixed type: numeric, text, boolean, image, video, audio, and so on. You don’t need to specify the type in advance. Give each column a name, and only pass data of that type into that column index. For a more detailed example, see the W&B Tables guide. Use the wandb.Table constructor in one of two ways:
  1. List of rows: Log named columns and rows of data. For example, the following code snippet generates a table with two rows and three columns:
    wandb.Table(columns=["a", "b", "c"], data=[["1a", "1b", "1c"], ["2a", "2b", "2c"]])
    
  2. Pandas DataFrame: Log a DataFrame using wandb.Table(dataframe=my_df). W&B extracts column names from the DataFrame.

From an existing array or dataframe

# assume a model has returned predictions on four images
# with the following fields available:
# - the image id
# - the image pixels, wrapped in a wandb.Image()
# - the model's predicted label
# - the ground truth label
my_data = [
    [0, wandb.Image("img_0.jpg"), 0, 0],
    [1, wandb.Image("img_1.jpg"), 8, 0],
    [2, wandb.Image("img_2.jpg"), 7, 1],
    [3, wandb.Image("img_3.jpg"), 1, 1],
]

# create a wandb.Table() with corresponding columns
columns = ["id", "image", "prediction", "truth"]
test_table = wandb.Table(data=my_data, columns=columns)

Add data

After you create a table, populate it with the rows or columns you want to track. Tables are mutable. As your script executes, you can add more data to your table, up to 200,000 rows. You can add data to a table in two ways:
  1. Add a row: table.add_data("3a", "3b", "3c"). The new row isn’t represented as a list. If your row is in list format, use the star notation, *, to expand the list to positional arguments: table.add_data(*my_row_list). The row must contain the same number of entries as there are columns in the table.
  2. Add a column: table.add_column(name="col_name", data=col_data). The length of col_data must equal the table’s current number of rows. Here, col_data can be a list data, or a NumPy NDArray.

Add data incrementally

This code sample shows how to create and populate a W&B table incrementally. You define the table with predefined columns, including confidence scores for all possible labels, and add data row by row during inference. You can also add data to tables incrementally when resuming runs.
# Define the columns for the table, including confidence scores for each label
columns = ["id", "image", "guess", "truth"]
for digit in range(10):  # Add confidence score columns for each digit (0-9)
    columns.append(f"score_{digit}")

# Initialize the table with the defined columns
test_table = wandb.Table(columns=columns)

# Iterate through the test dataset and add data to the table row by row
# Each row includes the image ID, image, predicted label, true label, and confidence scores
for img_id, img in enumerate(mnist_test_data):
    true_label = mnist_test_data_labels[img_id]  # Ground truth label
    guess_label = my_model.predict(img)  # Predicted label
    test_table.add_data(
        img_id, wandb.Image(img), guess_label, true_label
    )  # Add row data to the table

Add data to resumed runs

You can incrementally update a W&B table in resumed runs by loading an existing table from an artifact, retrieving the last row of data, and adding the updated metrics. Then, reinitialize the table for compatibility and log the updated version back to W&B.
import wandb

# Initialize a run
with wandb.init(project="my_project") as run:

    # Load the existing table from the artifact
    best_checkpt_table = run.use_artifact(table_tag).get(table_name)

    # Get the last row of data from the table for resuming
    best_iter, best_metric_max, best_metric_min = best_checkpt_table.data[-1]

    # Update the best metrics as needed

    # Add the updated data to the table
    best_checkpt_table.add_data(best_iter, best_metric_max, best_metric_min)

    # Reinitialize the table with its updated data to ensure compatibility
    best_checkpt_table = wandb.Table(
        columns=["col1", "col2", "col3"], data=best_checkpt_table.data
    )

    # Initialize the Run
    with wandb.init() as run:

        # Log the updated table to W&B
        run.log({table_name: best_checkpt_table})

Retrieve data

After data is in a table, you can read it back to compute statistics, feed it into downstream code, or inspect specific rows. Access the data by column or by row:
  1. Row iterator: Use the row iterator of the table such as for ndx, row in table.iterrows(): ... to efficiently iterate over the data’s rows.
  2. Get a column: Retrieve a column of data using table.get_column("col_name"). As a convenience, you can pass convert_to="numpy" to convert the column to a NumPy NDArray of primitives. This is useful if your column contains media types such as wandb.Image, so that you can access the underlying data directly.

Save tables

After you generate a table of data in your script, for example, a table of model predictions, save it to W&B to visualize the results live. Saving a table persists it to the W&B backend, so that you and your collaborators can view, query, and compare it in the UI.

Log a table to a run

To attach a table to a specific run so that it appears in that run’s workspace, use wandb.Run.log():
with wandb.init() as run:
    my_table = wandb.Table(columns=["a", "b"], data=[["1a", "1b"], ["2a", "2b"]])
    run.log({"table_key": my_table})
Each time you log a table to the same key, W&B creates a new version of the table and stores it in the backend. You can log the same table across multiple training steps to see how model predictions improve over time, or compare tables across different runs, as long as you log them to the same key. You can log up to 200,000 rows.
To log more than 200,000 rows, you can override the limit with:wandb.Table.MAX_ARTIFACT_ROWS = XHowever, this can cause performance issues, such as slower queries, in the UI.

Access tables programmatically

In the backend, W&B persists tables as artifacts. To access a specific version of a logged table from code, for example, to reload predictions for further analysis, use the artifact API. Replace [RUN-ID] with the run ID, [TABLE-NAME] with the table name, and [TAG] with the artifact alias or version tag:
with wandb.init() as run:
    my_table = run.use_artifact("run-[RUN-ID]-[TABLE-NAME]:[TAG]").get("[TABLE-NAME]")
For more information about artifacts, see the Artifacts chapter in the Developer Guide.

Visualize tables

Any table logged this way appears in your workspace on both the run page and the project page. For more information, see Visualize and Analyze Tables.

Artifact tables

Use artifact.add() to log tables to the Artifacts section of your run instead of the workspace. This approach is useful when you have a dataset that you want to log once and then reference from future runs, so you don’t re-upload the same data each time.
with wandb.init(project="my_project") as run:
    # create a wandb Artifact for each meaningful step
    test_predictions = wandb.Artifact("mnist_test_preds", type="predictions")

    # [build up your predictions data as above]
    test_table = wandb.Table(data=data, columns=columns)
    test_predictions.add(test_table, "my_test_key")
    run.log_artifact(test_predictions)
Refer to this Colab for a detailed example of artifact.add() with image data and this report for an example of how to use artifacts and tables to version control and deduplicate tabular data.

Join artifact tables

To compare or correlate data across two tables, for example, to align ground truth with predictions or pair original and generated samples, join them with wandb.JoinedTable(table_1, table_2, join_key). You can join tables you constructed locally or tables you retrieved from other artifacts.
ArgsDescription
table_1(str, wandb.Table, ArtifactEntry) the path to a wandb.Table in an artifact, the table object, or ArtifactEntry
table_2(str, wandb.Table, ArtifactEntry) the path to a wandb.Table in an artifact, the table object, or ArtifactEntry
join_key(str, [str, str]) key or keys on which to perform the join
To join two tables you logged previously in an artifact context, fetch them from the artifact and join the result into a new table. For example, the following code reads one table of original songs called 'original_songs' and another table of synthesized versions of the same songs called 'synth_songs'. It joins the two tables on "song_id" and uploads the result as a new W&B Table:
import wandb

with wandb.init(project="my_project") as run:

    # fetch original songs table
    orig_songs = run.use_artifact("original_songs:latest")
    orig_table = orig_songs.get("original_samples")

    # fetch synthesized songs table
    synth_songs = run.use_artifact("synth_songs:latest")
    synth_table = synth_songs.get("synth_samples")

    # join tables on "song_id"
    join_table = wandb.JoinedTable(orig_table, synth_table, "song_id")
    join_at = wandb.Artifact("synth_summary", "analysis")

    # add table to artifact and log to W&B
    join_at.add(join_table, "synth_explore")
    run.log_artifact(join_at)
Read this tutorial for an example of combining two tables stored in different artifact objects.