数据可视化Skill data-visualization

该技能专注于创建高效的数据可视化工具,包括图表、仪表板和报告,应用于分析、基础设施监控和机器学习等领域。核心能力涵盖库选择、UX设计和可访问性优化,旨在通过视觉方式清晰传达数据洞察。关键词:数据可视化、图表制作、仪表板设计、数据分析、机器学习可视化、Matplotlib、Plotly、Seaborn、Grafana、Tableau。

数据可视化 0 次安装 2 次浏览 更新于 3/24/2026

name: data-visualization description: 创建有效的数据可视化、图表、仪表板和报告,覆盖分析、基础设施监控和机器学习领域。包括库选择、UX设计和可访问性。触发关键词:图表、图形、绘图、仪表板、报告、可视化、matplotlib、plotly、d3、seaborn、grafana、tableau、superset、metabase、KPI、指标、分析、直方图、热图、时间序列、散点图、条形图。 allowed-tools: Read, Grep, Glob, Edit, Write, Bash

数据可视化

概述

本技能创建有效的数据可视化,清晰地传达洞察力,覆盖多个领域:分析仪表板、基础设施监控和机器学习模型性能跟踪。它结合了数据分析专业知识、仪表板架构模式、UX设计原则和可访问性考虑。

指令

1. 理解数据和上下文

  • 分析数据结构、类型和粒度
  • 识别关键指标、维度和关系
  • 确定要讲述的分析问题或故事
  • 考虑目标受众及其技术水平
  • 评估更新频率和实时需求
  • 识别特定领域需求(分析、监控、机器学习)

2. 选择可视化领域

分析仪表板:业务指标、KPI、趋势、比较

  • 工具:Tableau、Superset、Metabase、Plotly Dash、Streamlit
  • 重点:执行摘要、向下钻取探索、报告生成

基础设施监控:系统健康、资源使用、警报

  • 工具:Grafana、Prometheus、Datadog、CloudWatch
  • 重点:实时指标、警报阈值、SLA跟踪

机器学习模型性能:训练指标、预测、模型诊断

  • 工具:TensorBoard、Weights & Biases、MLflow、matplotlib
  • 重点:损失曲线、混淆矩阵、特征重要性

3. 选择图表类型和布局

  • 根据数据关系匹配图表类型(参见图表选择指南)
  • 考虑数据量和复杂性
  • 规划交互性需求(工具提示、过滤器、缩放)
  • 设计信息层次(KPI优先,细节在下)
  • 考虑可访问性(颜色、对比度、屏幕阅读器)

4. 设计清晰度和可用性

视觉设计

  • 选择可访问的颜色方案(色盲安全调色板)
  • 用单位清晰标注轴和数据
  • 移除图表垃圾和不必要的装饰
  • 用注释突出关键洞察
  • 在仪表板中使用一致的样式

UX考虑

  • 提供上下文(基准线、目标、历史比较)
  • 启用渐进披露(摘要 → 细节)
  • 添加清晰的图例和工具提示
  • 包括数据新鲜度指示器
  • 设计桌面和移动视图

可访问性

  • WCAG AA 颜色对比度比率(文本4.5:1,图形3:1)
  • 使用图案/纹理补充颜色
  • 为静态可视化提供替代文本
  • 支持交互图表的键盘导航
  • 包括数据表作为后备

5. 实施和验证

  • 用选择工具构建可视化
  • 以生产规模测试真实数据
  • 验证计算和聚合
  • 验证性能(仪表板加载时间<3秒)
  • 收集最终用户反馈
  • 基于使用模式和分析迭代

最佳实践

一般原则

  1. 数据的正确图表:将可视化与数据关系和问题匹配
  2. 少即是多:移除不必要元素,最大化数据墨水比
  3. 一致的样式:使用连贯的颜色方案和排版
  4. 可访问性设计:支持色盲用户、屏幕阅读器、键盘导航
  5. 清晰标注:描述性标题、带单位的轴标签、数据来源
  6. 上下文重要:包括基准线、目标、历史比较
  7. 交互性有帮助时:为探索添加工具提示、过滤器、缩放
  8. 性能优先:优化仪表板加载时间<3秒
  9. 移动响应式:设计桌面和移动视口

领域特定指南

分析仪表板

  • 从执行摘要开始(4-6个带趋势的KPI)
  • 支持从摘要向下钻取到细节
  • 包括日期范围选择器和常见过滤器
  • 提供导出选项(PDF、CSV、PNG)
  • 缓存昂贵查询,按计划刷新

