> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trainy.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Histograms

> Log binned distributions with pluto.Histogram.

## Logging

Histogram logging lets you track how distributions of values (such as weight distributions, activation values, or gradient norms) change over training steps.

To log a histogram, instantiate the `pluto.Histogram` class and pass it to `pluto.log`:

```python theme={null}
histogram = pluto.Histogram(
    data=values,
    bins=64,
)
pluto.log({"layers/layer_0/weights": histogram}, step=epoch)
```

| **Parameter** | **Type**                                | **Description**                                   |
| ------------- | --------------------------------------- | ------------------------------------------------- |
| `data`        | `Union[list, np.ndarray, torch.Tensor]` | The values to build the histogram from.           |
| `bins`        | `int`                                   | Number of bins for the histogram. Defaults to 64. |

### Examples

#### Logging Weight Distributions

```python theme={null}
import pluto
import torch

run = pluto.init(project="my-project")

model = MyModel()
for epoch in range(num_epochs):
    # ... training step ...

    # Log weight distributions for each layer
    for name, param in model.named_parameters():
        if "weight" in name:
            pluto.log({f"histograms/{name}": pluto.Histogram(param.data.cpu())}, step=epoch)
```

#### Logging Gradient Distributions

```python theme={null}
for name, param in model.named_parameters():
    if param.grad is not None:
        pluto.log({
            f"gradients/{name}": pluto.Histogram(param.grad.data.cpu(), bins=32)
        }, step=epoch)
```

## Viewing

Histograms render in the **Distributions** widget, which also hosts categorical [bar charts](/pluto/visualizations/bar-charts). To add one to a dashboard, open **Add Widget**, choose the **Distributions** tab, and search for your histogram log name (e.g. `distributions/weights`).

<img src="https://mintcdn.com/trainy/ck7WI5896eYTYADa/images/pluto/distributions-add-widget.png?fit=max&auto=format&n=ck7WI5896eYTYADa&q=85&s=905eb4f6cc0d751b1e97ba726d9aca6c" alt="Adding a Distributions widget from the Add Widget modal" width="1352" height="1656" data-path="images/pluto/distributions-add-widget.png" />

### View modes

Each widget renders in one of three modes.

**Step** — a bar chart of the current step's frequencies. Use the step slider below the widget to scrub through training; when several widgets share a section, their sliders can be **linked** via the **lock icon** so they move together. Each step is drawn with its own bins and scaled to its own range — see [Axis scaling](#axis-scaling).

<img src="https://mintcdn.com/trainy/RSBzmM6c9FBJXx6L/images/pluto/hist-step.png?fit=max&auto=format&n=RSBzmM6c9FBJXx6L&q=85&s=6d27c6a94eadb403d6e1d729e6223cf8" alt="Histogram in Step mode — the distribution at a single step" width="1030" height="928" data-path="images/pluto/hist-step.png" />

**Ridgeline** — one density curve (joyplot) per step, stacked so you can watch the distribution evolve over training.

<img src="https://mintcdn.com/trainy/RSBzmM6c9FBJXx6L/images/pluto/hist-ridgeline.png?fit=max&auto=format&n=RSBzmM6c9FBJXx6L&q=85&s=0a16524ab6b05cd3ea0946cda68d19c4" alt="Histogram in Ridgeline mode — one density curve per step" width="1030" height="928" data-path="images/pluto/hist-ridgeline.png" />

**Heatmap** — a density grid with frequency as color, the numeric bin on the X axis and step on the Y axis.

<img src="https://mintcdn.com/trainy/ck7WI5896eYTYADa/images/pluto/hist-heatmap.png?fit=max&auto=format&n=ck7WI5896eYTYADa&q=85&s=82d800f2afac0042d75f959bf6242aa1" alt="Histogram in Heatmap mode — frequency as color" width="1030" height="928" data-path="images/pluto/hist-heatmap.png" />

### Steps on X

In Ridgeline and Heatmap modes, a **Steps on X** toggle in the settings popover transposes the chart so steps run along the X axis.

<img src="https://mintcdn.com/trainy/RSBzmM6c9FBJXx6L/images/pluto/hist-ridgeline-stepsx.png?fit=max&auto=format&n=RSBzmM6c9FBJXx6L&q=85&s=41c90756844b09999ee7ff7b50a9245f" alt="Ridgeline with Steps on X" width="1030" height="928" data-path="images/pluto/hist-ridgeline-stepsx.png" />

The same transpose applies in Heatmap mode:

<img src="https://mintcdn.com/trainy/ck7WI5896eYTYADa/images/pluto/hist-heatmap-stepsx.png?fit=max&auto=format&n=ck7WI5896eYTYADa&q=85&s=7de693613c3189b7a521e2d96cd4878e" alt="Heatmap with Steps on X" width="1030" height="928" data-path="images/pluto/hist-heatmap-stepsx.png" />

### Axis scaling

In **Step** mode, each step is scaled to its own data by default: the X axis covers that step's bin range (padded slightly on each side) and the Y axis is that step's own peak frequency. Every step fills the frame, so you can read the shape of the distribution at each point in training.

This matters when one step is much wider or taller than the rest. A single step whose values span `0`–`2000`, logged alongside steps that span `-4`–`4`, would otherwise stretch the shared X axis and squash every other step into an unreadable sliver at the origin — and one unusually tall step would flatten everything else on Y.

