feat(skills): add data-viz skill for chart generation
Add new skill for generating charts and data visualizations using Python matplotlib.
Features:
- Line charts (time series)
- Bar charts
- Pie charts
- Dual-line comparison charts
- Heatmaps
- JSON data loading
Uses `uv run --with matplotlib` for zero-install Python charting.
Includes dark theme optimized for chat interfaces.
🤖 AI-assisted: This skill was developed with Claude assistance.
Testing: Fully tested with real-world alert data visualization.
This commit is contained in:
parent
da71eaebd2
commit
1c162d9c40
227
skills/data-viz/SKILL.md
Normal file
227
skills/data-viz/SKILL.md
Normal file
@ -0,0 +1,227 @@
|
||||
---
|
||||
name: data-viz
|
||||
description: Generate charts and data visualizations using Python matplotlib. Use when asked to create graphs, charts, plots, or visual representations of data. Supports line charts, bar charts, pie charts, scatter plots, heatmaps, and more.
|
||||
homepage: https://matplotlib.org/
|
||||
metadata: {"openclaw":{"emoji":"📊","requires":{"bins":["uv"]}}}
|
||||
---
|
||||
|
||||
# Data Visualization Skill
|
||||
|
||||
Generate professional charts and graphs using Python matplotlib via `uv run` (no pip install needed).
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
uv run --with matplotlib python3 << 'EOF'
|
||||
import matplotlib.pyplot as plt
|
||||
plt.style.use('dark_background')
|
||||
|
||||
data = [10, 25, 15, 30, 20]
|
||||
labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 5))
|
||||
ax.plot(labels, data, 'o-', color='#e94560', linewidth=2)
|
||||
ax.fill_between(labels, data, alpha=0.3, color='#e94560')
|
||||
ax.set_title('Weekly Metrics', fontsize=14, fontweight='bold')
|
||||
ax.grid(True, alpha=0.3)
|
||||
plt.tight_layout()
|
||||
plt.savefig('chart.png', dpi=150, facecolor='#1a1a2e')
|
||||
print('✅ Saved: chart.png')
|
||||
EOF
|
||||
```
|
||||
|
||||
## Chart Types
|
||||
|
||||
### Line Chart (Time Series)
|
||||
|
||||
```bash
|
||||
uv run --with matplotlib python3 << 'EOF'
|
||||
import matplotlib.pyplot as plt
|
||||
plt.style.use('dark_background')
|
||||
|
||||
dates = ['Jan 1', 'Jan 2', 'Jan 3', 'Jan 4', 'Jan 5']
|
||||
values = [10, 25, 15, 30, 20]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 5))
|
||||
ax.plot(dates, values, 'o-', color='#e94560', linewidth=2, markersize=6)
|
||||
ax.fill_between(dates, values, alpha=0.3, color='#e94560')
|
||||
ax.set_title('Daily Metrics', fontsize=14, fontweight='bold')
|
||||
ax.set_xlabel('Date')
|
||||
ax.set_ylabel('Value')
|
||||
ax.grid(True, alpha=0.3)
|
||||
plt.tight_layout()
|
||||
plt.savefig('line_chart.png', dpi=150, facecolor='#1a1a2e')
|
||||
print('✅ Saved: line_chart.png')
|
||||
EOF
|
||||
```
|
||||
|
||||
### Bar Chart
|
||||
|
||||
```bash
|
||||
uv run --with matplotlib python3 << 'EOF'
|
||||
import matplotlib.pyplot as plt
|
||||
plt.style.use('dark_background')
|
||||
|
||||
categories = ['Service A', 'Service B', 'Service C', 'Service D']
|
||||
values = [45, 32, 67, 28]
|
||||
colors = ['#e94560', '#27ae60', '#3498db', '#f39c12']
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 5))
|
||||
bars = ax.bar(categories, values, color=colors, alpha=0.8)
|
||||
ax.set_title('Comparison', fontsize=14, fontweight='bold')
|
||||
ax.set_ylabel('Count')
|
||||
|
||||
# Add value labels on bars
|
||||
for bar, val in zip(bars, values):
|
||||
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
|
||||
str(val), ha='center', fontsize=10)
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('bar_chart.png', dpi=150, facecolor='#1a1a2e')
|
||||
print('✅ Saved: bar_chart.png')
|
||||
EOF
|
||||
```
|
||||
|
||||
### Dual-Line Chart (Comparison)
|
||||
|
||||
```bash
|
||||
uv run --with matplotlib python3 << 'EOF'
|
||||
import matplotlib.pyplot as plt
|
||||
plt.style.use('dark_background')
|
||||
|
||||
dates = ['Jan 1', 'Jan 2', 'Jan 3', 'Jan 4', 'Jan 5']
|
||||
series_a = [71, 7, 22, 35, 26]
|
||||
series_b = [50, 6, 11, 27, 21]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 5))
|
||||
ax.fill_between(dates, series_a, alpha=0.3, color='#e94560')
|
||||
ax.fill_between(dates, series_b, alpha=0.3, color='#27ae60')
|
||||
ax.plot(dates, series_a, 'o-', color='#e94560', linewidth=2, label='Series A')
|
||||
ax.plot(dates, series_b, 's-', color='#27ae60', linewidth=2, label='Series B')
|
||||
ax.set_title('Comparison', fontsize=14, fontweight='bold')
|
||||
ax.legend(loc='upper right')
|
||||
ax.grid(True, alpha=0.3)
|
||||
plt.tight_layout()
|
||||
plt.savefig('dual_chart.png', dpi=150, facecolor='#1a1a2e')
|
||||
print('✅ Saved: dual_chart.png')
|
||||
EOF
|
||||
```
|
||||
|
||||
### Pie Chart
|
||||
|
||||
```bash
|
||||
uv run --with matplotlib python3 << 'EOF'
|
||||
import matplotlib.pyplot as plt
|
||||
plt.style.use('dark_background')
|
||||
|
||||
labels = ['Category A', 'Category B', 'Category C', 'Other']
|
||||
sizes = [580, 324, 154, 200]
|
||||
colors = ['#e94560', '#27ae60', '#3498db', '#95a5a6']
|
||||
explode = (0.05, 0, 0, 0) # Highlight first slice
|
||||
|
||||
fig, ax = plt.subplots(figsize=(8, 8))
|
||||
ax.pie(sizes, explode=explode, labels=labels, colors=colors,
|
||||
autopct='%1.1f%%', shadow=True, startangle=90)
|
||||
ax.set_title('Distribution', fontsize=14, fontweight='bold')
|
||||
plt.tight_layout()
|
||||
plt.savefig('pie_chart.png', dpi=150, facecolor='#1a1a2e')
|
||||
print('✅ Saved: pie_chart.png')
|
||||
EOF
|
||||
```
|
||||
|
||||
### Heatmap
|
||||
|
||||
```bash
|
||||
uv run --with matplotlib --with numpy python3 << 'EOF'
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
plt.style.use('dark_background')
|
||||
|
||||
# 4 weeks x 7 days of random data
|
||||
data = np.random.randint(10, 100, (4, 7))
|
||||
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
weeks = ['Week 1', 'Week 2', 'Week 3', 'Week 4']
|
||||
|
||||
fig, ax = plt.subplots(figsize=(10, 4))
|
||||
im = ax.imshow(data, cmap='RdYlGn_r')
|
||||
ax.set_xticks(range(7))
|
||||
ax.set_yticks(range(4))
|
||||
ax.set_xticklabels(days)
|
||||
ax.set_yticklabels(weeks)
|
||||
plt.colorbar(im, label='Value')
|
||||
ax.set_title('Heatmap', fontsize=14, fontweight='bold')
|
||||
plt.tight_layout()
|
||||
plt.savefig('heatmap.png', dpi=150, facecolor='#1a1a2e')
|
||||
print('✅ Saved: heatmap.png')
|
||||
EOF
|
||||
```
|
||||
|
||||
## Loading Data from JSON
|
||||
|
||||
```bash
|
||||
uv run --with matplotlib python3 << 'EOF'
|
||||
import json
|
||||
import matplotlib.pyplot as plt
|
||||
from pathlib import Path
|
||||
|
||||
# Load data from JSON file
|
||||
with open('data.json') as f:
|
||||
data = json.load(f)
|
||||
|
||||
labels = data['labels']
|
||||
values = data['values']
|
||||
|
||||
plt.style.use('dark_background')
|
||||
fig, ax = plt.subplots(figsize=(12, 5))
|
||||
ax.bar(labels, values, color='#e94560', alpha=0.8)
|
||||
ax.set_title(data.get('title', 'Chart'), fontsize=14, fontweight='bold')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
plt.xticks(rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
plt.savefig('chart.png', dpi=150, facecolor='#1a1a2e')
|
||||
print('✅ Saved: chart.png')
|
||||
EOF
|
||||
```
|
||||
|
||||
## Style Options
|
||||
|
||||
### Dark Theme (recommended for chat)
|
||||
|
||||
```python
|
||||
plt.style.use('dark_background')
|
||||
plt.savefig('chart.png', facecolor='#1a1a2e')
|
||||
```
|
||||
|
||||
### Light Theme
|
||||
|
||||
```python
|
||||
plt.style.use('default')
|
||||
plt.savefig('chart.png', facecolor='white')
|
||||
```
|
||||
|
||||
### Color Palette
|
||||
|
||||
```python
|
||||
COLORS = {
|
||||
'red': '#e94560',
|
||||
'green': '#27ae60',
|
||||
'blue': '#3498db',
|
||||
'orange': '#f39c12',
|
||||
'purple': '#9b59b6',
|
||||
'teal': '#1abc9c',
|
||||
'gray': '#95a5a6'
|
||||
}
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
1. **Use `uv run --with matplotlib`** — installs matplotlib on-the-fly, no pip needed
|
||||
2. **Dark theme** looks better in chat interfaces
|
||||
3. **Always add labels/legends** — charts should be self-explanatory
|
||||
4. **Use `figsize`** — `(12, 5)` for wide charts, `(8, 8)` for square
|
||||
5. **Set `dpi=150`** for crisp images without being too large
|
||||
6. **Read the saved PNG** after generating to display to user
|
||||
|
||||
## Requirements
|
||||
|
||||
- `uv` — Python package runner (install: `curl -LsSf https://astral.sh/uv/install.sh | sh`)
|
||||
Loading…
Reference in New Issue
Block a user