AXe Skills HubSearch /

← All skills

data-visualization

AXe First-party 

Reference: full SKILL.md

Below is the complete skill definition this hub loads when the skill is triggered — what the agent sees as its instructions, verbatim and unabridged.

Data Visualization

Role

You are an elite data visualization architect. You design clear, accurate, and

accessible charts and dashboards using matplotlib, plotly, and seaborn, selecting

the right visualization for each data story.

Part 1: Chart Selection Guide

Data TypeQuestionChart
CategoricalCompare valuesBar chart (vertical/horizontal)
CategoricalShow compositionStacked bar, pie (< 5 categories)
TemporalTrend over timeLine chart
TemporalVolume over timeArea chart
DistributionSingle variableHistogram, box plot, violin
DistributionTwo variablesScatter plot
CorrelationRelationshipScatter + regression line
ComparisonMultiple seriesGrouped bar, small multiples
Part-to-wholeProportionsPie (< 5), treemap, waterfall
GeospatialLocation dataChoropleth, bubble map
FlowProcess/connectionsSankey, network graph
HierarchicalNested categoriesTreemap, sunburst

Part 2: Matplotlib Production Charts

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import numpy as np
from datetime import datetime, timedelta

def setup_style():
    """Production-quality matplotlib style."""
    plt.rcParams.update({
        "figure.figsize": (12, 6),
        "figure.dpi": 150,
        "font.family": "sans-serif",
        "font.size": 11,
        "axes.titlesize": 14,
        "axes.titleweight": "bold",
        "axes.labelsize": 12,
        "axes.spines.top": False,
        "axes.spines.right": False,
        "axes.grid": True,
        "grid.alpha": 0.3,
        "legend.framealpha": 0.9,
    })

def time_series_chart(dates, values, title="Metric Over Time", ylabel="Value"):
    setup_style()
    fig, ax = plt.subplots()

    ax.plot(dates, values, color="#2196F3", linewidth=2, marker="o", markersize=4)
    ax.fill_between(dates, values, alpha=0.1, color="#2196F3")

    ax.set_title(title)
    ax.set_ylabel(ylabel)
    ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %d"))
    ax.xaxis.set_major_locator(mdates.WeekdayLocator(interval=1))
    fig.autofmt_xdate()

    plt.tight_layout()
    return fig

def multi_bar_chart(categories, series_data: dict, title="Comparison"):
    setup_style()
    fig, ax = plt.subplots()

    x = np.arange(len(categories))
    width = 0.8 / len(series_data)
    colors = ["#2196F3", "#FF9800", "#4CAF50", "#F44336"]

    for i, (label, values) in enumerate(series_data.items()):
        offset = (i - len(series_data) / 2 + 0.5) * width
        bars = ax.bar(x + offset, values, width, label=label, color=colors[i % len(colors)])
        ax.bar_label(bars, padding=3, fontsize=9)

    ax.set_title(title)
    ax.set_xticks(x)
    ax.set_xticklabels(categories)
    ax.legend()
    plt.tight_layout()
    return fig

# Export
fig = time_series_chart(dates, values, "API Requests per Day")
fig.savefig("chart.png", dpi=300, bbox_inches="tight")
fig.savefig("chart.svg", format="svg", bbox_inches="tight")
fig.savefig("chart.pdf", format="pdf", bbox_inches="tight")
plt.close(fig)

Part 3: Plotly Interactive Charts

import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd

def interactive_dashboard(df: pd.DataFrame):
    """Create a multi-chart interactive dashboard."""
    fig = make_subplots(
        rows=2, cols=2,
        subplot_titles=("Requests Over Time", "Status Distribution", "Latency Histogram", "Top Endpoints"),
        specs=[[{"type": "scatter"}, {"type": "pie"}],
               [{"type": "histogram"}, {"type": "bar"}]],
    )

    # Line chart
    fig.add_trace(
        go.Scatter(x=df["timestamp"], y=df["requests"], mode="lines+markers", name="Requests"),
        row=1, col=1,
    )

    # Pie chart
    status_counts = df["status"].value_counts()
    fig.add_trace(
        go.Pie(labels=status_counts.index, values=status_counts.values, name="Status"),
        row=1, col=2,
    )

    # Histogram
    fig.add_trace(
        go.Histogram(x=df["latency_ms"], nbinsx=50, name="Latency"),
        row=2, col=1,
    )

    # Horizontal bar
    top_endpoints = df["endpoint"].value_counts().head(10)
    fig.add_trace(
        go.Bar(x=top_endpoints.values, y=top_endpoints.index, orientation="h", name="Hits"),
        row=2, col=2,
    )

    fig.update_layout(
        height=800,
        title_text="API Dashboard",
        showlegend=False,
        template="plotly_dark",
    )

    return fig

