Claude Code for Matplotlib: Data Visualization in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for Matplotlib: Data Visualization in Python
AI

Claude Code for Matplotlib: Data Visualization in Python

Published: June 20, 2028
Read time: 5 min read
By: Claude Skills 360

Matplotlib is the standard Python plotting library. pip install matplotlib. Figure: import matplotlib.pyplot as plt; fig, ax = plt.subplots(figsize=(10,6)). plot: ax.plot(x, y, color="steelblue", linewidth=2, label="series"). scatter: ax.scatter(x, y, c=colors, s=sizes, alpha=0.7). bar: ax.bar(categories, values, color="steelblue"). barh: ax.barh(labels, values). hist: ax.hist(data, bins=30, edgecolor="white"). boxplot: ax.boxplot([data1, data2]). imshow: ax.imshow(matrix, cmap="viridis", aspect="auto"). labels: ax.set_xlabel("X"); ax.set_ylabel("Y"); ax.set_title("Title"). legend: ax.legend(loc="upper right"). xlim/ylim: ax.set_xlim(0, 10). grid: ax.grid(True, alpha=0.3). axhline/axvline: ax.axhline(y=0, color="gray", linestyle="--"). fill_between: ax.fill_between(x, y1, y2, alpha=0.3). errorbar: ax.errorbar(x, y, yerr=err, capsize=5). twinx: ax2 = ax.twinx(). subplots: fig, axes = plt.subplots(2, 3, figsize=(15,8)). tight_layout: fig.tight_layout(). colorbar: plt.colorbar(im, ax=ax). annotate: ax.annotate("peak", xy=(x,y), xytext=(x+1,y+5), arrowprops=dict(arrowstyle="->")). style: plt.style.use("seaborn-v0_8-whitegrid"). savefig: fig.savefig("plot.png", dpi=150, bbox_inches="tight"). show: plt.show(). close: plt.close(fig). Claude Code generates Matplotlib dashboards, statistical charts, heatmaps, and multi-panel figures.

CLAUDE.md for Matplotlib

## Matplotlib Stack
- Version: matplotlib >= 3.8 | pip install matplotlib
- Figure: fig, ax = plt.subplots(figsize=(10, 6)) | fig, axes = plt.subplots(r, c)
- Plot: ax.plot/scatter/bar/hist/boxplot/imshow(data, **style_kwargs)
- Style: ax.set_xlabel/ylabel/title | ax.legend() | ax.grid(True, alpha=0.3)
- Export: fig.savefig("out.png", dpi=150, bbox_inches="tight") | plt.close(fig)
- Style preset: plt.style.use("seaborn-v0_8-whitegrid")

Matplotlib Visualization Pipeline

# app/charts.py — Matplotlib Figure, Axes, plot types, subplots, export, style
from __future__ import annotations

from pathlib import Path
from typing import Any, Sequence

import matplotlib
matplotlib.use("Agg")  # non-interactive backend — safe for servers and CI
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np

# ─── Global style ────────────────────────────────────────────────────────────
plt.style.use("seaborn-v0_8-whitegrid")
COLORS = plt.rcParams["axes.prop_cycle"].by_key()["color"]


# ─────────────────────────────────────────────────────────────────────────────
# 1. Figure helpers
# ─────────────────────────────────────────────────────────────────────────────

def make_figure(
    nrows: int = 1,
    ncols: int = 1,
    figsize: tuple[float, float] | None = None,
    dpi: int = 100,
) -> tuple[plt.Figure, Any]:
    """
    Create a Figure and Axes (or array of Axes).

    Example:
        fig, ax = make_figure()
        fig, axes = make_figure(2, 3, figsize=(15, 8))
    """
    if figsize is None:
        figsize = (6 * ncols, 4 * nrows)
    return plt.subplots(nrows, ncols, figsize=figsize, dpi=dpi)


def apply_style(
    ax: plt.Axes,
    title: str = "",
    xlabel: str = "",
    ylabel: str = "",
    legend: bool = True,
    grid_alpha: float = 0.3,
    tight: bool = True,
) -> plt.Axes:
    """
    Apply common axis styling.

    Example:
        apply_style(ax, title="Monthly Revenue", xlabel="Month", ylabel="$USD")
    """
    if title:  ax.set_title(title, pad=10, fontsize=13, fontweight="bold")
    if xlabel: ax.set_xlabel(xlabel, fontsize=11)
    if ylabel: ax.set_ylabel(ylabel, fontsize=11)
    if legend: ax.legend(framealpha=0.9)
    ax.grid(True, alpha=grid_alpha, linestyle="--")
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    return ax