基础设施监控

  • 使用时间序列线图展示随时间变化的指标
  • 设置带颜色带的警报阈值
  • 突出显示当前状态(健康/降级/停机)
  • 包括百分位数指标(p50、p95、p99),不仅是平均值
  • 每30-60秒自动刷新仪表板

机器学习模型性能

  • 一起绘制训练/验证曲线
  • 用归一化值展示混淆矩阵
  • 用水平条可视化特征重要性
  • 为可解释性包括样本预测
  • 跨实验跟踪指标进行比较

可访问性清单

  • 使用色盲安全调色板(避免仅红/绿)
  • 确保文本对比度比率4.5:1
  • 为静态图像提供替代文本
  • 支持键盘导航(Tab、方向键)
  • 包括数据表作为替代表示
  • 用屏幕阅读器测试(NVDA、JAWS)
  • 使用图案/纹理补充颜色
  • 避免闪烁或快速变化的内容

示例

示例1:Python使用Matplotlib/Seaborn

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

# 为专业外观设置样式
plt.style.use('seaborn-v0_8-whitegrid')
sns.set_palette("husl")

# 创建带有子图的图形
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 示例1:时间序列线图
df_sales = pd.DataFrame({
    'date': pd.date_range('2024-01-01', periods=12, freq='M'),
    'revenue': [100, 120, 115, 140, 155, 170, 165, 180, 195, 210, 225, 250],
    'target': [110, 115, 120, 130, 145, 160, 175, 185, 200, 215, 230, 245]
})

ax1 = axes[0, 0]
ax1.plot(df_sales['date'], df_sales['revenue'], marker='o', linewidth=2, label='实际')
ax1.plot(df_sales['date'], df_sales['target'], linestyle='--', linewidth=2, label='目标')
ax1.fill_between(df_sales['date'], df_sales['revenue'], df_sales['target'],
                  alpha=0.3, where=(df_sales['revenue'] >= df_sales['target']), color='green')
ax1.fill_between(df_sales['date'], df_sales['revenue'], df_sales['target'],
                  alpha=0.3, where=(df_sales['revenue'] < df_sales['target']), color='red')
ax1.set_title('月度收入 vs 目标', fontsize=14, fontweight='bold')
ax1.set_xlabel('月份')
ax1.set_ylabel('收入(千美元)')
ax1.legend()
ax1.tick_params(axis='x', rotation=45)

# 示例2:比较条形图
df_products = pd.DataFrame({
    'product': ['产品A', '产品B', '产品C', '产品D', '产品E'],
    'sales': [45, 32, 28, 22, 18]
})

ax2 = axes[0, 1]
colors = sns.color_palette("Blues_r", len(df_products))
bars = ax2.barh(df_products['product'], df_products['sales'], color=colors)
ax2.bar_label(bars, padding=3, fmt='$%.0fK')
ax2.set_title('按产品销售', fontsize=14, fontweight='bold')
ax2.set_xlabel('销售(千美元)')
ax2.invert_yaxis()

# 示例3:带回归的散点图
np.random.seed(42)
df_scatter = pd.DataFrame({
    'ad_spend': np.random.uniform(10, 100, 50),
    'conversions': lambda x: x['ad_spend'] * 2.5 + np.random.normal(0, 15, 50)
}.__class__.__call__(pd.DataFrame({'ad_spend': np.random.uniform(10, 100, 50)})))
df_scatter['conversions'] = df_scatter['ad_spend'] * 2.5 + np.random.normal(0, 15, 50)

ax3 = axes[1, 0]
sns.regplot(data=df_scatter, x='ad_spend', y='conversions', ax=ax3,
            scatter_kws={'alpha': 0.6}, line_kws={'color': 'red'})
ax3.set_title('广告支出 vs 转化', fontsize=14, fontweight='bold')
ax3.set_xlabel('广告支出(千美元)')
ax3.set_ylabel('转化')

# 示例4:组成饼图/环形图
df_channels = pd.DataFrame({
    'channel': ['自然', '付费搜索', '社交', '邮件', '直接'],
    'traffic': [35, 25, 20, 12, 8]
})

ax4 = axes[1, 1]
wedges, texts, autotexts = ax4.pie(
    df_channels['traffic'],
    labels=df_channels['channel'],
    autopct='%1.1f%%',
    pctdistance=0.75,
    wedgeprops=dict(width=0.5)
)
ax4.set_title('按渠道流量', fontsize=14, fontweight='bold')

plt.tight_layout()
plt.savefig('dashboard.png', dpi=150, bbox_inches='tight')
plt.show()

示例2:使用Plotly的交互可视化

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

