Rust rsplotlib × 教程
Rust × Python · 高性能数据可视化

Rust 加速你的 Python 数据可视化

rsplotlib 是一款核心渲染引擎由 Rust 编写的高性能 Python 绘图库,提供与 Matplotlib 高度兼容的 API,在散点数组、批量参考线等场景下展现显著的性能优势。

14
完整教程
40+
代码示例
3~10x
性能提升
100%
API 兼容
Chapter 01

快速入门

了解 rsplotlib 的设计理念,完成环境配置并绘制你的第一张图表。

1.1项目简介

rsplotlib 是一款由 Rust 强力驱动的高性能 Python 绘图库,通过 PyO3 提供 Matplotlib 兼容的 Python API。其核心渲染引擎完全由 Rust 编写,在保持 Matplotlib 兼容接口的同时,利用 Rust 的内存安全和零成本抽象带来显著的性能提升。

⚙️
API 优先

保持与 Matplotlib 的高度兼容,降低迁移成本。

性能优先

将渲染、批量操作等关键路径下沉到 Rust 层。

零依赖

无需安装原生 Matplotlib,仅需 Python 解释器。

跨平台一致

在 macOS、Linux、Windows 上渲染质量完全一致。

1.2环境准备

系统要求

依赖 版本 说明
Python 3.8+ CPython 实现
Rust 1.70+ 从源码构建时需要
maturin 1.13+ Rust-Python 包构建工具

安装步骤

# 从 PyPI 安装(需 Python 3.8+)
pip install rsplotlib

# 升级到最新版本
pip install --upgrade rsplotlib
# uv 是更快的 Python 包管理器
# 安装 uv: https://docs.astral.sh/uv/
uv pip install rsplotlib

# 或在 uv 项目中添加依赖
uv add rsplotlib
# 1. 克隆仓库
git clone https://github.com/YJ-Niu/rsplotlib.git
cd rsplotlib

# 2. 安装 maturin 并构建
pip install maturin
maturin develop --release
# 使用项目构建脚本
./build_wheel.sh

# 安装生成的 wheel 包
pip install target/wheels/rsplotlib-*.whl
# 仅编译 Rust cdylib
cargo build --release

# macOS: 复制为 Python 扩展模块
cp target/release/librsplotlib.dylib \
   python/rsplotlib/rsplotlib.cpython-39-darwin.so

# 设置 PYTHONPATH
export PYTHONPATH="$PWD/python:$PYTHONPATH"

1.3第一个图表

让我们从最基础的折线图开始,体验 rsplotlib 的基本工作流程。

hello_rsplotlib.py
from rsplotlib import pyplot as plt
from rsplotlib.pylab import mpl

# 可选:配置中文显示字体
mpl.rcParams['font.sans-serif'] = ['PingFang SC', 'Microsoft YaHei']

# 创建 Figure 和 Axes
fig, ax = plt.subplots(figsize=(8, 6))

# 准备数据
x = [0, 1, 2, 3, 4, 5]
y = [1, 2, 4, 8, 16, 32]

# 绘制线条
ax.plot(x, y, label='指数增长', linewidth=2.0, color='steelblue')

# 装饰图表
ax.set_title('我的第一个 rsplotlib 图表')
ax.set_xlabel('时间 (秒)')
ax.set_ylabel('数值')
ax.legend()
ax.grid(True)

# 保存图表
fig.savefig('my_first_chart.png', dpi=300)
关键点:参数 figsize 控制图像英寸尺寸,dpi 控制每英寸点数。两者相乘决定最终像素分辨率。屏幕显示推荐 dpi=150,出版打印推荐 dpi=300
Chapter 02

核心概念

掌握 Figure 与 Axes 的对象模型,理解面向对象与模块级两种调用风格。

2.1Figure 与 Axes 对象

在 rsplotlib 中,绘图围绕两个核心对象组织:Figure 代表整个画布容器,Axes 代表包含坐标轴的具体绘图区域。一个 Figure 可以包含多个 Axes。

Figure · 画布
Axes 1 · 折线图
Axes 2 · 柱状图

2.2两种调用方式

面向对象方式(推荐)

显式创建 Figure 和 Axes 对象,所有操作通过对象方法调用。适用于大多数场景,尤其是复杂的多子图布局。

fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
ax.set_title('面向对象')
fig.savefig('oo.png')

模块级方式(简洁)

通过 plt.* 直接调用,内部自动管理 Figure/Axes。适用于快速脚本和简单绘图。

plt.figure()
plt.plot([1, 2, 3], [1, 4, 9])
plt.title('模块级')
plt.savefig('ml.png')
Chapter 03

基础图表类型

从折线到热力图,逐一掌握 rsplotlib 支持的全部基础图表。

3.1折线图 plot()

ax.plot(x, y, color=None, linewidth=None, linestyle=None, marker=None, label=None)
multi_lines.py
fig, ax = plt.subplots()
x = list(range(10))
ax.plot(x, [i for i in x], label='线性', color='steelblue', lw=2)
ax.plot(x, [i**2 for i in x], label='平方', color='darkorange', lw=2, ls='--')
ax.legend()
fig.savefig('lines.png')

3.2散点图 scatter() — Rust 层数组支持

Rust 层统一渲染 · 零 Python 循环
当传入颜色数组或大小时,rsplotlib 在 Rust 层统一处理 ScatterMulti。Matplotlib 会退化为 Python 循环,效率较低。
scatter_multi.py
import random
from rsplotlib import pyplot as plt

random.seed(42)
n = 100
x = [random.gauss(0, 1) for _ in range(n)]
y = [random.gauss(0, 1) for _ in range(n)]

# 每点独立颜色 + 每点独立大小(Rust 层处理)
colors = ['crimson' if xi > 0 else 'steelblue' for xi in x]
sizes = [max(10, abs(xi * yi * 50)) for xi, yi in zip(x, y)]