def realtime_chart():
    """Plotly chart with live updates via Dash."""
    from dash import Dash, dcc, html, callback, Output, Input
    import random

    app = Dash(__name__)
    app.layout = html.Div([
        dcc.Graph(id="live-graph"),
        dcc.Interval(id="interval", interval=1000, n_intervals=0),
    ])

    data = {"x": [], "y": []}

    @callback(Output("live-graph", "figure"), Input("interval", "n_intervals"))
    def update_graph(n):
        data["x"].append(n)
        data["y"].append(random.randint(50, 200))
        fig = go.Figure(go.Scatter(x=data["x"][-50:], y=data["y"][-50:], mode="lines"))
        fig.update_layout(title="Live Requests/sec", template="plotly_dark")
        return fig

    return app

# Export Plotly to static images
# pip install kaleido
fig = px.scatter(df, x="latency", y="throughput", color="service")
fig.write_image("scatter.png", scale=2)
fig.write_html("interactive.html")

Part 4: Seaborn Statistical Charts

import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd

def statistical_overview(df: pd.DataFrame):
    """Publication-quality statistical visualizations."""
    sns.set_theme(style="whitegrid", palette="husl", font_scale=1.1)

    # Distribution plot
    fig, axes = plt.subplots(1, 3, figsize=(18, 5))

    # Violin plot — distribution comparison
    sns.violinplot(data=df, x="service", y="latency_ms", ax=axes[0], inner="box")
    axes[0].set_title("Latency Distribution by Service")

    # Heatmap — correlation matrix
    corr = df[["latency_ms", "cpu", "memory", "requests"]].corr()
    sns.heatmap(corr, annot=True, cmap="RdYlBu_r", center=0, ax=axes[1], fmt=".2f")
    axes[1].set_title("Correlation Matrix")

    # Regression plot
    sns.regplot(data=df, x="cpu", y="latency_ms", ax=axes[2],
                scatter_kws={"alpha": 0.3}, line_kws={"color": "red"})
    axes[2].set_title("CPU vs Latency")

    plt.tight_layout()
    return fig

def pairplot(df: pd.DataFrame):
    """Pairwise relationship plot."""
    g = sns.pairplot(
        df[["latency_ms", "cpu", "memory", "requests", "service"]],
        hue="service",
        diag_kind="kde",
        plot_kws={"alpha": 0.5},
    )
    g.fig.suptitle("Metric Relationships", y=1.02)
    return g.fig

Part 5: Dashboard Layout Patterns

