Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Interactive data visualization with Plotly, ECharts, and D3
.claude/skills/brycewang-stanford-interactive-viz-guide/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 138% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 93% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 76% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 135% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 118% | 0% |
Create interactive, publication-ready visualizations using Plotly, ECharts, Altair, and Bokeh for academic papers, presentations, and supplementary materials.
| Scenario | Static | Interactive | |----------|--------|-------------| | Journal PDF figure | Preferred | Not supported | | Supplementary materials | Optional | Excellent | | Conference poster (digital) | Common | Increasingly popular | | Presentation slides | Standard | Engaging | | Online appendix / project website | Limited | Ideal | | Exploratory data analysis | Quick | Detailed exploration |
Plotly produces interactive HTML charts with hover tooltips, zoom, pan, and export capabilities.
pythonimport plotly.express as px import pandas as pd # Example: visualize paper citations vs. year df = pd.DataFrame({ "title": ["Paper A", "Paper B", "Paper C", "Paper D", "Paper E"], "year": [2019, 2020, 2021, 2022, 2023], "citations": [150, 320, 89, 450, 210], "field": ["NLP", "CV", "NLP", "RL", "CV"], "venue": ["ACL", "CVPR", "EMNLP", "NeurIPS", "ICCV"] }) fig = px.scatter( df, x="year", y="citations", color="field", size="citations", hover_data=["title", "venue"], title="Citation Counts by Year and Field", labels={"citations": "Citation Count", "year": "Publication Year"} ) fig.update_layout( template="plotly_white", font=dict(size=14), width=800, height=500 ) fig.write_html("citations_interactive.html") fig.show()
pythonimport plotly.graph_objects as go methods = ["Baseline", "Method A", "Method B", "Ours"] accuracy = [82.1, 85.3, 87.0, 89.4] f1_score = [79.8, 83.1, 85.2, 87.9] fig = go.Figure(data=[ go.Bar(name="Accuracy", x=methods, y=accuracy, text=[f"{v}%" for v in accuracy], textposition="auto"), go.Bar(name="F1 Score", x=methods, y=f1_score, text=[f"{v}%" for v in f1_score], textposition="auto") ]) fig.update_layout( barmode="group", title="Model Performance Comparison", yaxis_title="Score (%)", yaxis_range=[70, 95], template="plotly_white" ) fig.write_html("comparison.html")
pythonimport plotly.figure_factory as ff import numpy as np z = [[85, 5, 3, 7], [4, 90, 2, 4], [6, 3, 88, 3], [5, 2, 7, 86]] labels = ["Class A", "Class B", "Class C", "Class D"] fig = ff.create_annotated_heatmap( z, x=labels, y=labels, colorscale="Blues", showscale=True ) fig.update_layout( title="Confusion Matrix", xaxis_title="Predicted", yaxis_title="Actual" ) fig.write_html("confusion_matrix.html")
Altair uses Vega-Lite grammar for concise, declarative visualization.
pythonimport altair as alt import pandas as pd # Interactive scatter with selection df = pd.DataFrame({ "x": range(100), "y": [v**2 + 10 for v in range(100)], "category": ["A" if i % 3 == 0 else "B" if i % 3 == 1 else "C" for i in range(100)] }) selection = alt.selection_point(fields=["category"], bind="legend") chart = alt.Chart(df).mark_circle(size=60).encode( x="x:Q", y="y:Q", color="category:N", opacity=alt.condition(selection, alt.value(1), alt.value(0.2)), tooltip=["x", "y", "category"] ).add_params( selection ).properties( width=600, height=400, title="Interactive Scatter with Legend Selection" ).interactive() # Enable zoom/pan chart.save("altair_scatter.html")
Apache ECharts is a powerful JavaScript charting library ideal for web dashboards and complex visualizations.
html<!DOCTYPE html> <html> <head> <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> </head> <body> <div id="chart" style="width: 800px; height: 500px;"></div> <script> const chart = echarts.init(document.getElementById('chart')); const option = { title: { text: 'Research Output by Year', left: 'center' }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, legend: { data: ['Papers', 'Citations'], top: 30 }, xAxis: { type: 'category', data: ['2019', '2020', '2021', '2022', '2023'] }, yAxis: [ { type: 'value', name: 'Papers' }, { type: 'value', name: 'Citations' } ], series: [ { name: 'Papers', type: 'bar', data: [12, 15, 18, 22, 28], itemStyle: { color: '#3B82F6' } }, { name: 'Citations', type: 'line', yAxisIndex: 1, data: [45, 120, 280, 450, 680], itemStyle: { color: '#EF4444' }, smooth: true } ], dataZoom: [{ type: 'slider', start: 0, end: 100 }] }; chart.setOption(option); window.addEventListener('resize', () => chart.resize()); </script> </body> </html>
pythonimport plotly.graph_objects as go import networkx as nx # Create a citation network G = nx.karate_club_graph() pos = nx.spring_layout(G, seed=42) # Edge traces edge_x, edge_y = [], [] for edge in G.edges(): x0, y0 = pos[edge[0]] x1, y1 = pos[edge[1]] edge_x.extend([x0, x1, None]) edge_y.extend([y0, y1, None]) edge_trace = go.Scatter(x=edge_x, y=edge_y, mode="lines", line=dict(width=0.5, color="#888"), hoverinfo="none") # Node traces node_x = [pos[n][0] for n in G.nodes()] node_y = [pos[n][1] for n in G.nodes()] node_degree = [G.degree(n) for n in G.nodes()] node_trace = go.Scatter( x=node_x, y=node_y, mode="markers", marker=dict(size=[d*3 for d in node_degree], color=node_degree, colorscale="Viridis", showscale=True, colorbar=dict(title="Connections")), text=[f"Node {n}: {G.degree(n)} connections" for n in G.nodes()], hoverinfo="text" ) fig = go.Figure(data=[edge_trace, node_trace], layout=go.Layout(title="Citation Network", showlegend=False, xaxis=dict(showgrid=False, zeroline=False, showticklabels=False), yaxis=dict(showgrid=False, zeroline=False, showticklabels=False))) fig.write_html("network.html")
python# Export as high-res static image for journals fig.write_image("figure.pdf", width=1200, height=800, scale=2) # Vector PDF fig.write_image("figure.png", width=1200, height=800, scale=3) # 300 DPI PNG fig.write_image("figure.svg", width=1200, height=800) # Vector SVG # Requires: pip install kaleido
python# Plotly renders natively in Jupyter fig.show() # For Altair in Jupyter chart # Just display the chart object # For ECharts in Jupyter, use pyecharts from pyecharts.charts import Bar from pyecharts import options as opts bar = (Bar() .add_xaxis(["2019", "2020", "2021", "2022", "2023"]) .add_yaxis("Papers", [12, 15, 18, 22, 28]) .set_global_opts(title_opts=opts.TitleOpts(title="Research Output"))) bar.render_notebook()
window.addEventListener('resize') for ECharts.scattergl, Deck.gl) or server-side aggregation.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 16,747 | 17,796 | +6% | 1 | 1 | 0% | 2,949 | 5,690 | +93% | 0 | 0 | — |
case-02 | pass→pass | 17,066 | 16,129 | -5% | 1 | 1 | 0% | 3,397 | 5,977 | +76% | 0 | 0 | — |
case-03 | pass→pass | 12,702 | 14,000 | +10% | 1 | 1 | 0% | 2,327 | 5,461 | +135% | 0 | 0 | — |
case-04 | pass→pass | 12,578 | 11,048 | -12% | 1 | 1 | 0% | 1,955 | 4,259 | +118% | 0 | 0 | — |
case-05 | pass→pass | 16,444 | 18,530 | +13% | 1 | 1 | 0% | 2,375 | 5,792 | +144% | 0 | 0 | — |
case-06 | pass→pass | 8,865 | 8,521 | -4% | 1 | 1 | 0% | 1,740 | 4,124 | +137% | 0 | 0 | — |
case-07 | pass→pass | 7,071 | 5,684 | -20% | 1 | 1 | 0% | 1,423 | 3,731 | +162% | 0 | 0 | — |
case-08 | fail→pass | 10,262 | 10,239 | -0% | 1 | 1 | 0% | 1,958 | 4,652 | +138% | 0 | 0 | — |
case-09 | pass→pass | 9,861 | 7,589 | -23% | 1 | 1 | 0% | 1,843 | 4,110 | +123% | 0 | 0 | — |
case-10 | pass→pass | 2,876 | 3,605 | +25% | 1 | 1 | 0% | 487 | 3,284 | +574% | 0 | 0 | — |
case-11 | pass→pass | 8,440 | 8,237 | -2% | 1 | 1 | 0% | 1,642 | 4,137 | +152% | 0 | 0 | — |
case-12 | pass→pass | 11,551 | 10,190 | -12% | 1 | 1 | 0% | 2,312 | 4,649 | +101% | 0 | 0 | — |
case-13 | pass→pass | 8,837 | 5,492 | -38% | 1 | 1 | 0% | 1,620 | 3,517 | +117% | 0 | 0 | — |
case-14 | pass→pass | 5,296 | 3,966 | -25% | 1 | 1 | 0% | 869 | 3,315 | +281% | 0 | 0 | — |
case-15 | pass→pass | 6,011 | 5,401 | -10% | 1 | 1 | 0% | 1,126 | 3,566 | +217% | 0 | 0 | — |
case-16 | pass→pass | 10,724 | 9,520 | -11% | 1 | 1 | 0% | 2,123 | 4,437 | +109% | 0 | 0 | — |
case-17 | pass→pass | 6,263 | 3,514 | -44% | 1 | 1 | 0% | 1,150 | 3,229 | +181% | 0 | 0 | — |
case-18 | pass→pass | 11,984 | 12,526 | +5% | 1 | 1 | 0% | 2,137 | 4,867 | +128% | 0 | 0 | — |
case-19 | pass→pass | 4,795 | 3,044 | -37% | 1 | 1 | 0% | 963 | 3,254 | +238% | 0 | 0 | — |
case-20 | pass→pass | 16,749 | 15,898 | -5% | 1 | 1 | 0% | 2,822 | 5,383 | +91% | 0 | 0 | — |
case-21 | fail→fail | 8,276 | 5,232 | -37% | 1 | 1 | 0% | 1,404 | 3,549 | +153% | 0 | 0 | — |
case-22 | pass→pass | 10,181 | 6,849 | -33% | 1 | 1 | 0% | 1,748 | 3,802 | +118% | 0 | 0 | — |
case-23 | pass→pass | 9,686 | 4,844 | -50% | 1 | 1 | 0% | 1,392 | 3,325 | +139% | 0 | 0 | — |
case-24 | pass→pass | 17,265 | 19,906 | +15% | 1 | 1 | 0% | 2,590 | 6,095 | +135% | 0 | 0 | — |
case-25 | pass→pass | 11,364 | 9,489 | -16% | 1 | 1 | 0% | 1,741 | 4,061 | +133% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted. The headline lift of +4 percentage points is the difference between those two pass rates over the 25 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.