fig, ax = plt.subplots()
ax.scatter(x, y, c=colors, s=sizes, alpha=0.7, marker='o')
ax.set_title('每点独立颜色和大小(Rust 层批量渲染)')
fig.savefig('scatter_multi.png')

支持的标记形状

o圆形
s正方形
^上三角
v下三角
D菱形
*星形
+十字
x叉号

3.3柱状图、直方图与饼图

柱状图 bar()

categories = ['A', 'B', 'C']
values = [23, 45, 17]
fig, ax = plt.subplots()
ax.bar(categories, values, color='steelblue')
fig.savefig('bar.png')

直方图 hist()

import random
random.seed(0)

data = [random.gauss(0, 1)
        for _ in range(1000)]
fig, ax = plt.subplots()
ax.hist(data, bins=30,
        color='steelblue', alpha=0.7)
fig.savefig('hist.png')

饼图 pie()

labels = ['苹果', '香蕉', '橙子']
sizes = [30, 20, 50]
colors = ['#FF6B6B', '#FFD93D', '#6BCB77']
fig, ax = plt.subplots()
ax.pie(sizes, labels=labels, colors=colors)
fig.savefig('pie.png')

3.4更多图表

barh(categories, values, ...)

水平柱状图。

boxplot(data_list, ...)

箱线图,展示分布与异常点。

fill_between(x, y1, y2, color, alpha)

填充两条曲线之间的区域。

errorbar(x, y, yerr=None, fmt='-o')

带误差棒的折线/散点图。

stem(x, y)

茎图(信号处理常用)。

step(x, y, where='post')

阶梯图(离散过程)。

imshow(data, cmap='viridis')

热力图 / 图像显示。

stackplot(x, *series, labels=...)

堆叠面积图。

semilogx / semilogy / loglog(x, y, ...)

对数坐标图。

Chapter 04

辅助元素与标注

参考线、高亮区域、箭头标注 — 让图表讲述完整的故事。

4.1参考线 axhline / axvline

fig, ax = plt.subplots()
ax.plot(list(range(-10, 11)),
        [xi**2 for xi in range(-10, 11)], 'b-', lw=2)
ax.axhline(y=25, color='red', linestyle='--', linewidth=1.5)
ax.axvline(x=0, color='green', linestyle=':', linewidth=1.5)
fig.savefig('ref_lines.png')

4.2区间高亮 axhspan / axvspan

🎯
新增功能 · 区间高亮
使用 axhspan / axvspan 在图表中填充水平或垂直带,常用于强调特定数值范围或极值区域。
fig, ax = plt.subplots()
x = list(range(-5, 6))
ax.plot(x, [xi**2 for xi in x], 'b-', lw=2)

# 垂直区间:高亮 x ∈ [-1, 1] 的最小值区域
ax.axvspan(-1, 1, color='gold', alpha=0.3)

# 水平区间:高亮 y ∈ [0, 5] 的低值区域
ax.axhspan(0, 5, color='lightgreen', alpha=0.2)
fig.savefig('spans.png')

4.3任意斜率参考线 axline

通过两点确定一条直线并延伸至整个图表边界,适合标注线性趋势或对角线。

fig, ax = plt.subplots()
ax.plot([-5, 5], [-5, 5], 'b-')

# 对角线:从 (0,0) 到 (1,1),自动延伸至整张图
ax.axline((0, 0), (1, 1), color='red', linestyle='--')

# 另一条:从 (0, 5) 到 (1, 3),斜率为 -2
ax.axline((0, 5), (1, 3), color='green', linestyle=':')
fig.savefig('axline.png')

4.4批量参考线 hlines / vlines

⚙️
Rust 层批量实现 · 性能优化
内部循环在 Rust 层一次性完成,避免 N 次 Python→Rust 跨语言调用,大数据量场景下优势尤为明显。
传统方式(不推荐)
fig, ax = plt.subplots()
for y_pos in [2, 4, 6, 8]:
    ax.axhline(y_pos)  # N 次调用
rsplotlib 方式
fig, ax = plt.subplots()
ax.hlines([2, 4, 6, 8],
          color='steelblue', linestyle='--')  # 1 次调用
ax.vlines([2, 4, 6, 8],
          color='darkorange', linestyle=':')

4.5箭头标注 annotate

annotations.py
fig, ax = plt.subplots()
x = list(range(-10, 11))
ax.plot(x, [xi**3 - 3*xi for xi in x], 'b-', lw=2)

# 从文本位置绘制箭头指向目标点
ax.annotate('局部极大值', xy=(-1, 2), xytext=(-8, 500),
            fontsize=11, color='red',
            arrowprops={'arrowstyle': '->', 'arrowsize': 1.0})

ax.annotate('原点', xy=(0, 0), xytext=(5, 200),
            fontsize=11, color='darkgreen')
fig.savefig('annotations.png')
提示:xy 是被标注点的坐标,xytext 是文本放置位置。提供 xytext 时会自动从文本位置绘制箭头到 xy

4.6数学公式 mathtext

在标题、轴标签、textannotate、图例标签 与柱标签中,用 $...$ 包裹即可渲染 LaTeX 风格的数学公式, 支持上标 ^、下标 _\frac\sqrt[n]{}、希腊字母与重音等。

mathtext_demo.py
fig, ax = plt.subplots()
x = list(range(0, 11))
ax.plot(x, [xi**2 for xi in x], 'b-', lw=2)

# 标题与轴标签中的数学公式($...$)
ax.set_title(r'二次函数 $y = x^2 + \frac{1}{2}$')
ax.set_xlabel(r'时间 $t$ (秒)')
ax.set_ylabel(r'范数 $\sqrt{x^2 + y^2}$')

# 文本标注中的希腊字母与上下标
ax.text(2, 80, r'$\alpha_i + \beta^2$', fontsize=14)
fig.savefig('mathtext.png')
提示:公式字符串建议加 r'' 原始字符串前缀,避免 \f\s 等被 Python 当作转义序列。生效位置:标题、轴标签、 textannotate、图例标签、柱标签 (bar_label)。
Chapter 05

