mcpField
C
chart-generator

chart-generator

github.com/curiouslearner数据与数据库2944 次安装53 周前更新
代码仓库

关于 chart-generator

面向数据可视化任务的图表生成技能,根据数据和目标选择图表,并提供样式定制及导出指导。内容涵盖常见图表类型、静态格式和交互式 HTML。

主要功能

  1. 01按数据选择合适图表
  2. 02生成常见统计与关系图
  3. 03定制主题、标签和图例
  4. 04导出 PNG、SVG、PDF
  5. 05生成交互式 HTML 图表

适用场景

  1. 01比较不同类别的数据
  2. 02展示时间序列趋势
  3. 03查看数值分布或变量关系
  4. 04将图表嵌入报告或网页

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/curiouslearner/devkit --skill chart-generator

安装后在 Claude Code 中输入/chart-generator确认已加载。

详细介绍

图表生成技能

使用各种图表库和格式,根据数据生成图表和可视化内容。

说明

你是一名数据可视化专家。调用时:

  1. 分析数据:

    • 了解数据结构和类型
    • 确定合适的图表类型
    • 检测数据模式和趋势
    • 计算聚合结果和统计数据
    • 确定可视化目标
  2. 生成图表:

    • 创建条形图、折线图、饼图和散点图
    • 生成热力图和树图
    • 创建直方图和箱线图
    • 构建时间序列可视化图表
    • 设计多维图表
  3. 设置样式和自定义:

    • 应用配色方案和主题
    • 添加标签、图例和注释
    • 设置坐标轴和网格线格式
    • 自定义工具提示和交互
    • 确保内容易读且无障碍
  4. 导出和嵌入:

    • 保存为 PNG、SVG、PDF
    • 生成交互式 HTML 图表
    • 嵌入 Markdown 报告
    • 创建图表 API
    • 支持响应式设计

使用示例

@chart-generator data.csv --type bar
@chart-generator --line --time-series
@chart-generator --pie --group-by category
@chart-generator --scatter x:age y:income
@chart-generator --heatmap --correlation
@chart-generator --interactive --html

图表类型和适用场景

各类图表的适用场景
图表类型最适合的用途使用示例
条形图比较类别按产品统计销售额
折线图展示随时间变化的趋势按月统计收入
饼图展示部分与整体的关系市场份额
散点图展示变量之间的关系身高与体重
直方图展示数值分布年龄分布
箱线图展示统计分布各部门的薪资范围
热力图展示矩阵数据和相关性特征相关性
面积图展示累积趋势堆叠收入流
气泡图展示三维数据销售额与利润与市场份额
树图展示层次数据磁盘空间使用情况

Python - Matplotlib

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

def create_bar_chart(data, x_col, y_col, title='Bar Chart', output='chart.png'):
    """
    Create a bar chart
    """
    plt.figure(figsize=(10, 6))

    if isinstance(data, pd.DataFrame):
        x = data[x_col]
        y = data[y_col]
    else:
        x = data['labels']
        y = data['values']

    bars = plt.bar(x, y, color='steelblue', alpha=0.8)

    # Add value labels on bars
    for bar in bars:
        height = bar.get_height()
        plt.text(bar.get_x() + bar.get_width()/2., height,
                f'{height:.1f}',
                ha='center', va='bottom')

    plt.title(title, fontsize=16, fontweight='bold')
    plt.xlabel(x_col if isinstance(data, pd.DataFrame) else 'Category', fontsize=12)
    plt.ylabel(y_col if isinstance(data, pd.DataFrame) else 'Value', fontsize=12)
    plt.xticks(rotation=45, ha='right')
    plt.grid(axis='y', alpha=0.3)
    plt.tight_layout()

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

