Plotly creates interactive web-ready charts with hover, zoom, and pan. pip install plotly. import plotly.express as px, import plotly.graph_objects as go. Quick scatter: fig = px.scatter(df, x="col_a", y="col_b", color="category", size="value", hover_data=["label"]). Line chart: px.line(df, x="date", y="value", color="series"). Bar: px.bar(df, x="name", y="count", text_auto=True). Histogram: px.histogram(df, x="value", nbins=50, color="group"). Box: px.box(df, x="category", y="value", points="all"). Heatmap: px.imshow(matrix, color_continuous_scale="RdBu_r", zmin=-1, zmax=1). Map: px.choropleth(df, locations="iso_code", color="rate", scope="world"). Scatter 3D: px.scatter_3d(df, x, y, z, color="group"). Facet: px.scatter(df, facet_col="location", facet_row="year"). Animation: px.scatter(df, x, y, animation_frame="year", size="pop"). Template: fig.update_layout(template="plotly_dark"). Subplots: from plotly.subplots import make_subplots, fig = make_subplots(rows=2, cols=2), fig.add_trace(go.Scatter(...), row=1, col=1). Export: fig.write_html("chart.html"), fig.write_image("chart.png") — needs kaleido. Show: fig.show() in browser or notebook. Graph objects: go.Figure(data=[go.Candlestick(x=dates, open=o, high=h, low=l, close=c)]). Shared axes: make_subplots(shared_xaxes=True, shared_yaxes=True). Custom tooltip: go.Scatter(hovertemplate="%{x}: %{y:.2f}<extra></extra>"). Claude Code generates Plotly dashboards, animated time-series charts, geographic maps, financial candlestick charts, and correlation heatmaps.
CLAUDE.md for Plotly
## Plotly Stack
- Version: plotly >= 5.20
- Express: px.scatter/line/bar/histogram/box/heatmap/choropleth(df, x, y, color, ...)
- Objects: go.Figure(data=[go.Scatter/Bar/Heatmap/Candlestick/Surface(...)])
- Layout: fig.update_layout(title, xaxis_title, template, height, width)
- Subplots: make_subplots(rows, cols) → fig.add_trace(trace, row, col)
- Export: fig.write_html(path) | fig.write_image(path) [needs kaleido]
- Show: fig.show() | fig in Jupyter renders inline
- Color: color_continuous_scale | color_discrete_map | colorway
Plotly Visualization Pipeline
# viz/plotly_pipeline.py — interactive data visualization with Plotly
from __future__ import annotations
import numpy as np
import pandas as pd
from pathlib import Path
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.io as pio
# Default theme
pio.templates.default = "plotly_white"
# ── 1. Express charts ─────────────────────────────────────────────────────────
def scatter_chart(
df: pd.DataFrame,
x: str,
y: str,
color: str = None,
size: str = None,
hover_data: list[str] = None,
trendline: str = None, # "ols" | "lowess"
title: str = "",
labels: dict = None,
) -> go.Figure:
"""Interactive scatter plot with optional regression trendline."""
fig = px.scatter(
df, x=x, y=y, color=color, size=size,
hover_data=hover_data, trendline=trendline,
title=title, labels=labels or {},
template="plotly_white",
)
return fig
def line_chart(
df: pd.DataFrame,
x: str,
y: str | list[str] = None,
color: str = None,
markers: bool = False,
title: str = "",
y_title: str = None,
x_title: str = None,
) -> go.Figure:
"""Interactive line chart — supports multiple y columns."""
if isinstance(y, list):
# Melt for multiple lines
df_melt = df.melt(id_vars=[x], value_vars=y, var_name="series", value_name="value")
fig = px.line(df_melt, x=x, y="value", color="series", markers=markers, title=title)
else:
fig = px.line(df, x=x, y=y, color=color, markers=markers, title=title)
if y_title:
fig.update_yaxes(title_text=y_title)
if x_title:
fig.update_xaxes(title_text=x_title)
return fig
def bar_chart(
df: pd.DataFrame,
x: str,
y: str,
color: str = None,
barmode: str = "group", # "group" | "stack" | "relative"
horizontal: bool = False,
text_auto: bool = True,
title: str = "",
) -> go.Figure:
"""
Bar chart with optional grouping/stacking.
horizontal=True flips axes for long category names.
"""
kwargs = dict(x=x, y=y, color=color, barmode=barmode,
text_auto=text_auto, title=title)
if horizontal:
kwargs["x"], kwargs["y"] = kwargs["y"], kwargs["x"]
fig = px.bar(df, orientation="h", **kwargs)
else:
fig = px.bar(df, **kwargs)
return fig
def histogram_chart(
df: pd.DataFrame,
x: str,
color: str = None,
nbins: int = 50,
barmode: str = "overlay",
opacity: float = 0.7,
add_kde: bool = True,
title: str = "",
) -> go.Figure:
"""Histogram with optional KDE overlay."""
fig = px.histogram(
df, x=x, color=color, nbins=nbins,
barmode=barmode, opacity=opacity,
marginal="rug" if add_kde else None,
title=title,
)
return fig
# ── 2. Correlation and heatmaps ───────────────────────────────────────────────
def correlation_heatmap(
df: pd.DataFrame,
columns: list[str] = None,
method: str = "pearson",
colorscale: str = "RdBu_r",
title: str = "Correlation Matrix",
) -> go.Figure:
"""Interactive correlation heatmap with hover values."""
cols = columns or list(df.select_dtypes(include=np.number).columns)
corr = df[cols].corr(method=method)
fig = px.imshow(
corr,
color_continuous_scale=colorscale,
zmin=-1, zmax=1,
text_auto=".2f",
title=title,
aspect="auto",
)
fig.update_coloraxes(colorbar_title="r")
return fig
def matrix_heatmap(
matrix: np.ndarray,
x_labels: list[str] = None,
y_labels: list[str] = None,
colorscale: str = "Viridis",
title: str = "",
text_format: str = ".2f",
) -> go.Figure:
"""General matrix heatmap."""
fig = px.imshow(
matrix,
x=x_labels, y=y_labels,
color_continuous_scale=colorscale,
text_auto=text_format if text_format else False,
title=title,
)
return fig
# ── 3. Time series dashboard ──────────────────────────────────────────────────
def time_series_dashboard(
df: pd.DataFrame,
date_col: str,
value_col: str,
group_col: str = None,
rolling: int = None,
title: str = "Time Series Dashboard",
) -> go.Figure:
"""
Multi-panel time series dashboard:
- Row 1: Main time series with optional rolling mean
- Row 2: Daily/period changes
"""
fig = make_subplots(
rows=2, cols=1,
shared_xaxes=True,
subplot_titles=["Value Over Time", "Period Change"],
row_heights=[0.7, 0.3],
vertical_spacing=0.05,
)
if group_col:
groups = df[group_col].unique()
for group in groups:
gdf = df[df[group_col] == group].sort_values(date_col)
fig.add_trace(go.Scatter(
x=gdf[date_col], y=gdf[value_col],
mode="lines", name=str(group),
), row=1, col=1)
if rolling and len(gdf) >= rolling:
rm = gdf[value_col].rolling(rolling).mean()
fig.add_trace(go.Scatter(
x=gdf[date_col], y=rm,
mode="lines", name=f"{group} MA{rolling}",
line=dict(dash="dash"),
showlegend=False,
), row=1, col=1)
else:
df_sorted = df.sort_values(date_col)
fig.add_trace(go.Scatter(
x=df_sorted[date_col], y=df_sorted[value_col],
mode="lines+markers", name="Value",
), row=1, col=1)
pct_change = df_sorted[value_col].pct_change() * 100
colors = ["green" if v >= 0 else "red" for v in pct_change.fillna(0)]
fig.add_trace(go.Bar(
x=df_sorted[date_col], y=pct_change,
marker_color=colors, name="% Change",
), row=2, col=1)
fig.update_layout(title=title, hovermode="x unified", height=600)
return fig
def candlestick_chart(
df: pd.DataFrame,
date_col: str = "date",
open_col: str = "open",
high_col: str = "high",
low_col: str = "low",
close_col: str = "close",
volume_col: str = "volume",
title: str = "Price Chart",
) -> go.Figure:
"""Financial candlestick chart with optional volume bars."""
has_volume = volume_col in df.columns
if has_volume:
fig = make_subplots(
rows=2, cols=1, shared_xaxes=True,
row_heights=[0.8, 0.2], vertical_spacing=0.02,
)
else:
fig = make_subplots(rows=1, cols=1)
fig.add_trace(go.Candlestick(
x=df[date_col], open=df[open_col],
high=df[high_col], low=df[low_col],
close=df[close_col], name="OHLC",
), row=1, col=1)
if has_volume:
colors = ["green" if c >= o else "red"
for c, o in zip(df[close_col], df[open_col])]
fig.add_trace(go.Bar(
x=df[date_col], y=df[volume_col],
marker_color=colors, name="Volume",
), row=2, col=1)
fig.update_layout(
title=title, xaxis_rangeslider_visible=False,
height=500 + (150 if has_volume else 0),
)
return fig
# ── 4. Multi-panel dashboard ──────────────────────────────────────────────────
def multi_panel_dashboard(
charts: list[dict], # [{"fig": go.Figure, "title": str}, ...]
n_cols: int = 2,
title: str = "Dashboard",
height: int = 800,
) -> go.Figure:
"""
Combine multiple Plotly figures into a grid layout.
Each chart dict: {"fig": fig, "title": str}.
"""
n_rows = (len(charts) + n_cols - 1) // n_cols
subplot_titles = [c.get("title", "") for c in charts]
fig = make_subplots(
rows=n_rows, cols=n_cols,
subplot_titles=subplot_titles,
)
for i, chart_info in enumerate(charts):
row = i // n_cols + 1
col = i % n_cols + 1
for trace in chart_info["fig"].data:
fig.add_trace(trace, row=row, col=col)
fig.update_layout(title_text=title, height=height, showlegend=False)
return fig
# ── 5. 3D and animated charts ─────────────────────────────────────────────────
def scatter_3d(
df: pd.DataFrame,
x: str,
y: str,
z: str,
color: str = None,
size: str = None,
title: str = "3D Scatter",
) -> go.Figure:
"""Interactive 3D scatter plot."""
return px.scatter_3d(df, x=x, y=y, z=z, color=color, size=size, title=title)
def animated_scatter(
df: pd.DataFrame,
x: str,
y: str,
animation_frame: str,
size: str = None,
color: str = None,
title: str = "",
) -> go.Figure:
"""Animated scatter chart (Gapminder-style) with play button."""
return px.scatter(
df, x=x, y=y,
animation_frame=animation_frame,
animation_group=color,
size=size, color=color,
title=title,
size_max=60,
)
# ── 6. Export ─────────────────────────────────────────────────────────────────
def save_html(fig: go.Figure, path: str, include_plotlyjs: str = "cdn") -> str:
"""Save interactive HTML (cdn = small file; True = self-contained)."""
fig.write_html(path, include_plotlyjs=include_plotlyjs)
print(f"Saved HTML: {path}")
return path
def save_image(fig: go.Figure, path: str, scale: float = 2.0) -> str:
"""
Save static PNG/SVG/PDF (requires kaleido: pip install kaleido).
scale=2 for high-resolution (192 DPI equivalent).
"""
fig.write_image(path, scale=scale)
print(f"Saved image: {path}")
return path
# ── Demo ──────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("Plotly Visualization Demo")
print("="*50)
# Sample data
dates = pd.date_range("2023-01-01", periods=100, freq="D")
values = np.cumsum(np.random.randn(100)) + 100
df_ts = pd.DataFrame({"date": dates, "value": values})
df_ts["change"] = df_ts["value"].pct_change() * 100
# Time series
fig = time_series_dashboard(df_ts, "date", "value", rolling=7)
save_html(fig, "/tmp/timeseries.html")
# Correlation heatmap
df_num = pd.DataFrame(np.random.randn(200, 5), columns=list("ABCDE"))
fig_corr = correlation_heatmap(df_num)
save_html(fig_corr, "/tmp/correlation.html")
# Scatter with trendline
df_scatter = pd.DataFrame({
"x": np.random.randn(200),
"y": 2.5 * np.random.randn(200),
"cat": np.random.choice(["A", "B", "C"], 200),
})
fig_sc = scatter_chart(df_scatter, "x", "y", color="cat", trendline="ols")
save_html(fig_sc, "/tmp/scatter.html")
print("\nAll charts saved to /tmp/")
For the matplotlib alternative when needing publication-quality static figures with full LaTeX rendering, custom subplot geometries, or 3D surface plots for journals — matplotlib produces print-ready PDFs while Plotly’s default interactive HTML output with built-in zoom, pan, hover tooltips, and linked subplots eliminates the need to write custom event handlers for exploratory data analysis, and the fig.write_html single-file portable charts work in any browser without server infrastructure. For the Bokeh alternative when building streaming real-time dashboards or embedding charts in web applications without a Python server — Bokeh uses JavaScript callbacks for client-side updates while Plotly’s Dash framework provides a full Python-based reactive web application layer for interactive dashboards, and Plotly Express’s one-liner API for complex visualizations (faceted, animated, 3D) is significantly more concise than Bokeh’s explicit glyph-builder API for common EDA charts. The Claude Skills 360 bundle includes Plotly skill sets covering scatter, line, bar, histogram, and box charts, correlation heatmaps, time series dashboards, candlestick financial charts, 3D scatter, animated charts, make_subplots grid layouts, and HTML and image export. Start with the free tier to try interactive visualization code generation.