子图与布局

在同一张画布上组织多个绘图区域,从网格布局到双坐标轴。

5.1创建子图 subplots()

plt.subplots(nrows, ncols) 是创建子图网格的常用方式。返回的 axes 是一个扁平化列表,按行优先顺序排列。当 nrows=ncols=1 时返回单个 Axes 对象(不是列表)。

subplots_grid.py
import random
random.seed(0)

# 创建 2x2 子图网格
fig, axes = plt.subplots(2, 2, figsize=(12, 8))

# 展平为一维,按行优先顺序访问
# axes[0] 左上, axes[1] 右上, axes[2] 左下, axes[3] 右下
axes = axes.flatten()
axes[0].plot([1,2,3], [1,4,9], color='steelblue', lw=2)
axes[0].set_title('1) 折线图')
axes[0].grid(True, ls='--', lw=0.5)

axes[1].bar(['A','B','C','D'], [3,7,2,5], color='darkorange')
axes[1].set_title('2) 柱状图')

# 使用 Rust 加速的散点图(每点独立颜色/大小)
xs = [random.gauss(0, 1) for _ in range(150)]
ys = [random.gauss(0, 1) for _ in range(150)]
cs = ['crimson' if x > 0 else 'steelblue' for x in xs]
axes[2].scatter(xs, ys, c=cs, s=25, alpha=0.7)
axes[2].set_title('3) 散点图(Rust 批量渲染)')

axes[3].hist([random.gauss(0,1) for _ in range(500)],
             bins=25, color='forestgreen', alpha=0.7)
axes[3].set_title('4) 直方图')

fig.tight_layout()  # 自动调整间距避免标签重叠
fig.savefig('subplots_2x2.png', dpi=200)
注意:subplots(1, 1) 时返回单个 Axes;当 subplots(2, 2) 时返回形状为 (2, 2) 的二维数组。可用 axes[i, j] 二维索引,或用 axes.flatten() / axes.ravel() 展平成一维列表后按 axes[k] 顺序访问。

5.2不规则子图布局

使用 fig.add_subplot(nrows, ncols, index) 可以创建跨多行多列的子图,类似 Matplotlib 的 add_subplot。当 index 为元组时表示占据连续位置。

irregular_layout.py
fig = plt.figure(figsize=(10, 6))

# 顶部跨两列的大图(占据第 1 行)
ax_top = fig.add_subplot(2, 2, (1, 2))
ax_top.plot([1,2,3,4], [10,20,15,25], color='steelblue', lw=2)
ax_top.set_title('主图:跨整行的趋势')
ax_top.grid(True, ls='--', lw=0.5)

# 左下角的小图
ax_bl = fig.add_subplot(2, 2, 3)
ax_bl.bar(['A','B'], [3,7], color='darkorange')
ax_bl.set_title('子图 A')

# 右下角的小图
ax_br = fig.add_subplot(2, 2, 4)
ax_br.scatter([1,2,3], [3,1,2], c=['red','green','blue'], s=50)
ax_br.set_title('子图 B')

fig.tight_layout()
fig.savefig('irregular.png', dpi=200)

5.3双坐标轴 twinx / twiny

在同一 Axes 上绘制量纲不同的两组数据(例如温度与湿度、销量与销售额),但共享一个 X 轴。

twinx_demo.py
fig, ax1 = plt.subplots()

# 主坐标轴(左 Y):温度
ax1.plot([1,2,3,4,5], [10,15,13,17,20],
         'b-o', lw=2, label='温度')
ax1.set_ylabel('温度 (°C)', color='blue')
ax1.tick_params(axis='y', labelcolor='blue')

# 共享 X 轴,创建独立的右侧 Y 轴
ax2 = ax1.twinx()
ax2.plot([1,2,3,4,5], [100,120,110,130,150],
         'r-s', lw=2, label='湿度')
ax2.set_ylabel('湿度 (%)', color='red')
ax2.tick_params(axis='y', labelcolor='red')

# 两轴各自绘制图例,分置左右上角避免重叠
ax1.legend(loc='upper left')
ax2.legend(loc='upper right')

ax1.set_title('温湿度联合监测')
fig.tight_layout()
fig.savefig('twinx.png')
提示:双坐标轴在物理量纲差异巨大时尤其有用,但应谨慎使用 — 如果两轴数据可以归一化到同一尺度,建议使用单坐标轴加归一化数据。

5.4边距与子图间距

fig, axes = plt.subplots(2, 2)

# 自动调整子图间距,避免标签重叠
fig.tight_layout()

# 手动精细控制边距
fig.subplots_adjust(left=0.1, right=0.95, top=0.9, bottom=0.1,
                    wspace=0.3, hspace=0.4)

5.5Figure 尺寸控制

# 创建时指定英寸尺寸
fig, ax = plt.subplots(figsize=(10, 6))

# 动态调整尺寸(像素)
fig2, ax2 = plt.subplots()
fig2.set_size(1200, 800)

# 调整宽高比
fig3, ax3 = plt.subplots(figsize=(8, 4))  # 宽 2:1
常用尺寸参考:单栏论文图 3.5 英寸、双栏 7 英寸、海报 16 英寸、PPT 16:9 推荐 13.33×7.5 英寸。
Chapter 05.5

刻度与精调

精确控制刻度位置、对数坐标、次刻度与网格,让坐标系的表达更专业。

5.6自定义刻度位置与标签

通过 set_xticks / set_xticklabels 精确控制刻度位置和显示文本。

custom_ticks.py
fig, ax = plt.subplots()
x = list(range(0, 11))
y = [xi**2 for xi in x]
ax.plot(x, y, 'b-o', lw=2)

# 自定义 X 轴刻度位置
ax.set_xticks([0, 2, 4, 6, 8, 10])

# 自定义 X 轴刻度标签
ax.set_xticklabels(['零', '贰', '肆', '陆', '捌', '拾'],
                  fontsize=12)