def create_line_chart(data, x_col, y_col, title='Line Chart', output='chart.png'):
    """
    Create a line chart
    """
    plt.figure(figsize=(12, 6))

    if isinstance(data, pd.DataFrame):
        x = data[x_col]
        y = data[y_col]
    else:
        x = data['x']
        y = data['y']

    plt.plot(x, y, marker='o', linewidth=2, markersize=6, color='steelblue')

    # Add grid
    plt.grid(True, alpha=0.3)

    plt.title(title, fontsize=16, fontweight='bold')
    plt.xlabel(x_col if isinstance(data, pd.DataFrame) else 'X', fontsize=12)
    plt.ylabel(y_col if isinstance(data, pd.DataFrame) else 'Y', fontsize=12)
    plt.xticks(rotation=45, ha='right')
    plt.tight_layout()

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

def create_pie_chart(data, labels_col, values_col, title='Pie Chart', output='chart.png'):
    """
    Create a pie chart
    """
    plt.figure(figsize=(10, 8))

    if isinstance(data, pd.DataFrame):
        labels = data[labels_col]
        values = data[values_col]
    else:
        labels = data['labels']
        values = data['values']

    # Create color palette
    colors = plt.cm.Set3(np.linspace(0, 1, len(labels)))

    # Create pie chart
    wedges, texts, autotexts = plt.pie(
        values,
        labels=labels,
        autopct='%1.1f%%',
        startangle=90,
        colors=colors,
        explode=[0.05] * len(labels)  # Slightly separate slices
    )

    # Style percentage text
    for autotext in autotexts:
        autotext.set_color('white')
        autotext.set_fontweight('bold')
        autotext.set_fontsize(10)

    plt.title(title, fontsize=16, fontweight='bold')
    plt.axis('equal')
    plt.tight_layout()

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

def create_scatter_plot(data, x_col, y_col, color_col=None, size_col=None,
                       title='Scatter Plot', output='chart.png'):
    """
    Create a scatter plot
    """
    plt.figure(figsize=(10, 8))

    if isinstance(data, pd.DataFrame):
        x = data[x_col]
        y = data[y_col]
        c = data[color_col] if color_col else None
        s = data[size_col] if size_col else 50
    else:
        x = data['x']
        y = data['y']
        c = None
        s = 50

    scatter = plt.scatter(x, y, c=c, s=s, alpha=0.6, cmap='viridis')

    if color_col:
        plt.colorbar(scatter, label=color_col)

    # Add trend line
    z = np.polyfit(x, y, 1)
    p = np.poly1d(z)
    plt.plot(x, p(x), "r--", alpha=0.8, label='Trend')

    plt.title(title, fontsize=16, fontweight='bold')
    plt.xlabel(x_col if isinstance(data, pd.DataFrame) else 'X', fontsize=12)
    plt.ylabel(y_col if isinstance(data, pd.DataFrame) else 'Y', fontsize=12)
    plt.grid(True, alpha=0.3)
    plt.legend()
    plt.tight_layout()

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

def create_histogram(data, column, bins=30, title='Histogram', output='chart.png'):
    """
    Create a histogram
    """
    plt.figure(figsize=(10, 6))

    if isinstance(data, pd.DataFrame):
        values = data[column]
    else:
        values = data

    n, bins, patches = plt.hist(values, bins=bins, color='steelblue',
                                 alpha=0.7, edgecolor='black')

    # Add mean line
    mean_val = np.mean(values)
    plt.axvline(mean_val, color='red', linestyle='dashed', linewidth=2,
                label=f'Mean: {mean_val:.2f}')

    # Add median line
    median_val = np.median(values)
    plt.axvline(median_val, color='green', linestyle='dashed', linewidth=2,
                label=f'Median: {median_val:.2f}')

    plt.title(title, fontsize=16, fontweight='bold')
    plt.xlabel(column if isinstance(data, pd.DataFrame) else 'Value', fontsize=12)
    plt.ylabel('Frequency', fontsize=12)
    plt.legend()
    plt.grid(axis='y', alpha=0.3)
    plt.tight_layout()

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

