XY 是一个极其快速、可交互、可定制的 Python 图表库,适用于 Web、笔记本和静态导出。
图表可以通过声明式方式或遵循 matplotlib 约定进行组合。您可以 使用 Python、CSS 或 Tailwind 对其进行完全定制。
对于小型图表,每个点都会发送到浏览器。对于大型图表,Rust 核心 仅根据屏幕分辨率计算需要显示的内容。平移、缩放、悬停和选择 可以通过对新范围运行相同的过程来显示完整细节,并且选择会返回原始行。
使用 XY,我们渲染了整个 OpenStreetMap —— 一个包含 10,000,000,000 个点 的数据集。查看示例 →
[!IMPORTANT] XY 目前处于 alpha 阶段,并正在频繁进行增强。 ⭐️ 给仓库加星以关注其进展。
XY 适合我吗?
XY 适用于希望拥有一个灵活图表库的 Python 用户,该库可用于从日常 绘图到自定义应用程序视觉效果和大型数据集的一切。构建一次图表, 然后将其用于笔记本和 Web 应用,或将其导出为 HTML、PNG、 SVG 或 PDF。
安装
pip install xy
# or, with uv
uv add xy
入门
图表是一个容器加上其中的标记。任何序列都可以;NumPy 是可选的。
import xy
chart = xy.line_chart(xy.line([1, 2, 3, 4, 5], [120, 180, 165, 240, 310]))
# chart.to_html("chart.html")
# chart.to_png("chart.png")
# chart.to_svg("chart.svg")
chart # notebooks render it
相同的 API 可扩展至一亿个点的密度表面:
import numpy as np
import xy
rng = np.random.default_rng(7)
n = 100_000_000
r = 6.0 * rng.beta(1.2, 3.0, n)
theta = 2.9 * np.log1p(r) + rng.integers(0, 4, n) * (np.pi / 2) + rng.normal(0, 0.045 + 0.016 * r, n)
chart = xy.scatter_chart(
xy.scatter(
r * np.cos(theta),
r * np.sin(theta),
color=np.exp(-r / 2.2),
colormap="magma_r",
density=True,
opacity=0.85,
# Grow and solidify markers once a view drills through to real rows.
size=2.5,
zoom_size_factor=2.6,
zoom_opacity=0.95,
),
xy.theme(
background="#ffffff", plot_background="#ffffff", grid_color="#e6e6e1",
axis_color="#c3c2b7", text_color="#0b0b0b",
),
title="100 million points",
)
chart
来自 matplotlib
对于常见的 pyplot 工作流,只需更改导入并保留绘图代码:
import numpy as np
import xy.pyplot as plt
x = np.linspace(0, 10, 200)
fig, ax = plt.subplots()
ax.plot(x, np.sin(x), "r--", label="signal")
ax.legend()
plt.show()
参见兼容性指南;并非所有图表和功能均已支持。
自定义每一层
使用 Python 控制图表,从标记和坐标轴到交互和布局。
- 标记: 控制颜色、大小、不透明度、符号、渐变、描边、曲线和 颜色映射。
- 辅助元素: 自定义坐标轴、刻度、网格、注释、图例、颜色条和 工具提示。
- 交互: 添加平移、缩放、悬停、选择、十字准线、回调和 联动图表。
- 布局: 创建图层和分面,设置响应式尺寸,并应用 主题。
chart = xy.line_chart(
xy.line(x, y, color="#7c3aed", width=3),
class_name="rounded-xl bg-white",
class_names={"tooltip": "rounded-lg bg-zinc-900 text-white"},
)
参见样式指南 以获取示例。有关可定制内容的详细分解,请参见 功能矩阵。
基准测试
实时交互式图表,10k 至 100M 个点。每个库都包含每一行数据, 并在真实浏览器中通过其自身的输入路径驱动。计时仅在画布既正确(已验证植入的哨兵点已点亮) 又稳定(10 个字节相同的帧)时停止,因此渐进式渲染器会被计费直到 其最后一个块落地。
XY 在 10k 时为 0.071 秒,在 100M 时为 0.081 秒,在四个数量级范围内保持平稳, 因为当行数超过 200k 时,它绘制的是屏幕受限的密度表面 而不是每行一个标记,并且缩放可以钻取回精确的行。所有 精确标记路径则随 N 扩展:Matplotlib 在约 3M 时超过一秒, 在 50M 时达到 13.4 秒;Plotly 在约 2.5M 时超过一秒,在 25M 时达到 9.8 秒。
浅色线条是带有 density=False 的 XY:同一引擎绘制每行一个标记,
没有聚合信用。它在 5.26 GiB 内存下以 1.34 秒渲染 100M 个精确标记。
所有点显示在屏幕上所需的时间,以秒为单位。✕ 是库未能渲染的尺寸:Plotly 在 50M 时始终无法完成图形构建,而 Matplotlib 在 100M 时能够绘制,但始终无法解析随后的缩放。
| 点数 | 10k | 100k | 500k | 1M | 2.5M | 5M | 10M | 25M | 50M | 100M |
|---|---|---|---|---|---|---|---|---|---|---|
| XY 加速比 | 1× | 2× | 3× | 4× | 9× | 16× | 34× | 89× | 177× | — |
| XY | 0.071 | 0.072 | 0.075 | 0.084 | 0.083 | 0.089 | 0.083 | 0.077 | 0.076 | 0.081 |
XY (density=False) | 0.085 | 0.074 | 0.087 | 0.098 | 0.111 | 0.144 | 0.206 | 0.424 | 0.645 | 1.343 |
| Matplotlib (WebAgg) | 0.086 | 0.115 | 0.224 | 0.357 | 0.758 | 1.424 | 2.804 | 6.838 | 13.385 | ✕ |
| Plotly (scattergl) | 0.341 | 0.373 | 0.477 | 0.614 | 1.033 | 1.785 | 3.367 | 9.794 | ✕ | ✕ |
Python 端峰值常驻内存,以 GiB 为单位。浏览器内存单独跟踪并在此处排除,因为无头 Chrome 在绘制任何内容之前常驻约 1 GiB。
| 点数 | 10k | 100k | 500k | 1M | 2.5M | 5M | 10M | 25M | 50M | 100M |
|---|---|---|---|---|---|---|---|---|---|---|
| XY 优势 | 1.8× | 1.7× | 1.9× | 2.1× | 2.1× | 2.4× | 2.6× | 2.9× | 2.8× | — |
| XY | 0.05 | 0.05 | 0.06 | 0.07 | 0.13 | 0.19 | 0.32 | 0.70 | 1.36 | 2.58 |
XY (density=False) | 0.05 | 0.05 | 0.07 | 0.10 | 0.18 | 0.31 | 0.57 | 1.35 | 2.66 | 5.26 |
| Matplotlib (WebAgg) | 0.09 | 0.09 | 0.12 | 0.15 | 0.28 | 0.46 | 0.84 | 2.06 | 3.85 | ✕ |
| Plotly (scattergl) | 0.21 | 0.18 | 0.28 | 0.36 | 0.60 | 1.05 | 1.86 | 4.70 | ✕ | ✕ |
一台机器(Apple M5 Pro),每个单元格运行一次;在小数据量端,计时存在大约 ±10 ms 的运行间波动。
有关环境、方法论、各尺寸视频和原始结果,请参阅 基准测试运行手册 和 竞争性基准测试规范。
在 Reflex 应用中嵌入 XY
随 xy 捆绑的 Reflex 集成可将任何 XY 图表转换为标准的
Reflex 组件,无需 JavaScript、iframe 或独立的图表服务。
安装 reflex 附加组件以选择兼容的框架版本:
pip install "xy[reflex]"
# or, with uv
uv add "xy[reflex]"
导入命名空间仍为 reflex_xy。注册一次集成:
# rxconfig.py
import reflex as rx
import reflex_xy
config = rx.Config(
app_name="dashboard",
plugins=[reflex_xy.XYPlugin()],
)
然后在组件树的任意位置添加一个图表:
import reflex as rx
import reflex_xy
import xy
signups = xy.line_chart(
xy.line([1, 2, 3, 4, 5], [120, 180, 165, 240, 310]),
title="Weekly signups",
)
def index() -> rx.Component:
return rx.card(
rx.heading("Growth"),
reflex_xy.chart(signups, height="320px"),
width="100%",
)
app = rx.App()
app.add_page(index)
对于状态驱动的图表,在页面中声明图表,并从
@reflex_xy.data 状态方法中提供列 —— 结构(通道、颜色映射、坐标轴)
在 reflex run 编译应用时进行验证,而列则通过
应用自身的 websocket 作为二进制缓冲区传输,绝不通过 Reflex 状态:
from typing import TypedDict
import numpy as np
import reflex as rx
import reflex_xy
class CloudData(TypedDict):
x: np.ndarray
y: np.ndarray
mag: np.ndarray
class Dash(rx.State):
points: int = 200_000
@reflex_xy.data
def cloud(self) -> CloudData:
rng = np.random.default_rng(7)
x = rng.normal(size=self.points)
y = x * 0.6 + rng.normal(scale=0.6, size=self.points)
return {"x": x, "y": y, "mag": np.hypot(x, y)}
def dashboard() -> rx.Component:
return reflex_xy.scatter_chart(
data=Dash.cloud,
x="x", y="y", color="mag", colormap="viridis",
height="460px",
)
悬停、平移和缩放功能保持可用。对于由 Reflex 状态、事件或 实时流驱动的图表,请参阅 Reflex 集成指南 和 可运行的示例应用。
示例
每个笔记本从关联的公开数据源获取其行数据;本仓库中不存储任何原始数据集。计数描述的是特色图表,而笔记本可进一步扩展。请参阅 示例指南 以了解数据源、工作负载控制 和设置。
| Gaia DR3 · HR diagram 250,000 plotted stars ![]() Open notebook | gnomAD v4.1 · allele frequency 164,000 plotted variants ![]() Open notebook | Pan-UKBB · Manhattan plot 814,294 plotted variants ![]() Open notebook |
| Dukascopy · EUR/USD ticks 101,427 plotted ticks ![]() Open notebook | LIGO · GW150914 strain 16,777,216 raw · 3,441 shown ![]() Open notebook | NYC TLC · taxi pickup density 300,000 pickup records ![]() Open notebook |
工作原理
大多数图表堆栈将每个值序列化为 JSON,并要求浏览器绘制
每个标记。XY 在 ColumnStore 中保留精确值,在 Rust 中计算细节层次,
并传输类型化二进制缓冲区。降采样和密度视图受可见结果限制。
flowchart TB
API["Python API<br/>Build the chart"]
STORE["ColumnStore<br/>Keep canonical f64 columns"]
CORE["Native Rust compute<br/>Direct · decimated · density"]
PAYLOAD["Compact payload<br/>Data-less JSON spec + typed binary buffers"]
RENDER["Browser or notebook<br/>WebGL2 marks · Canvas axes · DOM interface"]
API --> STORE --> CORE --> PAYLOAD --> RENDER
因此,密集概览可以聚合数据,而窄视图则返回精确的数据点。 对于实时主机,平移和缩放请求会获取更精细的负载。 规范的 f64 数据保留在 Python 中,因此悬停和选择操作仍返回原始行。
有关完整设计,请参阅 设计档案。
路线图
首先实现广泛的 2D 覆盖,然后是地理、3D 和体积可视化。 已列入队列,未暗示具体日期:
- 类别分布: 条带图、蜂群图、蜜蜂群图、箱线图、地毯图
- 回归诊断: 趋势线、残差图、QQ 图、PP 图
- 散点矩阵和联合图: SPLOM、配对网格、边缘直方图
- 饼图 / 环形图:
xy.pie_chart(labels, values, hole=...)基于 不等宽核心极坐标条形图发布,并在xy.pyplot中提供 Matplotlib 风格的辅助函数; 嵌套环形图和可变半径组合仍待实现 - K 线 / OHLC 和金融叠加: SMA、VWAP、布林带、RSI、MACD;已原型化,等待正式发布
- 瀑布图和漏斗图
- 矩形树图、旭日图和冰柱图
- 仪表盘 / 指示器: 基于已发布的极坐标轴和可组合的径向标记构建
- 斜率图、凸起图和哑铃图
- 3D 和体积: 散点图、曲面、网格、等值面和体积视图
完整的排名待办事项列表位于 图表路线图。 想要未列出的图表或功能? 提交一个 issue。