# 创建交互式时间序列
df = pd.DataFrame({
    'date': pd.date_range('2024-01-01', periods=365, freq='D'),
    'value': (pd.Series(range(365)) * 0.1 +
              np.sin(pd.Series(range(365)) * 0.1) * 20 +
              np.random.normal(0, 5, 365)).cumsum()
})

fig = go.Figure()

fig.add_trace(go.Scatter(
    x=df['date'],
    y=df['value'],
    mode='lines',
    name='每日值',
    line=dict(color='#1f77b4', width=1.5),
    hovertemplate='%{x|%B %d, %Y}<br>值: %{y:.2f}<extra></extra>'
))

# 添加移动平均
df['ma_7'] = df['value'].rolling(7).mean()
fig.add_trace(go.Scatter(
    x=df['date'],
    y=df['ma_7'],
    mode='lines',
    name='7日移动平均',
    line=dict(color='#ff7f0e', width=2, dash='dash')
))

fig.update_layout(
    title='每日性能带移动平均',
    xaxis_title='日期',
    yaxis_title='值',
    hovermode='x unified',
    template='plotly_white',
    xaxis=dict(
        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(step="all")
            ])
        ),
        rangeslider=dict(visible=True)
    )
)

fig.write_html('interactive_chart.html')
fig.show()

示例3:图表类型选择指南

## 按数据类型选择图表

### 比较

- **条形图**:跨类别比较值
- **分组条形图**:跨类别比较多个系列
- **子弹图**:显示性能相对于目标

### 分布

- **直方图**:显示频率分布
- **箱线图**:显示分布汇总统计
- **小提琴图**:显示分布形状

### 组成

- **饼图/环形图**:显示整体部分(<6个类别)
- **堆叠条形图**:跨类别显示组成
- **树状图**:显示层次组成

### 关系

- **散点图**:显示两个变量之间的相关性
- **气泡图**:通过大小添加第三维度
- **热图**:显示相关矩阵

### 时间序列

- **线图**:显示随时间趋势
- **面积图**:显示累积趋势
- **蜡烛图**:显示OHLC金融数据

### 地理

- **分层设色地图**:按区域显示值
- **点地图**:显示带值的位置
- **流地图**:显示位置之间的移动

示例4:使用Streamlit的分析仪表板

# Streamlit仪表板示例
import streamlit as st
import pandas as pd
import plotly.express as px

st.set_page_config(page_title="销售仪表板", layout="wide")

# 头部
st.title("销售性能仪表板")
st.markdown("---")

# KPI行
col1, col2, col3, col4 = st.columns(4)
with col1:
    st.metric("总收入", "$1.2M", "+12%")
with col2:
    st.metric("订单", "8,543", "+8%")
with col3:
    st.metric("平均订单值", "$140", "+3%")
with col4:
    st.metric("转化率", "3.2%", "-0.5%")

st.markdown("---")

# 过滤器
with st.sidebar:
    st.header("过滤器")
    date_range = st.date_input("日期范围", [])
    region = st.multiselect("区域", ["北", "南", "东", "西"])
    category = st.selectbox("类别", ["全部", "电子产品", "服装", "家居"])

# 主要图表
left_col, right_col = st.columns([2, 1])

with left_col:
    st.subheader("收入趋势")
    # 此处线图

with right_col:
    st.subheader("按区域销售")
    # 此处饼图

# 细节表
st.subheader("近期订单")
# 此处数据表

示例5:用于基础设施监控的Grafana仪表板

# Grafana仪表板JSON结构
# 保存为dashboard.json并通过Grafana UI导入

apiVersion: 1
providers:
  - name: "default"
    folder: "基础设施"
    type: file
    options:
      path: /etc/grafana/provisioning/dashboards

---
# 仪表板面板配置
{
  "dashboard":
    {
      "title": "服务健康仪表板",
      "tags": ["基础设施", "监控"],
      "timezone": "浏览器",
      "panels":
        [
          {
            "id": 1,
            "title": "请求率",
            "type": "图形",
            "targets":
              [
                {
                  "expr": "rate(http_requests_total[5m])",
                  "legendFormat": "{{service}}",
                },
              ],
            "yaxes": [{ "format": "reqps", "label": "请求/秒" }],
            "alert":
              {
                "conditions":
                  [
                    {
                      "evaluator": { "params": [100], "type": "gt" },
                      "query": { "params": ["A", "5m", "now"] },
                    },
                  ],
              },
          },
          {
            "id": 2,
            "title": "错误率",
            "type": "图形",
            "targets":
              [
                {
                  "expr": "rate(http_errors_total[5m]) / rate(http_requests_total[5m])",
                  "legendFormat": "错误 %",
                },
              ],
            "fieldConfig":
              {
                "defaults":
                  {
                    "thresholds":
                      {
                        "steps":
                          [
                            { "value": 0, "color": "绿色" },
                            { "value": 0.01, "color": "黄色" },
                            { "value": 0.05, "color": "红色" },
                          ],
                      },
                  },
              },
          },
          {
            "id": 3,
            "title": "响应时间(p95)",
            "type": "图形",
            "targets":
              [
                {
                  "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
                  "legendFormat": "p95",
                },
              ],
          },
        ],
      "refresh": "30秒",
      "time": { "from": "now-1h", "to": "now" },
    },
}