def create_box_plot(data, columns, title='Box Plot', output='chart.png'):
    """
    Create a box plot
    """
    plt.figure(figsize=(10, 6))

    if isinstance(data, pd.DataFrame):
        data_to_plot = [data[col].dropna() for col in columns]
        labels = columns
    else:
        data_to_plot = data
        labels = [f'Group {i+1}' for i in range(len(data))]

    bp = plt.boxplot(data_to_plot, labels=labels, patch_artist=True)

    # Color boxes
    for patch in bp['boxes']:
        patch.set_facecolor('lightblue')
        patch.set_alpha(0.7)

    plt.title(title, fontsize=16, fontweight='bold')
    plt.ylabel('Value', fontsize=12)
    plt.grid(axis='y', alpha=0.3)
    plt.xticks(rotation=45, ha='right')
    plt.tight_layout()

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

def create_heatmap(data, title='Heatmap', output='chart.png'):
    """
    Create a heatmap (correlation matrix)
    """
    plt.figure(figsize=(10, 8))

    if isinstance(data, pd.DataFrame):
        # Calculate correlation matrix
        corr_matrix = data.corr()
    else:
        corr_matrix = data

    # Create heatmap
    im = plt.imshow(corr_matrix, cmap='coolwarm', aspect='auto',
                    vmin=-1, vmax=1)

    # Add colorbar
    cbar = plt.colorbar(im)
    cbar.set_label('Correlation', rotation=270, labelpad=20)

    # Set ticks and labels
    plt.xticks(range(len(corr_matrix.columns)), corr_matrix.columns,
               rotation=45, ha='right')
    plt.yticks(range(len(corr_matrix.columns)), corr_matrix.columns)

    # Add correlation values
    for i in range(len(corr_matrix)):
        for j in range(len(corr_matrix.columns)):
            text = plt.text(j, i, f'{corr_matrix.iloc[i, j]:.2f}',
                          ha='center', va='center', color='black', fontsize=9)

    plt.title(title, fontsize=16, fontweight='bold', pad=20)
    plt.tight_layout()

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

Python - Seaborn

import seaborn as sns

def create_seaborn_chart(data, chart_type, x, y=None, hue=None,
                        title='Chart', output='chart.png'):
    """
    Create charts using Seaborn
    """
    plt.figure(figsize=(12, 6))

    # Set style
    sns.set_style("whitegrid")
    sns.set_palette("husl")

    if chart_type == 'bar':
        sns.barplot(data=data, x=x, y=y, hue=hue)

    elif chart_type == 'line':
        sns.lineplot(data=data, x=x, y=y, hue=hue, marker='o')

    elif chart_type == 'scatter':
        sns.scatterplot(data=data, x=x, y=y, hue=hue, size=hue, alpha=0.6)

    elif chart_type == 'box':
        sns.boxplot(data=data, x=x, y=y, hue=hue)

    elif chart_type == 'violin':
        sns.violinplot(data=data, x=x, y=y, hue=hue)

    elif chart_type == 'dist':
        sns.histplot(data=data, x=x, hue=hue, kde=True)

    elif chart_type == 'heatmap':
        sns.heatmap(data.corr(), annot=True, fmt='.2f', cmap='coolwarm',
                   center=0, square=True, linewidths=1)

    elif chart_type == 'pairplot':
        # Special case - creates its own figure
        g = sns.pairplot(data, hue=hue)
        g.savefig(output, dpi=300, bbox_inches='tight')
        return output

    plt.title(title, fontsize=16, fontweight='bold')
    plt.xticks(rotation=45, ha='right')
    plt.tight_layout()

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

# Advanced Seaborn visualizations
def create_facet_grid(data, x, y, col=None, row=None, hue=None,
                     title='Facet Grid', output='chart.png'):
    """
    Create faceted charts
    """
    g = sns.FacetGrid(data, col=col, row=row, hue=hue, height=4)
    g.map(sns.scatterplot, x, y, alpha=0.6)
    g.add_legend()
    g.fig.suptitle(title, y=1.02, fontsize=16, fontweight='bold')

    plt.savefig(output, dpi=300, bbox_inches='tight')
    plt.close()

    return output

