Skip to content

Plotly Essentials

Interactive, web-ready charts from pandas DataFrames. Plotly Express (px) gives you polished figures in one line; hover tooltips, zoom, and pan come free — perfect for exploration and dashboards.

Table of Contents

Installation / Setup

pip install plotly            # or: uv add plotly
pip install kaleido           # optional: static PNG/SVG export
import plotly.express as px
import plotly.graph_objects as go

In Jupyter/VSCode/Colab figures render automatically; in plain scripts use fig.show() (opens a browser tab).

Core Syntax

One-liner charts (Plotly Express)

Goal One-liner
Line px.line(df, x="date", y="sales", color="region")
Bar px.bar(df, x="day", y="total_bill", color="sex")
Histogram px.histogram(df, x="age", nbins=30, color="churn")
Scatter px.scatter(df, x="bill", y="tip", color="day", size="size")
Box px.box(df, x="species", y="body_mass_g", color="sex")
Violin px.violin(df, x="species", y="flipper_length_mm", box=True)
Heatmap px.imshow(corr_matrix, text_auto=True, color_continuous_scale="RdBu_r")
Density heatmap px.density_heatmap(df, x="bill", y="tip", marginal_x="histogram")
Scatter matrix px.scatter_matrix(df, dimensions=[...], color="species")
fig = px.scatter(df, x="total_bill", y="tip",
                 color="day", title="Tips", template="plotly_white")
fig.show()

Low-level control (graph_objects)

fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=y, mode="lines+markers", name="train"))
fig.add_trace(go.Scatter(x=x, y=y2, mode="lines", name="val", line=dict(dash="dash")))
fig.update_layout(title="Loss curves", xaxis_title="epoch", yaxis_title="loss")

Use px for 90% of work; switch to go for multi-trace figures and fine control.

Layout, annotations, axes

fig.update_layout(
    title="Title",
    xaxis_title="x", yaxis_title="y",
    legend_title="Groups",
    template="plotly_white",          # plotly, plotly_dark, seaborn, ggplot2, none
    width=800, height=500,
)
fig.update_xaxes(range=[0, 10], title_text="epochs")
fig.update_yaxes(tickformat=".0%")     # axis tick formatting
fig.add_annotation(x=5, y=0.9, text="peak", showarrow=True, arrowhead=2)
fig.add_vline(x=7, line_dash="dash", line_color="red")   # reference lines
fig.add_hline(y=0.5)

Legends & colors

fig.update_layout(legend=dict(title="Model", orientation="h", y=1.05))
fig = px.line(df, x="epoch", y="acc", color="model",
              color_discrete_sequence=px.colors.qualitative.Set2)

Subplots

from plotly.subplots import make_subplots

fig = make_subplots(rows=2, cols=2, subplot_titles=("hist", "scatter", "box", "line"))
fig.add_trace(go.Histogram(x=df["age"]), row=1, col=1)
fig.add_trace(go.Scatter(x=df["x"], y=df["y"], mode="markers"), row=1, col=2)
fig.add_trace(go.Box(y=df["val"]), row=2, col=1)
fig.add_trace(go.Scatter(x=t, y=y), row=2, col=2)
fig.update_layout(height=700, showlegend=False)

Exporting

fig.write_html("chart.html")                # standalone interactive file
fig.write_html("chart.html", include_plotlyjs="cdn")   # smaller file
fig.write_image("chart.png", scale=2)       # needs kaleido
fig.write_image("chart.pdf")
fig.to_html() / fig.to_json()               # embed or transfer

Common Operations

# Marginal distributions on a scatter
px.scatter(df, x="total_bill", y="tip", marginal_x="histogram", marginal_y="box")

# Faceted by category
px.histogram(df, x="body_mass_g", color="species",
             facet_col="island", facet_col_wrap=2)

# Animation over time (great with gapminder-style data)
px.scatter(gap, x="gdpPercap", y="lifeExp", size="pop", color="continent",
           log_x=True, animation_frame="year", range_y=[20, 90])

# Update traces after creation
fig = px.scatter(df, x="x", y="y")
fig.update_traces(marker=dict(size=6, opacity=0.7))

Practical Examples

1. Explore a DataFrame interactively

import plotly.express as px

df = px.data.tips()                       # built-in tips dataset

fig = px.scatter(df, x="total_bill", y="tip",
                 color="day", size="size", facet_col="sex",
                 hover_data=["time", "smoker"], title="Tips exploration")
fig.update_layout(template="plotly_white")
fig.show()

2. Distribution comparisons with a toggle of plot types

px.histogram(px.data.penguins(), x="flipper_length_mm", color="species",
             nbins=30, marginal="box", barmode="overlay", opacity=0.6)

3. Correlation heatmap with values

import pandas as pd

corr = px.data.iris().select_dtypes("number").corr()
px.imshow(corr, text_auto=".2f", aspect="auto",
          color_continuous_scale="RdBu_r", zmin=-1, zmax=1)

Common Patterns

# Reusable theme
px.defaults.template = "plotly_white"

# Combine px figure with extra go traces
fig = px.line(df, x="date", y="obs")
fig.add_trace(go.Scatter(x=df["date"], y=df["pred"], name="forecast"))

# Range slider for long time series
fig.update_layout(xaxis=dict(rangeslider=dict(visible=True), type="date"))

# Downloadable hover info
fig = px.scatter(df, x="x", y="y", custom_data=["id"])
fig.update_traces(hovertemplate="id=%{customdata[0]}<br>x=%{x:.2f}")

Best Practices

  • Start with px, drop to go only when needed — Express handles aggregation, colors, legends, facets.
  • Give every figure a title, axis titles, and a legend title.
  • Use template for consistent styling; set px.defaults.template once per notebook.
  • Export write_html for sharing; write_image (kaleido) for papers/slides.
  • Downsample before plotting huge data — Plotly renders every point in the DOM.
  • Keep animations and 100k+ point scatters lazy: filter, aggregate, or use scattergl.

Common Mistakes

Mistake Consequence Correct approach
fig.show() in a script Blocks / opens browser unexpectedly Use fig.write_html in scripts
write_image without kaleido ValueError: Image export requires kaleido pip install kaleido
Sorting done by Plotly alphabetically Wrong categorical order category_orders={"day": ["Mon", "Tue", ...]}
Mixing px traces into a go.Figure Color/legend weirdness Use px alone, or add go traces to px.fig via add_trace
Hover on huge data Laggy page Sample or aggregate first
Nested layout dicts overwritten Confusing updates Use the kwargs form (xaxis_title= not dict)

Troubleshooting

Symptom Cause Fix
Figure not shown in Jupyter Missing renderer fig.show(); check plotly.io.renderers.default
Static export fails kaleido not installed / version mismatch pip install -U kaleido
Dates on x-axis look wrong Not datetime dtype pd.to_datetime first
KeyError for a column Typo or column dropped Verify df.columns
Huge HTML file Full plotly.js embedded include_plotlyjs="cdn"

Official Documentation

Last Updated

2026-09-06 — Initial version.