def save(
    fig: plt.Figure,
    path: str | Path,
    dpi: int = 150,
    close: bool = True,
) -> Path:
    """
    Save figure to disk and optionally close it.

    Example:
        save(fig, "reports/revenue.png")
        save(fig, "docs/chart.pdf")   # PDF vector
    """
    p = Path(path)
    p.parent.mkdir(parents=True, exist_ok=True)
    fig.tight_layout()
    fig.savefig(p, dpi=dpi, bbox_inches="tight")
    if close:
        plt.close(fig)
    return p


def to_bytes(fig: plt.Figure, fmt: str = "png", dpi: int = 150) -> bytes:
    """
    Render figure to bytes (for HTTP responses or embeddings).

    Example:
        img_bytes = to_bytes(fig, fmt="png")
        return Response(img_bytes, content_type="image/png")
    """
    import io
    buf = io.BytesIO()
    fig.tight_layout()
    fig.savefig(buf, format=fmt, dpi=dpi, bbox_inches="tight")
    plt.close(fig)
    buf.seek(0)
    return buf.read()


# ─────────────────────────────────────────────────────────────────────────────
# 2. Line and area charts
# ─────────────────────────────────────────────────────────────────────────────

def line_chart(
    x: Sequence,
    y: Sequence | list[Sequence],
    labels: list[str] | None = None,
    title: str = "",
    xlabel: str = "",
    ylabel: str = "",
    markers: bool = False,
    figsize: tuple = (10, 5),
) -> plt.Figure:
    """
    Line chart supporting single or multiple series.

    Example:
        fig = line_chart(months, revenue, title="Monthly Revenue", ylabel="$")
        fig = line_chart(months, [revenue_a, revenue_b], labels=["A", "B"])
    """
    fig, ax = make_figure(figsize=figsize)
    series_list = [y] if not (isinstance(y[0], (list, np.ndarray)) and hasattr(y[0], "__iter__")) else y
    for i, series in enumerate(series_list):
        label = (labels or [])[i] if labels and i < len(labels) else f"series {i+1}"
        marker = "o" if markers else None
        ax.plot(x, series, label=label, color=COLORS[i % len(COLORS)],
                linewidth=2, marker=marker, markersize=4)
    apply_style(ax, title=title, xlabel=xlabel, ylabel=ylabel)
    return fig


def area_chart(
    x: Sequence,
    y_lower: Sequence,
    y_upper: Sequence,
    y_mid: Sequence | None = None,
    label: str = "",
    title: str = "",
    color: str = "steelblue",
    figsize: tuple = (10, 5),
) -> plt.Figure:
    """
    Area / confidence-band chart.

    Example:
        fig = area_chart(dates, lower_bound, upper_bound, y_mid=forecast,
                         label="95% CI", title="Forecast")
    """
    fig, ax = make_figure(figsize=figsize)
    ax.fill_between(x, y_lower, y_upper, color=color, alpha=0.2, label=label)
    if y_mid is not None:
        ax.plot(x, y_mid, color=color, linewidth=2, label="mean")
    apply_style(ax, title=title)
    return fig


# ─────────────────────────────────────────────────────────────────────────────
# 3. Bar charts
# ─────────────────────────────────────────────────────────────────────────────

def bar_chart(
    categories: Sequence[str],
    values: Sequence[float],
    title: str = "",
    xlabel: str = "",
    ylabel: str = "",
    color: str = "steelblue",
    horizontal: bool = False,
    annotate: bool = True,
    figsize: tuple = (10, 5),
) -> plt.Figure:
    """
    Vertical or horizontal bar chart with optional value labels.

    Example:
        fig = bar_chart(countries, revenue, title="Revenue by Country", horizontal=True)
    """
    fig, ax = make_figure(figsize=figsize)
    cats = list(categories)
    vals = list(values)

    if horizontal:
        bars = ax.barh(cats, vals, color=color, edgecolor="white")
        if annotate:
            for bar in bars:
                w = bar.get_width()
                ax.text(w * 1.01, bar.get_y() + bar.get_height() / 2,
                        f"{w:,.0f}", va="center", fontsize=9)
    else:
        bars = ax.bar(cats, vals, color=color, edgecolor="white")
        ax.tick_params(axis="x", rotation=45)
        if annotate:
            for bar in bars:
                h = bar.get_height()
                ax.text(bar.get_x() + bar.get_width() / 2, h * 1.01,
                        f"{h:,.0f}", ha="center", va="bottom", fontsize=9)

    apply_style(ax, title=title, xlabel=xlabel, ylabel=ylabel, legend=False)
    return fig