JavaScript - Chart.js

const { ChartJSNodeCanvas } = require('chartjs-node-canvas');

async function createBarChart(data, options = {}) {
  const width = options.width || 800;
  const height = options.height || 600;

  const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height });

  const configuration = {
    type: 'bar',
    data: {
      labels: data.labels,
      datasets: [{
        label: options.label || 'Dataset',
        data: data.values,
        backgroundColor: 'rgba(54, 162, 235, 0.6)',
        borderColor: 'rgba(54, 162, 235, 1)',
        borderWidth: 2
      }]
    },
    options: {
      responsive: true,
      plugins: {
        title: {
          display: true,
          text: options.title || 'Bar Chart',
          font: { size: 18 }
        },
        legend: {
          display: true,
          position: 'top'
        }
      },
      scales: {
        y: {
          beginAtZero: true
        }
      }
    }
  };

  const imageBuffer = await chartJSNodeCanvas.renderToBuffer(configuration);
  return imageBuffer;
}

async function createLineChart(data, options = {}) {
  const width = options.width || 800;
  const height = options.height || 600;

  const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height });

  const configuration = {
    type: 'line',
    data: {
      labels: data.labels,
      datasets: [{
        label: options.label || 'Dataset',
        data: data.values,
        borderColor: 'rgba(75, 192, 192, 1)',
        backgroundColor: 'rgba(75, 192, 192, 0.2)',
        borderWidth: 2,
        tension: 0.4
      }]
    },
    options: {
      responsive: true,
      plugins: {
        title: {
          display: true,
          text: options.title || 'Line Chart',
          font: { size: 18 }
        }
      },
      scales: {
        y: {
          beginAtZero: true
        }
      }
    }
  };

  const imageBuffer = await chartJSNodeCanvas.renderToBuffer(configuration);
  return imageBuffer;
}

async function createPieChart(data, options = {}) {
  const width = options.width || 800;
  const height = options.height || 600;

  const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height });

  const configuration = {
    type: 'pie',
    data: {
      labels: data.labels,
      datasets: [{
        data: data.values,
        backgroundColor: [
          'rgba(255, 99, 132, 0.6)',
          'rgba(54, 162, 235, 0.6)',
          'rgba(255, 206, 86, 0.6)',
          'rgba(75, 192, 192, 0.6)',
          'rgba(153, 102, 255, 0.6)',
          'rgba(255, 159, 64, 0.6)'
        ],
        borderWidth: 2
      }]
    },
    options: {
      responsive: true,
      plugins: {
        title: {
          display: true,
          text: options.title || 'Pie Chart',
          font: { size: 18 }
        },
        legend: {
          position: 'right'
        }
      }
    }
  };

  const imageBuffer = await chartJSNodeCanvas.renderToBuffer(configuration);
  return imageBuffer;
}

交互式图表 - Plotly

import plotly.express as px
import plotly.graph_objects as go

def create_interactive_bar(data, x, y, title='Bar Chart', output='chart.html'):
    """
    Create interactive bar chart with Plotly
    """
    fig = px.bar(data, x=x, y=y, title=title,
                 color=y, color_continuous_scale='Viridis')

    fig.update_layout(
        font=dict(size=14),
        showlegend=True,
        hovermode='x unified'
    )

    fig.write_html(output)
    return output

def create_interactive_line(data, x, y, title='Line Chart', output='chart.html'):
    """
    Create interactive line chart
    """
    fig = px.line(data, x=x, y=y, title=title, markers=True)

    fig.update_traces(line=dict(width=3))

    fig.update_layout(
        hovermode='x unified',
        font=dict(size=14)
    )

    fig.write_html(output)
    return output

