安装 belt CLI 技能: npx skills add belt-sh/cli
数据可视化
通过 inference.sh CLI 创建清晰、有效的数据可视化。
快速开始
需要 inference.sh CLI(belt)。安装说明
belt login
belt app run infsh/python-executor --input '{
"code": "import matplotlib.pyplot as plt\nimport matplotlib\nmatplotlib.use(\"Agg\")\n\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\"]\nrevenue = [42, 48, 55, 61, 72, 89]\n\nfig, ax = plt.subplots(figsize=(10, 6))\nax.bar(months, revenue, color=\"#3b82f6\", width=0.6)\nax.set_ylabel(\"Revenue ($K)\")\nax.set_title(\"Monthly Revenue Growth\", fontweight=\"bold\")\nfor i, v in enumerate(revenue):\n ax.text(i, v + 1, f\"${v}K\", ha=\"center\", fontweight=\"bold\")\nplt.tight_layout()\nplt.savefig(\"revenue.png\", dpi=150)\nprint(\"Saved\")"
}'
图表选择指南
哪种数据用哪种图表?
| 数据关系 | 最佳图表 | 绝不使用 |
|---|
| 随时间变化 | 折线图 | 饼图 |
| 比较类别 | 条形图(类别多时用水平条形) | 折线图 |
| 整体的一部分 | 堆叠条形图、树图 | 饼图(争议性,但条形始终更清晰) |
| 分布 | 直方图、箱线图 | 条形图 |
| 相关性 | 散点图 | 条形图 |
| 排名 | 水平条形图 | 垂直条形图、饼图 |
| 地理数据 | 等值线地图 | 条形图 |
| 随时间变化的组成 | 堆叠面积图 | 多个饼图 |
| 单一指标 | 大数字(KPI 卡片) | 任何图表(过度设计) |
| 流程/过程 | 桑基图 | 条形图 |
饼图问题
饼图几乎总是错误的选择:
❌ 饼图的问题:
- 难以比较相似大小的切片
- 不能显示超过 5-6 个类别
- 3D 饼图总是错的
- 无法读取精确值
✅ 改用:
- 水平条形图(易于比较)
- 堆叠条形图(整体的一部分)
- 树图(分层部分)
- 仅用表格(如果精度重要)
设计规则
坐标轴
| 规则 | 原因 |
|---|
| 条形图 Y 轴始终从 0 开始 | 防止误导视觉 |
| 折线图可以从 0 以上开始 | 当显示变化而非绝对值时 |
| 标注两个坐标轴 | 读者不应猜测单位 |
| 移除不必要的网格线 | 减少视觉噪音 |
| 使用水平标签 | 垂直文本难以阅读 |
| 条形图按值排序 | 除非有理由,否则不要使用字母顺序 |
颜色
| 原则 | 应用 |
|---|
| 每张图最多 5-7 种颜色 | 更多则难以阅读 |
| 突出显示一件事 | 将其他都设为灰色,用颜色凸显焦点 |
| 顺序用于数量 | 浅→深表示低→高 |
| 发散用于正/负 | 红 ← 中性 → 蓝 |
| 分类用于分组 | 不同的色调,相似的亮度 |
| 色盲友好 | 避免仅用红/绿——添加形状或标签 |
| 含义一致 | 如果蓝色 = 收入,则始终用蓝色表示收入 |
好的调色板
sequential = ["#eff6ff", "#bfdbfe", "#60a5fa", "#2563eb", "#1d4ed8"]
diverging = ["#ef4444", "#f87171", "#d1d5db", "#34d399", "#10b981"]
categorical = ["#3b82f6", "#f59e0b", "#10b981", "#8b5cf6", "#ef4444"]
cb_safe = ["#0077BB", "#33BBEE", "#009988", "#EE7733", "#CC3311"]
文本和标签
| 元素 | 规则 |
|---|
| 标题 | 陈述洞察,而非数据类型。"Q2 收入翻倍"而非"Q2 收入图表" |
| 标注 | 直接在图表上标出关键数据点 |
| 图例 | 尽量避免——直接在图表线条/条柱上标注 |
| 字号 | 最小 12px,演示用 14px+ |
| 数字格式 | 大数字用 K、M、B(42K 而非 42,000) |
| 数据标签 | 当精确值重要时,添加到条柱/点上 |
图表配方
折线图(时间序列)
belt app run infsh/python-executor --input '{
"code": "import matplotlib.pyplot as plt\nimport matplotlib\nmatplotlib.use(\"Agg\")\n\nfig, ax = plt.subplots(figsize=(12, 6))\nfig.patch.set_facecolor(\"white\")\n\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\nthis_year = [120, 135, 148, 162, 178, 195, 210, 228, 245, 268, 290, 320]\nlast_year = [95, 102, 108, 115, 122, 130, 138, 145, 155, 165, 178, 190]\n\nax.plot(months, this_year, color=\"#3b82f6\", linewidth=2.5, marker=\"o\", markersize=6, label=\"2024\")\nax.plot(months, last_year, color=\"#94a3b8\", linewidth=2, linestyle=\"--\", label=\"2023\")\nax.fill_between(range(len(months)), last_year, this_year, alpha=0.1, color=\"#3b82f6\")\n\nax.annotate(\"$320K\", xy=(11, 320), fontsize=14, fontweight=\"bold\", color=\"#3b82f6\")\nax.annotate(\"$190K\", xy=(11, 190), fontsize=12, color=\"#94a3b8\")\n\nax.set_ylabel(\"Revenue ($K)\", fontsize=12)\nax.set_title(\"Revenue grew 68% year-over-year\", fontsize=16, fontweight=\"bold\")\nax.legend(fontsize=12)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.grid(axis=\"y\", alpha=0.3)\nplt.tight_layout()\nplt.savefig(\"line-chart.png\", dpi=150)\nprint(\"Saved\")"
}'
水平条形图(比较)
belt app run infsh/python-executor --input '{
"code": "import matplotlib.pyplot as plt\nimport matplotlib\nmatplotlib.use(\"Agg\")\n\nfig, ax = plt.subplots(figsize=(10, 6))\n\ncategories = [\"Email\", \"Social\", \"SEO\", \"Paid Ads\", \"Referral\", \"Direct\"]\nvalues = [12, 18, 35, 22, 8, 5]\ncolors = [\"#94a3b8\"] * len(values)\ncolors[2] = \"#3b82f6\" # Highlight the winner\n\n# Sort by value\nsorted_pairs = sorted(zip(values, categories, colors))\nvalues, categories, colors = zip(*sorted_pairs)\n\nax.barh(categories, values, color=colors, height=0.6)\nfor i, v in enumerate(values):\n ax.text(v + 0.5, i, f\"{v}%\", va=\"center\", fontsize=12, fontweight=\"bold\")\n\nax.set_xlabel(\"% of Total Traffic\", fontsize=12)\nax.set_title(\"SEO drives the most traffic\", fontsize=16, fontweight=\"bold\")\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nplt.tight_layout()\nplt.savefig(\"bar-chart.png\", dpi=150)\nprint(\"Saved\")"
}'
KPI / 大数字卡片
belt app run infsh/html-to-image --input '{
"html": "<div style=\"display:flex;gap:20px;padding:20px;background:white;font-family:system-ui\"><div style=\"background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:24px;width:200px;text-align:center\"><p style=\"color:#64748b;font-size:14px;margin:0\">Monthly Revenue</p><p style=\"font-size:48px;font-weight:900;margin:8px 0;color:#1e293b\">$89K</p><p style=\"color:#22c55e;font-size:14px;margin:0\">↑ 23% vs last month</p></div><div style=\"background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:24px;width:200px;text-align:center\"><p style=\"color:#64748b;font-size:14px;margin:0\">Active Users</p><p style=\"font-size:48px;font-weight:900;margin:8px 0;color:#1e293b\">12.4K</p><p style=\"color:#22c55e;font-size:14px;margin:0\">↑ 8% vs last month</p></div><div style=\"background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:24px;width:200px;text-align:center\"><p style=\"color:#64748b;font-size:14px;margin:0\">Churn Rate</p><p style=\"font-size:48px;font-weight:900;margin:8px 0;color:#1e293b\">2.1%</p><p style=\"color:#ef4444;font-size:14px;margin:0\">↑ 0.3% vs last month</p></div></div>"
}'
热力图
belt app run infsh/python-executor --input '{
"code": "import matplotlib.pyplot as plt\nimport numpy as np\nimport matplotlib\nmatplotlib.use(\"Agg\")\n\nfig, ax = plt.subplots(figsize=(10, 6))\n\ndays = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\nhours = [\"9AM\", \"10AM\", \"11AM\", \"12PM\", \"1PM\", \"2PM\", \"3PM\", \"4PM\", \"5PM\"]\ndata = np.random.randint(10, 100, size=(len(hours), len(days)))\ndata[2][1] = 95 # Tuesday 11AM peak\ndata[2][3] = 88 # Thursday 11AM\n\nim = ax.imshow(data, cmap=\"Blues\", aspect=\"auto\")\nax.set_xticks(range(len(days)))\nax.set_yticks(range(len(hours)))\nax.set_xticklabels(days, fontsize=12)\nax.set_yticklabels(hours, fontsize=12)\n\nfor i in range(len(hours)):\n for j in range(len(days)):\n color = \"white\" if data[i][j] > 60 else \"black\"\n ax.text(j, i, data[i][j], ha=\"center\", va=\"center\", fontsize=10, color=color)\n\nax.set_title(\"Website Traffic by Day & Hour\", fontsize=16, fontweight=\"bold\")\nplt.colorbar(im, label=\"Visitors\")\nplt.tight_layout()\nplt.savefig(\"heatmap.png\", dpi=150)\nprint(\"Saved\")"
}'
数据叙事
叙事弧线
| 步骤 | 做法 | 示例 |
|---|
| 1. 背景 | 设定读者需要了解的信息 | "我们按月跟踪客户获取成本" |
| 2. 张力 | 展示问题或变化 | "Q3 客户获取成本增加了 40%" |
| 3. 解决 | 展示洞察或解决方案 | "但生命周期价值增长了 80%,因此单位经济改善了" |
标题即洞察
❌ 描述性标题(展示的内容):
"各产品线 Q3 收入"
"2024 年月活跃用户"
"客户满意度调查结果"
✅ 洞察性标题(数据的含义):
"企业级产品驱动了 70% 的收入增长"
"免费套餐发布后用户增长加速"
"支持响应时间是第一满意度驱动因素"
标注技巧
| 技巧 | 何时使用 |
|---|
| 调用标签 | 突出显示特定数据点("峰值:320K") |
| 参考线 | 显示目标/基准("目标:100K") |
| 阴影区域 | 标记时间段("产品发布窗口") |
| 箭头 + 文本 | 引起对趋势变化的注意 |
| 之前/之后线 | 显示事件的影响 |
暗色模式图表
belt app run infsh/python-executor --input '{
"code": "import matplotlib.pyplot as plt\nimport matplotlib\nmatplotlib.use(\"Agg\")\n\n# Dark theme\nplt.rcParams.update({\n \"figure.facecolor\": \"#0f172a\",\n \"axes.facecolor\": \"#0f172a\",\n \"axes.edgecolor\": \"#334155\",\n \"axes.labelcolor\": \"white\",\n \"text.color\": \"white\",\n \"xtick.color\": \"white\",\n \"ytick.color\": \"white\",\n \"grid.color\": \"#1e293b\"\n})\n\nfig, ax = plt.subplots(figsize=(12, 6))\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\"]\nvalues = [45, 52, 58, 72, 85, 98]\n\nax.plot(months, values, color=\"#818cf8\", linewidth=3, marker=\"o\", markersize=8)\nax.fill_between(range(len(months)), values, alpha=0.15, color=\"#818cf8\")\nax.set_title(\"MRR Growth: On track for $100K\", fontsize=18, fontweight=\"bold\")\nax.set_ylabel(\"MRR ($K)\", fontsize=13)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nax.grid(axis=\"y\", alpha=0.2)\n\nfor i, v in enumerate(values):\n ax.annotate(f\"${v}K\", (i, v), textcoords=\"offset points\", xytext=(0, 12), ha=\"center\", fontsize=11, fontweight=\"bold\")\n\nplt.tight_layout()\nplt.savefig(\"dark-chart.png\", dpi=150, facecolor=\"#0f172a\")\nprint(\"Saved\")"
}'
常见错误
| 错误 | 问题 | 修复 |
|---|
| 饼图 | 难以比较,总是误导 | 使用条形图或树图 |
| Y 轴不从 0 开始(条形图) | 夸大差异 | 条形图从 0 开始,折线图可以截断 |
| 颜色过多 | 视觉噪音,混乱 | 最多 5-7 种颜色,仅突出显示重要内容 |
| 无标题或通用标题 | 读者不了解洞察 | 标题 = 要点,而非数据类型 |
| 3D 图表 | 扭曲数据,显得不专业 | 始终使用 2D |
| 双重 Y 轴 | 误导,难以阅读 | 使用两个独立的图表 |
| 条形图按字母排序 | 隐藏了故事 | 按值排序(最大在前) |
| 坐标轴无标签 | 读者无法解读 | 始终标注单位 |
| 图表垃圾(装饰元素) | 分散数据注意力 | 移除一切不传递信息的内容 |
| 仅用红/绿进行颜色编码 | 色盲用户无法阅读 | 使用形状、图案或色盲友好调色板 |
相关技能
npx skills add inference-sh/skills@pitch-deck-visuals
npx skills add inference-sh/skills@technical-blog-writing
npx skills add inference-sh/skills@competitor-teardown
浏览所有应用:belt app list