示例6:机器学习模型性能可视化

# TensorBoard风格训练可视化
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sns

# 训练历史可视化
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 损失曲线
ax1 = axes[0, 0]
epochs = range(1, 51)
train_loss = np.exp(-np.array(epochs) / 10) + np.random.normal(0, 0.05, 50)
val_loss = np.exp(-np.array(epochs) / 10) + np.random.normal(0, 0.08, 50) + 0.1

ax1.plot(epochs, train_loss, label='训练损失', linewidth=2)
ax1.plot(epochs, val_loss, label='验证损失', linewidth=2)
ax1.axvline(x=30, color='red', linestyle='--', alpha=0.5, label='最佳模型')
ax1.set_xlabel('轮次')
ax1.set_ylabel('损失')
ax1.set_title('训练和验证损失', fontsize=14, fontweight='bold')
ax1.legend()
ax1.grid(alpha=0.3)

# 准确度曲线
ax2 = axes[0, 1]
train_acc = 1 - train_loss
val_acc = 1 - val_loss

ax2.plot(epochs, train_acc, label='训练准确度', linewidth=2)
ax2.plot(epochs, val_acc, label='验证准确度', linewidth=2)
ax2.axvline(x=30, color='red', linestyle='--', alpha=0.5)
ax2.set_xlabel('轮次')
ax2.set_ylabel('准确度')
ax2.set_title('训练和验证准确度', fontsize=14, fontweight='bold')
ax2.legend()
ax2.grid(alpha=0.3)

# 混淆矩阵
ax3 = axes[1, 0]
y_true = np.random.randint(0, 3, 500)
y_pred = y_true.copy()
y_pred[np.random.random(500) < 0.15] = np.random.randint(0, 3, (y_pred.shape[0]))

cm = confusion_matrix(y_true, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=ax3,
            xticklabels=['类别A', '类别B', '类别C'],
            yticklabels=['类别A', '类别B', '类别C'])
ax3.set_xlabel('预测')
ax3.set_ylabel('实际')
ax3.set_title('混淆矩阵', fontsize=14, fontweight='bold')

# 特征重要性
ax4 = axes[1, 1]
features = ['特征A', '特征B', '特征C', '特征D', '特征E']
importance = np.array([0.35, 0.28, 0.18, 0.12, 0.07])

colors = plt.cm.viridis(importance / importance.max())
bars = ax4.barh(features, importance, color=colors)
ax4.set_xlabel('重要性')
ax4.set_title('特征重要性', fontsize=14, fontweight='bold')
ax4.invert_yaxis()

for i, (bar, val) in enumerate(zip(bars, importance)):
    ax4.text(val + 0.01, i, f'{val:.2f}', va='center')

plt.tight_layout()
plt.savefig('ml_performance_dashboard.png', dpi=150, bbox_inches='tight')
plt.show()

示例7:可访问颜色调色板

# 用于数据可视化的色盲安全调色板
import matplotlib.pyplot as plt
import seaborn as sns

# 定义可访问颜色方案
palettes = {
    'colorblind_safe': ['#0173B2', '#DE8F05', '#029E73', '#CC78BC', '#CA9161'],
    'high_contrast': ['#000000', '#E69F00', '#56B4E9', '#009E73', '#F0E442'],
    'okabe_ito': ['#E69F00', '#56B4E9', '#009E73', '#F0E442', '#0072B2', '#D55E00', '#CC79A7']
}

# 用可访问调色板测试可视化
data = [25, 20, 18, 15, 12, 10]
labels = ['A', 'B', 'C', 'D', 'E', 'F']

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

for ax, (name, colors) in zip(axes, palettes.items()):
    ax.bar(labels, data, color=colors[:len(data)])
    ax.set_title(f'{name.replace("_", " ").title()}', fontsize=12, fontweight='bold')
    ax.set_ylabel('值')

    # 为可访问性添加值标签
    for i, (label, value) in enumerate(zip(labels, data)):
        ax.text(i, value + 0.5, str(value), ha='center', va='bottom')

plt.tight_layout()
plt.savefig('accessible_palettes.png', dpi=150, bbox_inches='tight')