def grouped_bar_chart(
    categories: Sequence[str],
    groups: dict[str, Sequence[float]],
    title: str = "",
    xlabel: str = "",
    ylabel: str = "",
    figsize: tuple = (12, 5),
) -> plt.Figure:
    """
    Grouped bar chart.

    Example:
        fig = grouped_bar_chart(["Q1","Q2","Q3","Q4"],
                                 {"Product A": [120,140,130,160],
                                  "Product B": [90,110,95,130]})
    """
    fig, ax = make_figure(figsize=figsize)
    n_groups = len(categories)
    n_bars   = len(groups)
    width    = 0.8 / n_bars
    x        = np.arange(n_groups)

    for i, (name, vals) in enumerate(groups.items()):
        offset = (i - n_bars / 2 + 0.5) * width
        ax.bar(x + offset, vals, width, label=name,
               color=COLORS[i % len(COLORS)], edgecolor="white")

    ax.set_xticks(x)
    ax.set_xticklabels(categories)
    apply_style(ax, title=title, xlabel=xlabel, ylabel=ylabel)
    return fig


# ─────────────────────────────────────────────────────────────────────────────
# 4. Distribution charts
# ─────────────────────────────────────────────────────────────────────────────

def histogram(
    data: np.ndarray,
    bins: int = 30,
    title: str = "",
    xlabel: str = "",
    color: str = "steelblue",
    density: bool = False,
    kde: bool = False,
    figsize: tuple = (8, 5),
) -> plt.Figure:
    """
    Histogram with optional KDE overlay.

    Example:
        fig = histogram(scores, bins=20, title="Score Distribution", kde=True)
    """
    fig, ax = make_figure(figsize=figsize)
    ax.hist(data, bins=bins, density=density, color=color, edgecolor="white",
            alpha=0.75, label="data")
    if kde:
        from scipy.stats import gaussian_kde  # type: ignore[import]
        xr = np.linspace(data.min(), data.max(), 300)
        kde_fn = gaussian_kde(data)
        ax2 = ax.twinx()
        ax2.plot(xr, kde_fn(xr), color="darkorange", linewidth=2, label="KDE")
        ax2.set_ylabel("Density")
    apply_style(ax, title=title, xlabel=xlabel, ylabel="Frequency")
    return fig


def scatter_plot(
    x: np.ndarray,
    y: np.ndarray,
    c: np.ndarray | None = None,
    labels: np.ndarray | None = None,
    title: str = "",
    xlabel: str = "",
    ylabel: str = "",
    cmap: str = "viridis",
    alpha: float = 0.7,
    figsize: tuple = (8, 6),
) -> plt.Figure:
    """
    Scatter plot with optional color mapping and point labels.

    Example:
        fig = scatter_plot(X_2d[:, 0], X_2d[:, 1], c=label_ids,
                           title="PCA projection", cmap="tab10")
    """
    fig, ax = make_figure(figsize=figsize)
    sc = ax.scatter(x, y, c=c, cmap=cmap, alpha=alpha, edgecolors="none", s=30)
    if c is not None:
        plt.colorbar(sc, ax=ax)
    if labels is not None:
        for xi, yi, lab in zip(x, y, labels):
            ax.annotate(str(lab), (xi, yi), fontsize=7, alpha=0.8)
    apply_style(ax, title=title, xlabel=xlabel, ylabel=ylabel, legend=False)
    return fig


# ─────────────────────────────────────────────────────────────────────────────
# 5. Heatmap
# ─────────────────────────────────────────────────────────────────────────────