def create_interactive_scatter(data, x, y, color=None, size=None,
                              title='Scatter Plot', output='chart.html'):
    """
    Create interactive scatter plot
    """
    fig = px.scatter(data, x=x, y=y, color=color, size=size,
                    title=title, hover_data=data.columns)

    fig.update_traces(marker=dict(line=dict(width=0.5, color='white')))

    fig.write_html(output)
    return output

def create_3d_scatter(data, x, y, z, color=None, title='3D Scatter',
                     output='chart.html'):
    """
    Create 3D scatter plot
    """
    fig = px.scatter_3d(data, x=x, y=y, z=z, color=color, title=title)

    fig.update_layout(scene=dict(
        xaxis_title=x,
        yaxis_title=y,
        zaxis_title=z
    ))

    fig.write_html(output)
    return output

def create_time_series(data, date_col, value_col, title='Time Series',
                      output='chart.html'):
    """
    Create time series chart
    """
    fig = go.Figure()

    fig.add_trace(go.Scatter(
        x=data[date_col],
        y=data[value_col],
        mode='lines+markers',
        name=value_col,
        line=dict(width=2),
        marker=dict(size=6)
    ))

    # Add range slider
    fig.update_xaxes(
        rangeslider_visible=True,
        rangeselector=dict(
            buttons=list([
                dict(count=7, label="1w", step="day", stepmode="backward"),
                dict(count=1, label="1m", step="month", stepmode="backward"),
                dict(count=3, label="3m", step="month", stepmode="backward"),
                dict(count=1, label="1y", step="year", stepmode="backward"),
                dict(step="all")
            ])
        )
    )

    fig.update_layout(
        title=title,
        xaxis_title=date_col,
        yaxis_title=value_col,
        font=dict(size=14)
    )

    fig.write_html(output)
    return output

def create_dashboard(data, output='dashboard.html'):
    """
    Create multi-chart dashboard
    """
    from plotly.subplots import make_subplots

    fig = make_subplots(
        rows=2, cols=2,
        subplot_titles=('Bar Chart', 'Line Chart', 'Pie Chart', 'Scatter Plot'),
        specs=[[{'type': 'bar'}, {'type': 'scatter'}],
               [{'type': 'pie'}, {'type': 'scatter'}]]
    )

    # Add charts
    # Bar chart
    fig.add_trace(
        go.Bar(x=data['category'], y=data['value1'], name='Bar'),
        row=1, col=1
    )

    # Line chart
    fig.add_trace(
        go.Scatter(x=data['date'], y=data['value2'], mode='lines', name='Line'),
        row=1, col=2
    )

    # Pie chart
    fig.add_trace(
        go.Pie(labels=data['category'], values=data['value1'], name='Pie'),
        row=2, col=1
    )

    # Scatter plot
    fig.add_trace(
        go.Scatter(x=data['value1'], y=data['value2'], mode='markers', name='Scatter'),
        row=2, col=2
    )

    fig.update_layout(height=800, showlegend=True, title_text="Dashboard")

    fig.write_html(output)
    return output

图表样式和主题

# Matplotlib themes
def apply_matplotlib_theme(theme='default'):
    """
    Apply theme to matplotlib charts
    """
    themes = {
        'default': 'seaborn-v0_8-darkgrid',
        'minimal': 'seaborn-v0_8-whitegrid',
        'dark': 'dark_background',
        'classic': 'classic',
        'ggplot': 'ggplot'
    }

    plt.style.use(themes.get(theme, 'default'))

# Custom color palettes
COLOR_PALETTES = {
    'corporate': ['#003f5c', '#58508d', '#bc5090', '#ff6361', '#ffa600'],
    'pastel': ['#a8e6cf', '#dcedc1', '#ffd3b6', '#ffaaa5', '#ff8b94'],
    'vibrant': ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6'],
    'monochrome': ['#2c3e50', '#34495e', '#7f8c8d', '#95a5a6', '#bdc3c7']
}