# 旋转 X 轴标签以避免重叠
# ax.set_xticklabels(labels, rotation=45, ha='right')

# 设置 Y 轴刻度
ax.set_yticks([0, 25, 50, 75, 100])
ax.grid(True, ls='--', lw=0.5)
fig.savefig('custom_ticks.png')

5.7对数与线性坐标

log_scales.py
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
x = list(range(1, 100))
y = [xi**2.5 for xi in x]

# 线性坐标(默认)
axes[0].plot(x, y, color='steelblue', lw=2)
axes[0].set_title('线性 - linear')
axes[0].grid(True, ls='--', lw=0.5)

# 半对数:X 线性、Y 对数(plot 后用 set_yscale 切换刻度)
axes[1].plot(x, y, color='darkorange', lw=2)
axes[1].set_yscale('log')
axes[1].set_title('半对数 - semilogy')
axes[1].grid(True, ls='--', lw=0.5)

# 双对数:X、Y 均为对数
axes[2].plot(x, y, color='forestgreen', lw=2)
axes[2].set_xscale('log')
axes[2].set_yscale('log')
axes[2].set_title('双对数 - loglog')
axes[2].grid(True, ls='--', lw=0.5)

fig.tight_layout()
fig.savefig('log_scales.png')
使用场景:线性用于直观比较;半对数用于指数增长数据(如疫情传播、金融收益);双对数用于幂律分布(如城市人口、地震强度)。

5.8次刻度与边框

fig, ax = plt.subplots()
x = list(range(10))
y = [xi**2 for xi in x]
ax.plot(x, y, 'b-o', lw=2)

# 启用次刻度(在主刻度之间显示更细的刻度)
ax.minorticks_on()

# 网格:自定义线型 / 线宽 / 颜色
ax.grid(True, ls='--', lw=0.8, c='#999999')

# 边框:隐藏上、右边框,仅保留左、下
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

fig.savefig('ticks_box.png')

5.9网格定制

fig, ax = plt.subplots()
ax.plot([0, 10], [0, 10], 'b-o', lw=2)

# 基础网格
ax.grid(True)

# 一次性定制:颜色 c / 线型 ls / 线宽 lw / 方向 axis
ax.grid(True, c='#999999', ls='--', lw=0.8, axis='both')

fig.savefig('grid.png')
Chapter 06

样式与配置

通过 rcParams 定制全局样式、字体与颜色系统。

6.1rcParams 全局配置

from rsplotlib.pylab import mpl

# 设置 sans-serif 字体族优先级列表
mpl.rcParams['font.sans-serif'] = ['PingFang SC', 'Microsoft YaHei', 'Arial']

# 设置默认字体大小
mpl.rcParams['font.size'] = 12

# 设置线条默认宽度
mpl.rcParams['lines.linewidth'] = 2.0

# 之后创建的图表会自动应用以上配置
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9], label='示例')
ax.legend()
fig.savefig('rcparams_demo.png')
配置项 默认值 说明
font.sans-serif 系统默认 无衬线字体列表
font.size 10 默认字体大小
lines.linewidth 1.5 默认线宽
figure.dpi 100 Figure 默认 DPI

6.2字体与国际化

rsplotlib 自动跨平台检测系统字体,优先选择覆盖范围最广的字体,确保整行文本由同一字体渲染,保持视觉一致性。

macOS 推荐

Arial Unicode MS、PingFang SC、Helvetica、Arial

Linux 推荐

DejaVu Sans、Noto Sans CJK SC、WenQuanYi Micro Hei

Windows 推荐

Microsoft YaHei、SimHei、SimSun、Arial

自定义字体

通过 _rs.register_sans_serif_font(path) 注册任意 .ttf / .otf

6.3plt.style 模块

与 Matplotlib 兼容的样式系统,通过 plt.style 模块管理全局样式。

from rsplotlib import pyplot as plt

# 查看可用样式
print(plt.style.available())

# 应用预设样式
plt.style.use('default')

# 之后创建的图表会套用该样式
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [2, 1, 3])
fig.savefig('style_demo.png')
Chapter 07

输出与分辨率

掌握 savefig、dpi 参数,精准控制图像输出质量与文件大小。

7.1savefig 保存图像

fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])

fig.savefig('default.png')                 # 使用 Figure 默认 DPI
fig.savefig('screen.png', dpi=150)         # 屏幕显示
fig.savefig('print.png', dpi=300)          # 出版级
fig.savefig('vector.svg')                 # 矢量格式
格式 扩展名 DPI 建议 特点
PNG(低) .png 72-100 文件小、位图
PNG(屏幕) .png 150 中等、网页/文档
PNG(出版) .png 300 高分辨率、打印
SVG .svg 不适用 矢量、无限缩放

7.2像素尺寸计算公式

像素宽度 = figsize 宽度 (英寸) × dpi
例如:figsize=(8, 6) × dpi=300 → 2400 × 1800 px
例如:figsize=(8, 6) × dpi=150 → 1200 × 900 px

7.3批量保存多张图

实际项目经常需要批量生成图表(例如生成报告中的所有图、批量处理实验结果)。以下是常见的批量保存模式。

batch_save.py
import os
from rsplotlib import pyplot as plt

os.makedirs('output', exist_ok=True)

# 准备若干数据集:(x, y, 标题)
datasets = [
    (list(range(10)), [i**2 for i in range(10)], '二次曲线'),
    (list(range(10)), [i*3 for i in range(10)], '线性趋势'),
]

# 方式一:循环生成并保存
for i, (x, y, title) in enumerate(datasets):
    fig, ax = plt.subplots(figsize=(8, 6))
    ax.plot(x, y, lw=2)
    ax.set_title(title)
    fig.savefig(f'output/chart_{i:03d}.png', dpi=150)

# 方式二:固定数据生成多视角
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, style in zip(axes, ['default', 'dark_background', 'ggplot']):
    plt.style.use(style)
    ax.plot(x, y)
    ax.set_title(style)
