name: 图表生成器 description: 使用各种图表库和格式从数据生成图表和可视化。
图表生成技能
使用各种图表库和格式从数据生成图表和可视化。
指令
您是一名数据可视化专家。当被调用时:
-
分析数据:
- 理解数据结构和类型
- 识别适当的图表类型
- 检测数据模式和趋势
- 计算聚合和统计
- 确定可视化目标
-
生成图表:
- 创建条形图、折线图、饼图、散点图
- 生成热图和树状图
- 创建直方图和箱线图
- 构建时间序列可视化
- 设计多维图表
-
样式和自定义:
- 应用配色方案和主题
- 添加标签、图例和注释
- 格式化轴和网格线
- 自定义工具提示和交互
- 确保可访问性和可读性
-
导出和嵌入:
- 保存为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
图表类型和用例
何时使用每种图表类型
| 图表类型 | 最适用于 | 示例用例 |
|---|---|---|
| 条形图 | 比较类别 | 按产品销售额 |
| 折线图 | 时间趋势 | 月度收入 |
| 饼图 | 部分到整体关系 | 市场份额 |
| 散点图 | 变量间关系 | 身高与体重 |
| 直方图 | 值分布 | 年龄分布 |
| 箱线图 | 统计分布 | 按部门薪资范围 |
| 热图 | 矩阵数据、相关性 | 特征相关性 |
| 面积图 | 累积趋势 | 堆叠收入流 |
| 气泡图 | 三维数据 | 销售额 vs 利润 vs 市场份额 |
| 树状图 | 分层数据 | 磁盘空间使用情况 |
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='条形图', output='chart.png'):
"""
创建条形图
"""
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)
# 在条形上添加值标签
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 '类别', fontsize=12)
plt.ylabel(y_col if isinstance(data, pd.DataFrame) else '值', 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='折线图', output='chart.png'):
"""
创建折线图
"""
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')
# 添加网格
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='饼图', output='chart.png'):
"""
创建饼图
"""
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']
# 创建颜色调色板
colors = plt.cm.Set3(np.linspace(0, 1, len(labels)))
# 创建饼图
wedges, texts, autotexts = plt.pie(
values,
labels=labels,
autopct='%1.1f%%',
startangle=90,
colors=colors,
explode=[0.05] * len(labels) # 稍微分离切片
)
# 样式化百分比文本
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='散点图', output='chart.png'):
"""
创建散点图
"""
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)
# 添加趋势线
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
plt.plot(x, p(x), "r--", alpha=0.8, label='趋势')
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='直方图', output='chart.png'):
"""
创建直方图
"""
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')
# 添加均值线
mean_val = np.mean(values)
plt.axvline(mean_val, color='red', linestyle='dashed', linewidth=2,
label=f'均值: {mean_val:.2f}')
# 添加中位数线
median_val = np.median(values)
plt.axvline(median_val, color='green', linestyle='dashed', linewidth=2,
label=f'中位数: {median_val:.2f}')
plt.title(title, fontsize=16, fontweight='bold')
plt.xlabel(column if isinstance(data, pd.DataFrame) else '值', fontsize=12)
plt.ylabel('频率', 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='箱线图', output='chart.png'):
"""
创建箱线图
"""
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'组 {i+1}' for i in range(len(data))]
bp = plt.boxplot(data_to_plot, labels=labels, patch_artist=True)
# 颜色化箱子
for patch in bp['boxes']:
patch.set_facecolor('lightblue')
patch.set_alpha(0.7)
plt.title(title, fontsize=16, fontweight='bold')
plt.ylabel('值', 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='热图', output='chart.png'):
"""
创建热图(相关矩阵)
"""
plt.figure(figsize=(10, 8))
if isinstance(data, pd.DataFrame):
# 计算相关矩阵
corr_matrix = data.corr()
else:
corr_matrix = data
# 创建热图
im = plt.imshow(corr_matrix, cmap='coolwarm', aspect='auto',
vmin=-1, vmax=1)
# 添加颜色条
cbar = plt.colorbar(im)
cbar.set_label('相关性', rotation=270, labelpad=20)
# 设置刻度和标签
plt.xticks(range(len(corr_matrix.columns)), corr_matrix.columns,
rotation=45, ha='right')
plt.yticks(range(len(corr_matrix.columns)), corr_matrix.columns)
# 添加相关值
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='图表', output='chart.png'):
"""
使用Seaborn创建图表
"""
plt.figure(figsize=(12, 6))
# 设置样式
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':
# 特殊情况 - 创建自己的图形
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
# 高级Seaborn可视化
def create_facet_grid(data, x, y, col=None, row=None, hue=None,
title='分面网格', output='chart.png'):
"""
创建分面图表
"""
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 || '数据集',
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 || '条形图',
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 || '数据集',
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 || '折线图',
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 || '饼图',
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='条形图', output='chart.html'):
"""
使用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='折线图', output='chart.html'):
"""
创建交互式折线图
"""
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='散点图', output='chart.html'):
"""
创建交互式散点图
"""
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散点图',
output='chart.html'):
"""
创建3D散点图
"""
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='时间序列',
output='chart.html'):
"""
创建时间序列图表
"""
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)
))
# 添加范围滑块
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=list([
dict(count=7, label="1周", step="day", stepmode="backward"),
dict(count=1, label="1月", step="month", stepmode="backward"),
dict(count=3, label="3月", step="month", stepmode="backward"),
dict(count=1, label="1年", 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'):
"""
创建多图表仪表板
"""
from plotly.subplots import make_subplots
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('条形图', '折线图', '饼图', '散点图'),
specs=[[{'type': 'bar'}, {'type': 'scatter'}],
[{'type': 'pie'}, {'type': 'scatter'}]]
)
# 添加图表
# 条形图
fig.add_trace(
go.Bar(x=data['category'], y=data['value1'], name='条形图'),
row=1, col=1
)
# 折线图
fig.add_trace(
go.Scatter(x=data['date'], y=data['value2'], mode='lines', name='折线图'),
row=1, col=2
)
# 饼图
fig.add_trace(
go.Pie(labels=data['category'], values=data['value1'], name='饼图'),
row=2, col=1
)
# 散点图
fig.add_trace(
go.Scatter(x=data['value1'], y=data['value2'], mode='markers', name='散点图'),
row=2, col=2
)
fig.update_layout(height=800, showlegend=True, title_text="仪表板")
fig.write_html(output)
return output
图表样式和主题
# Matplotlib主题
def apply_matplotlib_theme(theme='default'):
"""
将主题应用于matplotlib图表
"""
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'))
# 自定义调色板
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'):
"""应用自定义调色板"""
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'):
"""
以多种格式导出图表
"""
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'):
"""
基于配置从数据生成多个图表
chart_configs = [
{
'type': 'bar',
'x': 'category',
'y': 'value',
'title': '按类别销售额',
'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', '图表'), output)
elif chart_type == 'line':
create_line_chart(data, config['x'], config['y'],
config.get('title', '图表'), output)
elif chart_type == 'pie':
create_pie_chart(data, config['x'], config['y'],
config.get('title', '图表'), output)
elif chart_type == 'scatter':
create_scatter_plot(data, config['x'], config['y'],
config.get('color'), config.get('size'),
config.get('title', '图表'), output)
elif chart_type == 'heatmap':
create_heatmap(data, config.get('title', '图表'), output)
generated_charts.append({
'type': chart_type,
'title': config.get('title'),
'file': output
})
return generated_charts
最佳实践
- 选择适当的图表类型 用于数据
- 使用清晰、描述性的标题 和标签
- 应用一致的配色方案 跨图表
- 确保可读性(字体大小、对比度)
- 添加上下文(注释、参考线)
- 以高分辨率导出(最小300 DPI)
- 考虑可访问性(色盲友好调色板)
- 在不同屏幕尺寸上测试(响应式设计)
- 优化文件大小 用于网络使用
- 文档化图表生成 代码以确保可重现性
常见图表模式
比较图表
- 条形图用于类别比较
- 分组条形图用于多系列比较
- 堆叠条形图用于部分到整体比较
趋势图表
- 折线图用于时间序列
- 面积图用于累积趋势
- 迷你图用于内联趋势
分布图表
- 直方图用于频率分布
- 箱线图用于统计分布
- 小提琴图用于分布形状
关系图表
- 散点图用于相关性
- 气泡图用于3D关系
- 热图用于矩阵关系
构成图表
- 饼图用于简单的部分到整体
- 堆叠面积图用于时间趋势
- 树状图用于分层构成
注释
- 始终标注轴并提供单位
- 使用适当的比例(线性、对数)
- 考虑数据墨水比率(最小化图表垃圾)
- 使用不同的数据范围测试图表
- 在使用多个系列时提供图例
- 使用注释来突出关键洞察
- 为出版物导出矢量格式图表
- 保持配色方案在相关图表中一致
- 考虑颜色含义的文化差异
- 在可视化之前验证数据