#### Lock axes across steps

To compare *magnitude* across steps rather than read each step's shape, enable **Lock axes across steps** in the settings popover. All steps then share one X range and one Y peak, so bar heights are directly comparable from step to step.

The toggle is **Step mode only** and **off by default**. It applies to the widget you set it on and is not saved with the dashboard.

Here is the same step of the same run with the toggle off and then on. This run's distribution widens over training, so an early step is narrow while later steps are far wider:

<img src="https://mintcdn.com/trainy/4ENmou7Nry2P0TBA/images/pluto/hist-lock-off.png?fit=max&auto=format&n=4ENmou7Nry2P0TBA&q=85&s=e59259442f0cda327fc1bfcf2dba2424" alt="Step view with per-step scaling: a full bell curve spanning -4 to 4, filling the card" width="706" height="768" data-path="images/pluto/hist-lock-off.png" />

**Off (default)** — the X axis covers only this step's bins (`-4` to `4`) and the Y axis tops out at its own peak, so the distribution fills the card and its shape is readable.

<img src="https://mintcdn.com/trainy/4ENmou7Nry2P0TBA/images/pluto/hist-lock-on.png?fit=max&auto=format&n=4ENmou7Nry2P0TBA&q=85&s=e352e66303f8dbc709b977759684881d" alt="The same step with axes locked: the same distribution reduced to a narrow spike on an axis spanning -80 to 80" width="706" height="768" data-path="images/pluto/hist-lock-on.png" />

**On** — the axes now span every step's range (`-80` to `80`), so this step becomes a narrow spike at the origin. Its shape is no longer readable, but its height is directly comparable against every other step.

<Note>
  Ridgeline and Heatmap modes stack every step by design, so they always use the shared per-run range. **Lock axes across steps** and **Y max** don't apply there.
</Note>

### Settings

Open the settings popover (the sliders icon, shown on hover) to adjust:

<img src="https://mintcdn.com/trainy/4ENmou7Nry2P0TBA/images/pluto/hist-axis-settings.png?fit=max&auto=format&n=4ENmou7Nry2P0TBA&q=85&s=ddf71b26194a8b42c78c17166e9820c7" alt="The histogram settings popover in Step mode, showing X min, X max, Y max, Lock axes across steps, Ignore outliers, and a disabled Steps on X" width="1356" height="108" data-path="images/pluto/hist-axis-settings.png" />

* **X min** / **X max** — pin the X axis to manual bounds instead of the per-run **Auto** range.
* **Y max** — manual Y bound (Step mode only).
* **Lock axes across steps** — share one X range and Y peak across all steps instead of scaling each step to its own data (Step mode only, off by default). See [Axis scaling](#axis-scaling).
* **Ignore outliers** — clamp the axis and frequency scale so one extreme step doesn't flatten the rest (on by default). Affects Ridgeline and Heatmap; in Step mode it only does something when **Lock axes across steps** is on.

#### Which setting wins

The three controls interact. When more than one applies, they resolve in this order:

| Priority | Control                           | Applies to                                            |
| -------- | --------------------------------- | ----------------------------------------------------- |
| 1 (wins) | **X min** / **X max** / **Y max** | All modes (**Y max**: Step only)                      |
| 2        | **Lock axes across steps**        | Step only                                             |
| 3        | **Ignore outliers** fences        | Ridgeline and Heatmap; Step only when axes are locked |
| 4        | Per-step auto range               | Step only (the default)                               |

A manual bound of `0` counts as a bound — it is not treated as "unset".

#### When Ignore outliers actually clamps

It always applies in Ridgeline and Heatmap. In Step mode it only applies with **Lock axes across steps** on — per-step scaling leaves it nothing to clamp, so the toggle does nothing there.

Even where it applies, the fence stays off unless a spike is genuinely dominating. All three must hold:

* at least **20 steps** to compute fences from
* the full range is more than **3×** the fenced range
* at most **10%** of steps fall outside the fenced range

The 3× rule keeps real spread from being clamped, and the 10% rule rejects bimodal data where many steps legitimately share the wide range. To clamp anyway, set **X min** / **X max** manually.

The difference on a Ridgeline where a single step blows up mid-training:

<img src="https://mintcdn.com/trainy/4ENmou7Nry2P0TBA/images/pluto/hist-outliers-on.png?fit=max&auto=format&n=4ENmou7Nry2P0TBA&q=85&s=8bcf4dc0ebceaeacf4879557ba002664" alt="Ridgeline with Ignore outliers on: 100 readable density curves across a -0.20 to 0.20 axis" width="706" height="768" data-path="images/pluto/hist-outliers-on.png" />

**On (default)** — the one runaway step is fenced out of the axis, so the remaining 100 steps keep a readable `-0.20` to `0.20` range.

<img src="https://mintcdn.com/trainy/4ENmou7Nry2P0TBA/images/pluto/hist-outliers-off.png?fit=max&auto=format&n=4ENmou7Nry2P0TBA&q=85&s=1330190ff1acfc90a8ad40787884159a" alt="Ridgeline with Ignore outliers off: every step flattened to a line on a -60 to 60 axis, with one narrow spike" width="706" height="768" data-path="images/pluto/hist-outliers-off.png" />

**Off** — the axis stretches to fit the outlier (`-60` to `60`) and every step collapses to a flat line. Nothing is readable, including the outlier that caused it.