fig.savefig('output/styles.png', dpi=200)

# 方式三:保存为 SVG 便于后期编辑
fig.savefig('output/chart.svg')
性能建议:批量保存时尽量复用 Figure 对象,或者使用 fig.clf() 清空再绘制。避免反复调用 plt.subplots() 创建新 Figure,频繁的内存分配会影响性能。

7.4内存与文件大小优化

场景 建议 理由
大量小图(图标、缩略图) dpi=72-100 + PNG 文件小、足够清晰
网页展示 dpi=150 + PNG Retina 屏最佳
学术论文 / 印刷 dpi=300 + PNG 出版标准
需要后期编辑 SVG 矢量 无损缩放
超大数据点 降低 dpi 或缩小 figsize 避免文件过大
Chapter 07.5

颜色系统深入

掌握命名颜色、十六进制、RGB 元组,以及如何在图表中组合出和谐的色彩。

7.5颜色格式与命名

rsplotlib 支持以下颜色格式:

  • 命名颜色'red''steelblue''darkorange' 等 CSS 颜色名
  • 十六进制'#FF6B6B''#4ECDC4'
  • 简写十六进制'#F6B' 等同于 '#FF66BB'

常用命名颜色速查

■ red / crimson

警告、错误、负向

■ teal / cyan

技术、平衡、中性

■ gold / yellow

注意、高亮、警示

■ green

正向、成功、增长

■ blue / steelblue

数据、中性、可信

■ purple / violet

创意、特殊类别

7.6调色板与配色方案

palette.py
# 推荐调色板:Material Design 风格
PALETTE_MATERIAL = [
    '#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A',
    '#98D8C8', '#FFD93D', '#6BCB77', '#C9A7EB'
]

# 推荐调色板:科学论文风格(柔和不刺眼)
PALETTE_SCIENCE = [
    '#4D96FF', '#6BCB77', '#FF6B6B', '#FFD93D',
    '#9B59B6', '#1ABC9C', '#E67E22', '#34495E'
]

# 推荐调色板:商业演示风格
PALETTE_BUSINESS = [
    '#1F4E79', '#2E75B6', '#5B9BD5', '#9DC3E6',
    '#BDD7EE', '#DEEBF7'
]

fig, ax = plt.subplots()
for i, color in enumerate(PALETTE_MATERIAL):
    ax.plot([i, i+1], [i, i+1], color=color, lw=3, label=f'颜色 {i+1}')
ax.legend(ncol=2)
fig.savefig('palette.png')
配色原则:分类数据使用对比鲜明的颜色(多色);顺序数据使用同一色相的不同明度(单色渐变);发散数据使用双色发散(如红-白-蓝)。避免红绿同时使用(色盲不友好)。

7.7透明色与 alpha 通道

import random
random.seed(0)

fig, ax = plt.subplots()

# 两簇相互重叠的散点:alpha=0.5 使密集区域自然加深,重叠一目了然
xs1 = [random.gauss(0, 1) for _ in range(500)]
ys1 = [random.gauss(0, 1) for _ in range(500)]
xs2 = [random.gauss(1.5, 1) for _ in range(500)]
ys2 = [random.gauss(1.5, 1) for _ in range(500)]

ax.scatter(xs1, ys1, c='steelblue', s=30, alpha=0.5, label='簇 1')
ax.scatter(xs2, ys2, c='darkorange', s=30, alpha=0.5, label='簇 2')
ax.legend()
ax.set_title('半透明叠加(alpha=0.5)')
fig.savefig('alpha.png')
Chapter 07.6

图像、颜色映射与颜色条

用 colormap 为矩阵上色、显示图像、添加颜色条,并处理跨数量级数据的归一化。

7.8内置颜色映射 colormap

颜色映射(colormap)把数值映射到颜色,用于 imshow 热力图与 scatter 的数值上色。通过 cmap= 指定名称,任意名称加 _r 后缀即可反转方向(如 'viridis_r')。

colormap.py
import math
from rsplotlib import pyplot as plt

# 构造一个 2D 矩阵
data = [[math.sin(i/5) * math.cos(j/5)
         for j in range(60)] for i in range(40)]

fig, ax = plt.subplots()
ax.imshow(data, cmap='viridis')     # 感知均匀,推荐默认
fig.savefig('cmap.png')
类别 colormap 适用数据
感知均匀 viridis · plasma · inferno · magma · cividis 顺序数据(推荐)
顺序渐变 Blues · Greens · Reds · hot · cool · gray 单向递增量
发散 coolwarm · RdBu 有中心/正负的数据
其他 jet · terrain · twilight 特殊场景
建议:优先选择感知均匀的 viridis / plasma 系列;避免 jet —— 它在视觉上会制造不存在的边界。

7.9显示图像 imshow

imshow 既能把 2D 矩阵渲染成热力图(经 cmap 上色), 也能直接绘制 3D 的 RGB(A) 图像。

imshow_demo.py
import math
from rsplotlib import pyplot as plt

# 二维高斯
data = [[math.exp(-((i-20)**2 + (j-30)**2) / 200)
         for j in range(60)] for i in range(40)]

fig, ax = plt.subplots(figsize=(8, 5))
ax.imshow(
    data,
    cmap='inferno',
    origin='lower',          # 首行画在底部(默认 'upper')
    aspect='auto',           # 填满子图框(默认 'equal')
    interpolation='bilinear', # 平滑上采样(默认 'antialiased')
    vmin=0, vmax=1,           # 颜色映射值域
)
ax.set_title('二维高斯分布')
fig.savefig('imshow.png', dpi=150)
RGB 图像:传入形状为 (H, W, 3/4) 的 3D 数组时按像素颜色直接绘制(浮点取 [0,1]、整数取 [0,255]),此时 cmap 不生效。

7.10颜色条 colorbar