def multi_panel_dashboard(data: dict):
    """Professional multi-panel dashboard with matplotlib."""
    fig = plt.figure(figsize=(20, 12))
    gs = fig.add_gridspec(3, 4, hspace=0.35, wspace=0.3)

    # Large time series (spans 2 columns)
    ax1 = fig.add_subplot(gs[0, :2])
    ax1.plot(data["dates"], data["requests"], color="#2196F3", linewidth=2)
    ax1.set_title("Requests Over Time")
    ax1.set_ylabel("Requests/min")

    # Latency distribution
    ax2 = fig.add_subplot(gs[0, 2:])
    ax2.hist(data["latencies"], bins=50, color="#FF9800", edgecolor="white")
    ax2.axvline(np.median(data["latencies"]), color="red", linestyle="--", label="Median")
    ax2.set_title("Latency Distribution")
    ax2.legend()

    # Status code breakdown
    ax3 = fig.add_subplot(gs[1, 0])
    colors = {"2xx": "#4CAF50", "3xx": "#2196F3", "4xx": "#FF9800", "5xx": "#F44336"}
    ax3.pie(data["status_counts"].values(), labels=data["status_counts"].keys(),
            colors=[colors[k] for k in data["status_counts"]], autopct="%1.1f%%")
    ax3.set_title("Status Codes")

    # KPI cards (simulated with text)
    ax4 = fig.add_subplot(gs[1, 1])
    ax4.axis("off")
    ax4.text(0.5, 0.7, "99.9%", fontsize=36, ha="center", va="center", fontweight="bold", color="#4CAF50")
    ax4.text(0.5, 0.3, "Uptime", fontsize=14, ha="center", va="center", color="gray")

    ax5 = fig.add_subplot(gs[1, 2])
    ax5.axis("off")
    ax5.text(0.5, 0.7, "45ms", fontsize=36, ha="center", va="center", fontweight="bold", color="#2196F3")
    ax5.text(0.5, 0.3, "P95 Latency", fontsize=14, ha="center", va="center", color="gray")

    ax6 = fig.add_subplot(gs[1, 3])
    ax6.axis("off")
    ax6.text(0.5, 0.7, "1.2K", fontsize=36, ha="center", va="center", fontweight="bold", color="#FF9800")
    ax6.text(0.5, 0.3, "Req/sec", fontsize=14, ha="center", va="center", color="gray")

    # Error rate over time (bottom spanning all columns)
    ax7 = fig.add_subplot(gs[2, :])
    ax7.fill_between(data["dates"], data["error_rates"], alpha=0.3, color="#F44336")
    ax7.plot(data["dates"], data["error_rates"], color="#F44336", linewidth=2)
    ax7.axhline(y=0.1, color="red", linestyle="--", alpha=0.5, label="SLO Threshold")
    ax7.set_title("Error Rate (%)")
    ax7.legend()

    fig.suptitle("System Dashboard", fontsize=18, fontweight="bold", y=0.98)
    return fig

Part 6: Accessibility in Charts

def accessible_chart(dates, series: dict[str, list]):
    """Chart designed for accessibility."""
    setup_style()
    fig, ax = plt.subplots()

    # Use colorblind-safe palette
    colors = ["#0072B2", "#D55E00", "#009E73", "#CC79A7", "#F0E442"]
    # Use distinct line styles for colorblind users
    styles = ["-", "--", "-.", ":", (0, (3, 1, 1, 1))]
    markers = ["o", "s", "^", "D", "v"]

    for i, (label, values) in enumerate(series.items()):
        ax.plot(dates, values,
                color=colors[i % len(colors)],
                linestyle=styles[i % len(styles)],
                marker=markers[i % len(markers)],
                markersize=6,
                linewidth=2,
                label=label)

    ax.set_title("Service Metrics Comparison")
    ax.set_ylabel("Requests per Second")
    ax.legend(loc="upper left", fontsize=11)

    # High contrast grid
    ax.grid(True, alpha=0.4, linewidth=0.8)

    # Ensure sufficient font sizes
    for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
                 ax.get_xticklabels() + ax.get_yticklabels()):
        item.set_fontsize(12)

    plt.tight_layout()
    return fig

# Alt text generation
def generate_alt_text(chart_type: str, data_summary: dict) -> str:
    """Generate descriptive alt text for charts."""
    return (
        f"{chart_type} showing {data_summary['metric']} from "
        f"{data_summary['start_date']} to {data_summary['end_date']}. "
        f"Values range from {data_summary['min']} to {data_summary['max']}, "
        f"with a mean of {data_summary['mean']:.1f}. "
        f"{'An upward trend is visible.' if data_summary.get('trend') == 'up' else ''}"
    )

Part 7: Real-Time Updating Charts

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from collections import deque
import random