def apply_color_palette(palette_name='corporate'):
    """Apply custom color palette"""
    colors = COLOR_PALETTES.get(palette_name, COLOR_PALETTES['corporate'])
    plt.rcParams['axes.prop_cycle'] = plt.cycler(color=colors)

导出格式

def export_chart_multiple_formats(fig, base_name='chart'):
    """
    Export chart in multiple formats
    """
    formats = {
        'png': {'dpi': 300, 'transparent': False},
        'svg': {'format': 'svg'},
        'pdf': {'format': 'pdf'},
        'jpg': {'dpi': 300, 'format': 'jpg'}
    }

    files = []

    for fmt, kwargs in formats.items():
        output_file = f"{base_name}.{fmt}"
        fig.savefig(output_file, bbox_inches='tight', **kwargs)
        files.append(output_file)

    return files

图表生成流程

def generate_charts_from_data(data, chart_configs, output_dir='charts'):
    """
    Generate multiple charts from data based on configurations

    chart_configs = [
        {
            'type': 'bar',
            'x': 'category',
            'y': 'value',
            'title': 'Sales by Category',
            'output': 'sales_bar.png'
        },
        ...
    ]
    """
    import os
    os.makedirs(output_dir, exist_ok=True)

    generated_charts = []

    for config in chart_configs:
        chart_type = config['type']
        output = os.path.join(output_dir, config['output'])

        if chart_type == 'bar':
            create_bar_chart(data, config['x'], config['y'],
                           config.get('title', 'Chart'), output)

        elif chart_type == 'line':
            create_line_chart(data, config['x'], config['y'],
                            config.get('title', 'Chart'), output)

        elif chart_type == 'pie':
            create_pie_chart(data, config['x'], config['y'],
                           config.get('title', 'Chart'), output)

        elif chart_type == 'scatter':
            create_scatter_plot(data, config['x'], config['y'],
                              config.get('color'), config.get('size'),
                              config.get('title', 'Chart'), output)

        elif chart_type == 'heatmap':
            create_heatmap(data, config.get('title', 'Chart'), output)

        generated_charts.append({
            'type': chart_type,
            'title': config.get('title'),
            'file': output
        })

    return generated_charts

最佳实践

  1. 根据数据选择合适的图表类型
  2. 使用清晰、描述性强的标题和标签
  3. 在各图表中采用一致的配色方案
  4. 确保内容易读(字体大小、对比度)
  5. 添加上下文信息(注释、参考线)
  6. 以高分辨率导出(至少 300 DPI)
  7. 考虑无障碍性(采用适合色盲人群的调色板)
  8. 在不同屏幕尺寸下测试(响应式设计)
  9. 针对网页使用优化文件大小
  10. 记录图表生成代码,确保可复现

常见图表模式

比较图表
  • 使用条形图比较类别
  • 使用分组条形图比较多个系列
  • 使用堆叠条形图展示部分与整体的对比
趋势图表
  • 使用折线图展示时间序列
  • 使用面积图展示累积趋势
  • 使用迷你图展示行内趋势
分布图表
  • 使用直方图展示频率分布
  • 使用箱线图展示统计分布
  • 使用小提琴图展示分布形态
关系图表
  • 使用散点图展示相关性
  • 使用气泡图展示三维关系
  • 使用热力图展示矩阵关系
构成图表
  • 使用饼图简单展示部分与整体的关系
  • 使用堆叠面积图展示随时间变化的趋势
  • 使用树图展示层次构成

注意事项

  • 始终为坐标轴添加标签并注明单位
  • 使用合适的刻度(线性、对数)
  • 考虑数据墨水比(尽量减少图表冗余)
  • 使用不同的数据范围测试图表
  • 使用多个系列时提供图例
  • 使用注释突出关键见解
  • 出版物中的图表应以矢量格式导出
  • 相关图表应保持配色方案一致
  • 考虑颜色含义上的文化差异
  • 可视化前验证数据

评论

支持 Markdown