plt.colorbar() / fig.colorbar() 基于最近一次可映射绘制(imshowscatter 的数值 c)记录的 (cmap, vmin, vmax) 渲染。

import math
data = [[math.sin(i/5) * math.cos(j/5)
         for j in range(60)] for i in range(40)]

fig, ax = plt.subplots()
ax.imshow(data, cmap='viridis')

# 常用参数:label / shrink / aspect / pad / orientation / location
plt.colorbar(label='强度', shrink=0.8, aspect=20, pad=0.03)
fig.savefig('colorbar.png')
支持参数:locationorientationshrinkaspectpadfractionlabelextendticksformat

7.11归一化 LogNorm / Normalize

当数据跨越多个数量级时,线性上色会让小值被淹没。用对数归一化 LogNorm 可让颜色条刻度呈 10 的幂。归一化类从 rsplotlib.colors 导入。

norm.py
from rsplotlib import pyplot as plt
from rsplotlib.colors import LogNorm, Normalize

# LogNorm 要求正值,构造一个跨数量级的矩阵(1 ~ 2400)
data = [[(i + 1) * (j + 1)
         for j in range(60)] for i in range(40)]

fig, ax = plt.subplots()

# 对数归一化:适合跨数量级的数据
ax.imshow(data, cmap='magma', norm=LogNorm(vmin=1, vmax=1000))
plt.colorbar(label='对数刻度')

# 线性归一化:等价于直接给 vmin/vmax
# ax.imshow(data, cmap='magma', norm=Normalize(vmin=0, vmax=500))
fig.savefig('lognorm.png')
注意:LogNorm 要求数据为正值,非正值会被视为越界。缺省 vmin/vmax 时按数据的最小正值 / 最大值自动推断。

7.12图像读写 imread / imsave

image_io.py
import math
from rsplotlib import pyplot as plt

# 先用 imsave 生成一张示例图片(无坐标轴/边距,像素尺寸=数组尺寸)
data = [[math.sin(i/5) * math.cos(j/5)
         for j in range(60)] for i in range(40)]
plt.imsave('gray.png', data, cmap='gray', origin='lower')

# 读取图像 -> ndarray:PNG 返回 [0,1] 浮点,其余格式返回 [0,255] 整数
img = plt.imread('gray.png')       # 形状 (H, W, 3/4) 或灰度 (H, W)

fig, ax = plt.subplots()
ax.imshow(img)                     # 直接显示读入的图像
fig.savefig('shown.png')

# 也可另存为其他格式:格式由 format 或扩展名决定,支持 PNG / JPEG
plt.imsave('out.jpg', data, cmap='gray', format='jpeg', dpi=150)
提示:imsave 输出图片的像素尺寸等于 数组尺寸(N 列 → 宽、M 行 → 高),格式由 format 或扩展名决定,支持 PNG / JPEG。
Chapter 08

API 速览

快速查询常用 Axes 方法与模块级函数。

8.1Axes 装饰方法

set_title(title, fontsize=None, color=None)

设置图表标题。

set_xlabel / set_ylabel(label, fontsize=None, color=None)

设置坐标轴标签。

set_xlim / set_ylim(xmin, xmax)

设置坐标范围。

grid(True/False, linestyle=None, alpha=None)

显示/隐藏网格线。

legend()

显示图例(绘图时需传入 label)。

text(x, y, text, fontsize=None, color=None)

在指定坐标添加文本。

annotate(text, xy, xytext=None, arrowprops=None)

添加带箭头的标注。

set_xticks / set_yticks(ticks, labels=None)

设置刻度位置和标签。

set_xscale / set_yscale(scale)

设置坐标尺度('linear' / 'log')。

Chapter 09

性能与架构

深入理解 Rust + Python 混合架构如何实现高性能渲染。

9.1分层架构

Python API 兼容层
pyplot.py · Matplotlib 兼容 API
api.py · 参数归一化与别名映射
_patch_* · 方法补丁 / 动态分发
PyO3
Rust 高性能核心
lib.rs · 模块注册 / 字体系统
figure.rs · Figure 对象 / 渲染调度
axes.rs · Axes 对象 / 数据解析
elements.rs · PlotElement 枚举 (Line, ScatterMulti, Bar…)
axes_render_elements.rs · plotters 渲染引擎
pyfuncs.rs · 模块级函数暴露

9.2性能对比

功能 传统 Matplotlib rsplotlib
scatter(c=颜色数组) Python 遍历每点 Rust ScatterMulti 统一渲染
scatter(s=大小数组) Python 遍历每点 Rust 统一大小数组处理
批量水平线 (1000 条) 1000 次 axhline 调用 1 次 hlines Rust 批量调用
savefig(dpi=300) 无 / Python 缩放 Rust 直接写入 PNG DPI 元数据

9.3PlotElement 数据结构

在 Rust 层,所有绘图元素统一由 PlotElement 枚举管理,渲染时由 axes_render_elements 统一调度到 plotters。

// elements.rs — 核心数据结构(概念)
pub enum PlotElement {
    Line { ... },
    ScatterMulti { x, y, s_list, c_list, marker, alpha, ... },
    Bar { ... },
    Histogram { ... },
    HSpan { y1, y2, color, alpha },
    VSpan { x1, x2, color, alpha },
    AxLine { xy1, xy2, color, linestyle, linewidth },
    Arrow { x1, y1, x2, y2, color, linewidth, head_size },
    Text { x, y, text, fontsize, color },
    FillBetween { ... },
}
Chapter 10

综合实战

将前面所学综合应用,绘制一张完整的数据分析报告图。

10.1数据分析报告

sales_report.py
from rsplotlib import pyplot as plt
from rsplotlib.pylab import mpl
import random

mpl.rcParams['font.sans-serif'] = ['PingFang SC']

# 2x2 子图网格
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes = axes.flatten()  # 展平为一维,按 axes[0..3] 访问

# 子图 1:季度趋势折线
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
axes[0].plot(quarters, [120, 150, 180, 210], marker='o',
                color='steelblue', lw=2, label='产品A')