def live_metrics_chart():
    """Real-time updating matplotlib chart."""
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
    window = 100

    x_data = deque(maxlen=window)
    y_requests = deque(maxlen=window)
    y_latency = deque(maxlen=window)

    line1, = ax1.plot([], [], color="#2196F3", linewidth=2)
    line2, = ax2.plot([], [], color="#FF9800", linewidth=2)

    ax1.set_title("Requests/sec (Live)")
    ax1.set_ylim(0, 300)
    ax2.set_title("Latency ms (Live)")
    ax2.set_ylim(0, 200)

    def update(frame):
        x_data.append(frame)
        y_requests.append(150 + random.gauss(0, 30))
        y_latency.append(50 + random.gauss(0, 15))

        line1.set_data(list(x_data), list(y_requests))
        line2.set_data(list(x_data), list(y_latency))

        for ax in [ax1, ax2]:
            ax.set_xlim(max(0, frame - window), frame + 5)

        return line1, line2

    ani = FuncAnimation(fig, update, interval=100, blit=True)
    plt.tight_layout()
    plt.show()
    return ani

Part 8: Chart Export & Embedding

import io
import base64

def chart_to_base64(fig) -> str:
    """Convert matplotlib figure to base64 for embedding in HTML/email."""
    buf = io.BytesIO()
    fig.savefig(buf, format="png", dpi=150, bbox_inches="tight")
    buf.seek(0)
    b64 = base64.b64encode(buf.read()).decode("utf-8")
    plt.close(fig)
    return f"data:image/png;base64,{b64}"

def embed_in_html(charts: list[str], title: str = "Report") -> str:
    """Create standalone HTML report with embedded charts."""
    images_html = "\n".join(
        f'<div class="chart"><img src="{b64}" alt="Chart {i+1}"></div>'
        for i, b64 in enumerate(charts)
    )
    return f"""<!DOCTYPE html>
<html>
<head>
    <title>{title}</title>
    <style>
        body {{ font-family: sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }}
        .chart {{ margin: 20px 0; text-align: center; }}
        .chart img {{ max-width: 100%; border: 1px solid #ddd; border-radius: 8px; }}
        h1 {{ color: #333; }}
    </style>
</head>
<body>
    <h1>{title}</h1>
    {images_html}
</body>
</html>"""

# Usage
fig1 = time_series_chart(dates, values, "Requests")
fig2 = multi_bar_chart(categories, data, "Comparison")
b64_charts = [chart_to_base64(fig1), chart_to_base64(fig2)]
html = embed_in_html(b64_charts, "Weekly Report")
with open("report.html", "w") as f:
    f.write(html)

AXE MCP Server Integration

Every skill in the AXE Skills Hub runs with access to the AXE MCP Server — giving it the full fleet intelligence toolkit automatically. No setup required; tools are available in any AXE-powered session.

Core Tools Available

CategoryToolsUse Case
Memoryread_memory, write_memory, list_memoryPersist context across sessions
Webweb_search, web_fetchLive data, docs, research
File Opsread_file, write_fileRead/write any local file
Fleetfleet_ssh, axe_pushRun commands on JL2/JL3/JL4, send notifications
AI Modelsquery_team_channel, get_partner_stateCross-agent coordination
Dataqdrant_search, qdrant_storeSemantic memory & vector search
Pipelinehydra_addAdd high-quality outputs to Edge training
Skillshub_list_skills, hub_get_skill, hub_search_skills, hub_get_registry, hub_skill_metadataChain skills together
Secretsget_secretRetrieve API keys securely

Quick Start

# In any AXE session, tools are pre-loaded. Example chaining:

# 1. Search for context
results = qdrant_search("user query here", collection="axe_persistent_memory")

# 2. Fetch live data if needed
content = web_fetch("https://docs.example.com/api")

# 3. Write result to memory for next session
write_memory("shared/last_result.md", output)

# 4. Log quality output to Edge training pipeline
hydra_add(prompt=user_query, response=output, score=0.9, source="skill-name")

Edge Training Integration

High-quality skill outputs are automatically eligible for Edge model training via hydra_add. When a response scores ≥0.85 in evals, pipe it to the Hydra pipeline to compound Edge's knowledge. This is how skills make Edge smarter over time.

# After generating a high-quality response:
hydra_add(
    prompt=user_input,
    response=final_output,
    score=0.9,          # eval score
    source="skill-name" # tracks provenance
)

Metadata

Category
Data
Tier
community
Version
1.0.0
License
MIT
Path
skills/data-visualization/SKILL.md

Use with an agent

Fetch this skill’s definition over the open API — no key required.

curl -s /v1/skills/data-visualization

View source ↗