def heatmap(
    matrix: np.ndarray,
    row_labels: list[str] | None = None,
    col_labels: list[str] | None = None,
    title: str = "",
    cmap: str = "Blues",
    annotate: bool = True,
    fmt: str = ".2f",
    figsize: tuple | None = None,
) -> plt.Figure:
    """
    Annotated heatmap (confusion matrix, correlation matrix, etc.).

    Example:
        fig = heatmap(corr_matrix, row_labels=features, col_labels=features,
                      title="Correlation Matrix", fmt=".2f")
    """
    n, m = matrix.shape
    if figsize is None:
        figsize = (max(6, m * 0.8), max(5, n * 0.6))
    fig, ax = make_figure(figsize=figsize)

    im = ax.imshow(matrix, cmap=cmap, aspect="auto")
    plt.colorbar(im, ax=ax)

    if row_labels:
        ax.set_yticks(range(len(row_labels)))
        ax.set_yticklabels(row_labels, fontsize=9)
    if col_labels:
        ax.set_xticks(range(len(col_labels)))
        ax.set_xticklabels(col_labels, rotation=45, ha="right", fontsize=9)

    if annotate:
        vmin, vmax = matrix.min(), matrix.max()
        threshold  = (vmin + vmax) / 2
        for i in range(n):
            for j in range(m):
                color = "white" if matrix[i, j] > threshold else "black"
                ax.text(j, i, format(matrix[i, j], fmt),
                        ha="center", va="center", color=color, fontsize=8)

    if title:
        ax.set_title(title, pad=10, fontsize=13, fontweight="bold")
    fig.tight_layout()
    return fig


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    import tempfile, os
    rng = np.random.default_rng(42)

    print("=== Matplotlib demo (saving to temp PNG files) ===")

    # Line chart
    months = [f"2024-{m:02d}" for m in range(1, 13)]
    rev_a  = rng.normal(100, 15, 12).cumsum()
    rev_b  = rng.normal(80,  10, 12).cumsum()
    fig = line_chart(months, [rev_a, rev_b], labels=["Product A","Product B"],
                     title="Monthly Revenue", ylabel="$K")
    with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tf:
        p = save(fig, tf.name)
        print(f"  line_chart saved: {os.path.getsize(tf.name):,} bytes")
        os.unlink(tf.name)

    # Bar chart
    countries = ["US","UK","DE","FR","JP","AU"]
    revenue   = [450, 280, 310, 190, 260, 140]
    fig = bar_chart(countries, revenue, title="Revenue by Country",
                    ylabel="$K", horizontal=True)
    with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tf:
        p = save(fig, tf.name)
        print(f"  bar_chart saved:  {os.path.getsize(tf.name):,} bytes")
        os.unlink(tf.name)

    # Heatmap
    corr = np.corrcoef(rng.normal(0, 1, (6, 50)))
    feats = ["age","income","score","tenure","clicks","orders"]
    fig = heatmap(corr, feats, feats, title="Feature Correlation")
    with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tf:
        p = save(fig, tf.name)
        print(f"  heatmap saved:    {os.path.getsize(tf.name):,} bytes")
        os.unlink(tf.name)

    # Scatter
    X = rng.normal(0, 1, (200, 2))
    labels = (X[:, 0] + X[:, 1] > 0).astype(int)
    fig = scatter_plot(X[:, 0], X[:, 1], c=labels, title="2-Class Scatter", cmap="bwr")
    with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tf:
        p = save(fig, tf.name)
        print(f"  scatter saved:    {os.path.getsize(tf.name):,} bytes")
        os.unlink(tf.name)

    print("=== done ===")

For the plotly alternative — plotly generates interactive HTML charts with zoom, pan, hover tooltips, and can be embedded in Dash apps or Jupyter notebooks; matplotlib generates static images (PNG, SVG, PDF) suitable for reports, papers, and server-side rendering without a browser — use plotly for dashboards and exploratory analysis where interactivity adds value, matplotlib for publication-quality static figures, batch report generation, and CI-generated images. For the seaborn alternative — seaborn is a statistical visualization library built on top of matplotlib, providing high-level functions for distributions, regressions, facet grids, and categorical plots with sensible defaults; matplotlib is lower-level with complete control over every visual element — use seaborn for statistical charts (pairplot, boxplot, violin, lmplot) with minimal code, matplotlib when you need pixel-level control over layouts, custom color cycles, or multi-panel figures that don’t fit seaborn’s API. The Claude Skills 360 bundle includes Matplotlib skill sets covering make_figure()/apply_style()/save()/to_bytes() figure helpers, line_chart()/area_chart() time series, bar_chart()/grouped_bar_chart() categorical, histogram()/scatter_plot() distribution, and heatmap() annotated grid. Start with the free tier to try data visualization and chart generation code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free