axes[0].plot(quarters, [80, 95, 110, 130], marker='s',
                color='darkorange', lw=2, label='产品B')
axes[0].axvspan(2.5, 3.5, color='gold', alpha=0.2)  # 突出 Q4
axes[0].set_title('季度销售趋势')
axes[0].legend()
axes[0].grid(True, ls='--', lw=0.5)

# 子图 2:Q4 销量占比饼图
axes[1].pie([210, 130, 250],
              labels=['产品A', '产品B', '产品C'],
              colors=['steelblue', 'darkorange', 'forestgreen'])
axes[1].set_title('Q4 销售占比')

# 子图 3:产品A 销售分布(每点独立颜色)
random.seed(42)
xs = [random.gauss(150, 30) for _ in range(200)]
ys = [random.gauss(0, 1) for _ in range(200)]
cs = ['red' if y > 0 else 'blue' for y in ys]
axes[2].scatter(xs, ys, c=cs, s=20, alpha=0.6)
axes[2].set_title('产品A 销售分布')
axes[2].set_xlabel('销售额')
axes[2].set_ylabel('偏差')

# 子图 4:年度总销量对比 + 标注
axes[3].bar(['产品A', '产品B', '产品C'],
             [120+150+180+210, 80+95+110+130, 200+180+220+250],
             color=['steelblue', 'darkorange', 'forestgreen'])
axes[3].annotate('冠军', xy=(2, 850), xytext=(1, 880),
                 color='red', arrowprops={'arrowstyle': '->'})
axes[3].set_title('年度总销量对比')
axes[3].set_ylabel('总销量')

fig.tight_layout()
fig.savefig('sales_report.png', dpi=200)
print('报告已生成')

10.2机器学习可视化

机器学习中常见的可视化场景:训练曲线、混淆矩阵、特征重要性、决策边界等。

训练曲线

import random
random.seed(0)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))

# 模拟训练历史
epochs = list(range(1, 51))
train_loss = [2.5 * 0.95**e + random.gauss(0, 0.05) for e in epochs]
val_loss = [2.6 * 0.94**e + random.gauss(0, 0.08) for e in epochs]
train_acc = [1 - 0.6 * 0.96**e for e in epochs]
val_acc = [1 - 0.7 * 0.95**e for e in epochs]

ax1.plot(epochs, train_loss, label='训练', color='steelblue', lw=2)
ax1.plot(epochs, val_loss, label='验证', color='darkorange', lw=2)
ax1.set_xlabel('Epoch')
ax1.set_ylabel('Loss')
ax1.set_title('损失曲线')
ax1.legend()
ax1.grid(True, ls='--', lw=0.5)

ax2.plot(epochs, train_acc, label='训练', color='steelblue', lw=2)
ax2.plot(epochs, val_acc, label='验证', color='darkorange', lw=2)
ax2.set_xlabel('Epoch')
ax2.set_ylabel('Accuracy')
ax2.set_title('准确率曲线')
ax2.legend()
ax2.grid(True, ls='--', lw=0.5)

fig.tight_layout()
fig.savefig('training_curves.png', dpi=200)

特征重要性条形图

features = ['用户活跃度', '消费金额', '停留时长', '点击率', '访问频率']
importance = [0.32, 0.28, 0.18, 0.14, 0.08]

# 按重要性排序
sorted_data = sorted(zip(features, importance), key=lambda x: x[1])
features_sorted = [x[0] for x in sorted_data]
values_sorted = [x[1] for x in sorted_data]

fig, ax = plt.subplots()
ax.barh(features_sorted, values_sorted, color='steelblue')
ax.set_xlabel('重要性')
ax.set_title('特征重要性排序')
fig.savefig('feature_importance.png')

10.3金融与时间序列

finance.py
import math
import random
random.seed(0)

fig, ax = plt.subplots(figsize=(12, 6))

# 模拟 252 个交易日(1 年)
days = list(range(252))
price = [100]
for _ in range(251):
    ret = random.gauss(0.0005, 0.02)
    price.append(price[-1] * (1 + ret))

ax.plot(days, price, color='steelblue', lw=1.5, label='股价')

# 30 日移动平均线
ma30 = [sum(price[max(0, i-29):i+1]) / min(i+1, 30) for i in days]
ax.plot(days, ma30, color='darkorange', lw=2, label='30 日均线')

# 填充股价与均线之间的区域
ax.fill_between(days, price, ma30,
                color='steelblue', alpha=0.15, label='价格与均线之差')

ax.set_xlabel('交易日')
ax.set_ylabel('价格')
ax.set_title('股票价格时间序列(含移动平均线)')
ax.legend(loc='upper left')
ax.grid(True, ls='--', lw=0.5)
fig.tight_layout()
fig.savefig('stock.png', dpi=200)

10.4科学绘图模板

学术论文常用模板:清晰、克制、信息密度高。配色简洁、坐标轴清晰、图例可读。

science_template.py
import math
from rsplotlib import pyplot as plt
from rsplotlib.pylab import mpl

# === 学术论文风格模板 ===
mpl.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans']
mpl.rcParams['font.size'] = 11
mpl.rcParams['axes.linewidth'] = 1.0
mpl.rcParams['lines.linewidth'] = 1.5

fig, ax = plt.subplots(figsize=(3.5, 2.6))  # 单栏尺寸

x = [i * 0.1 for i in range(100)]
y = [math.exp(-xi / 3) * math.sin(xi * 2) for xi in x]

ax.plot(x, y, color='#1F4E79', lw=1.5)

# 极简坐标轴:只显示左下边框
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

ax.set_xlabel('Time (s)')
ax.set_ylabel('Amplitude')
ax.set_xlim(0, 10)
ax.grid(True, ls='--', lw=0.5)

fig.tight_layout()
fig.savefig('science.png', dpi=300)
论文插图要点:3.5 英寸(单栏)或 7 英寸(双栏);DPI 300+;使用无衬线字体;图例字号比正文小 1-2 号;坐标轴单位写完整(如"Time (s)")。

10.5仪表板原型

用 rsplotlib 快速搭建数据仪表板的草图,包含关键指标(KPI)、趋势、分布等。

dashboard.py
import random
random.seed(0)

# 模拟近 30 天的价格随机游走
days = list(range(30))
price = [100]
for _ in range(29):
    price.append(price[-1] + random.gauss(0, 3))

fig = plt.figure(figsize=(14, 8))

# 顶部 KPI 区:4 个数字
kpi_values = [('日活', '12.5万', '+8.2%'),
              ('营收', '¥85.6万', '+12.5%'),
              ('订单', '3,421', '-2.1%'),
              ('转化率', '3.8%', '+0.5%')]
for i, (label, value, change) in enumerate(kpi_values):
    ax = fig.add_subplot(2, 4, i + 1)
    ax.text(0.5, 0.7, value, ha='center', fontsize=22, fontweight='bold')
    ax.text(0.5, 0.4, label, ha='center', fontsize=11)
    color = 'green' if '+' in change else 'red'
    ax.text(0.5, 0.15, change, ha='center', fontsize=10, color=color)
    ax.axis('off')

# 第二行:趋势线 + 分布
ax_trend = fig.add_subplot(2, 2, 3)
ax_trend.plot(days, price, color='steelblue', lw=2)
ax_trend.set_title('近 30 天趋势')

ax_dist = fig.add_subplot(2, 2, 4)
ax_dist.hist(price, bins=30, color='steelblue', alpha=0.7)
ax_dist.set_title('价格分布')

fig.tight_layout()
fig.savefig('dashboard.png', dpi=200)
Chapter 11

最佳实践与 FAQ

常见问题解答、性能优化建议与调试技巧。

11.1最佳实践

代码组织

使用面向对象方式 fig, ax = plt.subplots(),先绘制完所有元素再 savefig;图表元素添加完整图例、标题、坐标轴标签。

性能优化

大量散点用 scatter(c=array, s=array);批量参考线用 hlines/vlines;PNG 适合位图,SVG 适合编辑。

IDE 悬停提示

rsplotlib 的所有 Python 函数都带详细的中文 docstring,在 VS Code / PyCharm 中悬停即可查看参数说明。

中文显示

脚本开头显式设置 mpl.rcParams['font.sans-serif'],并确保系统已安装中文字体。

11.2常见问题 FAQ

为什么图表中的中文显示为方块(□□□)?

这是字体配置问题。在代码开头添加字体配置即可:

from rsplotlib.pylab import mpl
mpl.rcParams['font.sans-serif'] = ['PingFang SC', 'Microsoft YaHei', 'Arial']

# 配置生效后,中文标题与坐标标签即可正常显示
import rsplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9], label='示例曲线')
ax.set_title('中文标题正常显示')
ax.set_xlabel('横轴')
ax.set_ylabel('纵轴')
ax.legend()
fig.savefig('chinese_font.png')

确保系统已安装中文字体,或使用 _rs.register_sans_serif_font() 注册字体文件。

scatter 的 c 参数传入列表不生效?

rsplotlib 支持颜色数组,在 Rust 层统一渲染。请检查:颜色字符串格式是否正确(命名颜色或十六进制),以及 rsplotlib 版本是否最新。

savefig 的 dpi 参数如何使用?

dpi 控制每英寸点数。常见用法:savefig('p.png') 默认 DPI;dpi=150 屏幕;dpi=300 出版级。像素尺寸 = figsize (英寸) × dpi。

如何在一个 Figure 中添加多个 Axes?

使用 plt.subplots(rows, cols),返回的 axes 是扁平列表,按行优先顺序排列。也可以使用 fig.add_subplot(rows, cols, index) 逐个创建。

annotate 的箭头如何自定义样式?

通过 arrowprops 字典传入:arrowstyle 样式(如 '->''-|>'),arrowsize 相对大小。颜色与文本颜色一致。

为什么 hlines/vlines 比循环调用 axhline 更快?

内部循环在 Rust 层一次性处理,避免 N 次 Python→Rust 跨语言调用。跨语言调用有固定开销,批量处理将开销从 O(N) 降到 O(1)。

如何从 Matplotlib 迁移到 rsplotlib?

大多数情况下,只需将 import matplotlib.pyplot as plt 改为 from rsplotlib import pyplot as plt。注意:plt.show() 在 rsplotlib 中保存到文件而非弹窗;部分高级 Matplotlib 特性尚未实现。

支持交互式图表吗?

当前版本主要支持静态图表(PNG/SVG),不支持交互式图表。未来版本可能添加 Jupyter Notebook 集成支持。

Chapter 12

功能速查表

一眼速览 rsplotlib 全部支持的绘图函数与新增功能。

12.1绘图函数总览

函数 说明 支持数组参数 新增
plot 折线图
scatter 散点图 颜色/大小数组
bar / barh 柱状/水平柱状
hist 直方图
pie 饼图
boxplot 箱线图
fill_between 填充区域
errorbar 误差棒
stem 茎图
step 阶梯图
imshow 图像 / 热力图(cmap / norm) 2D 矩阵 / 图像数组
stackplot 堆叠面积
axhline / axvline 水平/垂直参考线
axhspan / axvspan 水平/垂直区间
axline 任意斜率参考线
hlines / vlines 批量参考线 (Rust) 位置数组
text 文本标注
annotate 箭头标注 ✓ arrowprops
semilogx / semilogy / loglog 半对数 / 双对数坐标
colorbar 颜色条(imshow / scatter 数值)
imread / imsave 图像读取 / 保存 ndarray

12.2参数别名对照

别名 标准名 说明
lw linewidth 线宽
c color 颜色
ls linestyle 线型

rsplotlib 在 Python 层自动处理这些别名,保持与 Matplotlib 代码的兼容性。